@@ -8,10 +8,14 @@ import { tcls } from '@/lib/tailwind';
88import { Button , Link } from '../primitives' ;
99
1010/**
11- * A set of tabs representing site sections for multi-section sites
11+ * A set of navigational tabs representing site sections for multi-section sites
1212 */
13- export function SiteSectionTabs ( props : { sections : SiteSection [ ] ; section : SiteSection } ) {
14- const { sections, section : currentSection } = props ;
13+ export function SiteSectionTabs ( props : {
14+ list : SiteSection [ ] ;
15+ section : SiteSection ;
16+ index : number ;
17+ } ) {
18+ const { list : sections , section : currentSection , index : currentIndex } = props ;
1519
1620 const tabs = sections . map ( ( section ) => ( {
1721 id : section . id ,
@@ -22,34 +26,33 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
2226 const currentTabRef = React . useRef < HTMLAnchorElement > ( null ) ;
2327 const navRef = React . useRef < HTMLDivElement > ( null ) ;
2428
25- const [ currentIndex , setCurrentIndex ] = React . useState (
26- sections . findIndex ( ( section ) => section . id === currentSection ?. id ) ,
27- ) ;
2829 const [ tabDimensions , setTabDimensions ] = React . useState < {
2930 left : number ;
3031 width : number ;
3132 } | null > ( null ) ;
3233
33- React . useEffect ( ( ) => {
34+ const updateTabDimensions = React . useCallback ( ( ) => {
3435 if ( currentTabRef . current && navRef . current ) {
3536 const rect = currentTabRef . current . getBoundingClientRect ( ) ;
3637 const navRect = navRef . current . getBoundingClientRect ( ) ;
3738 setTabDimensions ( { left : rect . left - navRect . left , width : rect . width } ) ;
3839 }
39- } , [ currentIndex ] ) ;
40+ } , [ ] ) ;
41+
42+ React . useEffect ( ( ) => {
43+ updateTabDimensions ( ) ;
44+ } , [ currentIndex , updateTabDimensions ] ) ;
4045
4146 React . useLayoutEffect ( ( ) => {
42- function onResize ( ) {
43- if ( currentTabRef . current && navRef . current ) {
44- const rect = currentTabRef . current . getBoundingClientRect ( ) ;
45- const navRect = navRef . current . getBoundingClientRect ( ) ;
46- setTabDimensions ( { left : rect . left - navRect . left , width : rect . width } ) ;
47- }
48- }
49- window . addEventListener ( 'resize' , onResize ) ;
50- ( ) => window . removeEventListener ( 'resize' , onResize ) ;
51- } , [ ] ) ;
47+ window . addEventListener ( 'load' , updateTabDimensions ) ;
48+ window . addEventListener ( 'resize' , updateTabDimensions ) ;
49+ ( ) => {
50+ window . removeEventListener ( 'resize' , updateTabDimensions ) ;
51+ window . removeEventListener ( 'load' , updateTabDimensions ) ;
52+ } ;
53+ } , [ updateTabDimensions ] ) ;
5254
55+ const opacity = Boolean ( tabDimensions ) ? 1 : 0.0 ;
5356 const scale = ( tabDimensions ?. width ?? 0 ) * 0.01 ;
5457 const startPos = `${ tabDimensions ?. left ?? 0 } px` ;
5558
@@ -62,6 +65,7 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
6265 className = "flex flex-nowrap items-center max-w-screen mb-px"
6366 style = {
6467 {
68+ '--tab-opacity' : `${ opacity } ` ,
6569 '--tab-scale' : `${ scale } ` ,
6670 '--tab-start' : `${ startPos } ` ,
6771 } as React . CSSProperties
@@ -80,9 +84,12 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
8084 'after:absolute' ,
8185 'after:-bottom-px' ,
8286 'after:left-0' ,
87+ 'after:opacity-[--tab-opacity]' ,
8388 'after:scale-x-[--tab-scale]' ,
84- 'after:transition-transform' ,
89+ 'after:[transition:_opacity_150ms_25ms,transform_150ms]' ,
90+ 'after:motion-reduce:transition-none' ,
8591 'after:translate-x-[var(--tab-start)]' ,
92+ 'after:will-change-transform' ,
8693 'after:h-0.5' ,
8794 'after:w-[100px]' ,
8895 'after:bg-primary' ,
@@ -97,7 +104,6 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
97104 label = { tab . label }
98105 href = { tab . path }
99106 ref = { currentIndex === index ? currentTabRef : null }
100- onClick = { ( ) => setCurrentIndex ( index ) }
101107 />
102108 ) ) }
103109 </ div >
@@ -109,29 +115,27 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
109115/**
110116 * The tab item - a link to a site section
111117 */
112- const Tab = React . forwardRef <
113- HTMLSpanElement ,
114- { active : boolean ; href : string ; label : string ; onClick ?: ( ) => void }
115- > ( function Tab ( props , ref ) {
116- const { active, href, label, onClick } = props ;
117- return (
118- < Link
119- className = { tcls (
120- 'px-3 py-1 my-2 rounded straight-corners:rounded-none transition-colors' ,
121- active && 'text-primary dark:text-primary-400' ,
122- ! active &&
123- 'text-dark/8 hover:bg-dark/1 hover:text-dark/9 dark:text-light/8 dark:hover:bg-light/2 dark:hover:text-light/9' ,
124- ) }
125- role = "tab"
126- href = { href }
127- onClick = { onClick }
128- >
129- < span ref = { ref } className = { tcls ( 'inline-flex w-full truncate' ) } >
130- { label }
131- </ span >
132- </ Link >
133- ) ;
134- } ) ;
118+ const Tab = React . forwardRef < HTMLSpanElement , { active : boolean ; href : string ; label : string } > (
119+ function Tab ( props , ref ) {
120+ const { active, href, label } = props ;
121+ return (
122+ < Link
123+ className = { tcls (
124+ 'px-3 py-1 my-2 rounded straight-corners:rounded-none transition-colors' ,
125+ active && 'text-primary dark:text-primary-400' ,
126+ ! active &&
127+ 'text-dark/8 hover:bg-dark/1 hover:text-dark/9 dark:text-light/8 dark:hover:bg-light/2 dark:hover:text-light/9' ,
128+ ) }
129+ role = "tab"
130+ href = { href }
131+ >
132+ < span ref = { ref } className = { tcls ( 'inline-flex w-full truncate' ) } >
133+ { label }
134+ </ span >
135+ </ Link >
136+ ) ;
137+ } ,
138+ ) ;
135139
136140/**
137141 * Dropdown trigger for when there are too many sections to show them all
0 commit comments