@@ -8,10 +8,14 @@ import { tcls } from '@/lib/tailwind';
8
8
import { Button , Link } from '../primitives' ;
9
9
10
10
/**
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
12
12
*/
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 ;
15
19
16
20
const tabs = sections . map ( ( section ) => ( {
17
21
id : section . id ,
@@ -22,34 +26,33 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
22
26
const currentTabRef = React . useRef < HTMLAnchorElement > ( null ) ;
23
27
const navRef = React . useRef < HTMLDivElement > ( null ) ;
24
28
25
- const [ currentIndex , setCurrentIndex ] = React . useState (
26
- sections . findIndex ( ( section ) => section . id === currentSection ?. id ) ,
27
- ) ;
28
29
const [ tabDimensions , setTabDimensions ] = React . useState < {
29
30
left : number ;
30
31
width : number ;
31
32
} | null > ( null ) ;
32
33
33
- React . useEffect ( ( ) => {
34
+ const updateTabDimensions = React . useCallback ( ( ) => {
34
35
if ( currentTabRef . current && navRef . current ) {
35
36
const rect = currentTabRef . current . getBoundingClientRect ( ) ;
36
37
const navRect = navRef . current . getBoundingClientRect ( ) ;
37
38
setTabDimensions ( { left : rect . left - navRect . left , width : rect . width } ) ;
38
39
}
39
- } , [ currentIndex ] ) ;
40
+ } , [ ] ) ;
41
+
42
+ React . useEffect ( ( ) => {
43
+ updateTabDimensions ( ) ;
44
+ } , [ currentIndex , updateTabDimensions ] ) ;
40
45
41
46
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 ] ) ;
52
54
55
+ const opacity = Boolean ( tabDimensions ) ? 1 : 0.0 ;
53
56
const scale = ( tabDimensions ?. width ?? 0 ) * 0.01 ;
54
57
const startPos = `${ tabDimensions ?. left ?? 0 } px` ;
55
58
@@ -62,6 +65,7 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
62
65
className = "flex flex-nowrap items-center max-w-screen mb-px"
63
66
style = {
64
67
{
68
+ '--tab-opacity' : `${ opacity } ` ,
65
69
'--tab-scale' : `${ scale } ` ,
66
70
'--tab-start' : `${ startPos } ` ,
67
71
} as React . CSSProperties
@@ -80,9 +84,12 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
80
84
'after:absolute' ,
81
85
'after:-bottom-px' ,
82
86
'after:left-0' ,
87
+ 'after:opacity-[--tab-opacity]' ,
83
88
'after:scale-x-[--tab-scale]' ,
84
- 'after:transition-transform' ,
89
+ 'after:[transition:_opacity_150ms_25ms,transform_150ms]' ,
90
+ 'after:motion-reduce:transition-none' ,
85
91
'after:translate-x-[var(--tab-start)]' ,
92
+ 'after:will-change-transform' ,
86
93
'after:h-0.5' ,
87
94
'after:w-[100px]' ,
88
95
'after:bg-primary' ,
@@ -97,7 +104,6 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
97
104
label = { tab . label }
98
105
href = { tab . path }
99
106
ref = { currentIndex === index ? currentTabRef : null }
100
- onClick = { ( ) => setCurrentIndex ( index ) }
101
107
/>
102
108
) ) }
103
109
</ div >
@@ -109,29 +115,27 @@ export function SiteSectionTabs(props: { sections: SiteSection[]; section: SiteS
109
115
/**
110
116
* The tab item - a link to a site section
111
117
*/
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
+ ) ;
135
139
136
140
/**
137
141
* Dropdown trigger for when there are too many sections to show them all
0 commit comments