1
1
'use client' ;
2
- import { SiteSection } from '@gitbook/api' ;
2
+
3
+ import type { SiteSection } from '@gitbook/api' ;
4
+ import type { IconName } from '@gitbook/icons' ;
3
5
import React from 'react' ;
4
6
5
7
import { tcls } from '@/lib/tailwind' ;
6
8
7
9
import { Link } from '../primitives' ;
10
+ import { SectionIcon } from './SectionIcon' ;
8
11
9
12
/**
10
13
* A set of navigational tabs representing site sections for multi-section sites
@@ -14,13 +17,7 @@ export function SiteSectionTabs(props: {
14
17
section : SiteSection ;
15
18
index : number ;
16
19
} ) {
17
- const { list : sections , section : currentSection , index : currentIndex } = props ;
18
-
19
- const tabs = sections . map ( ( section ) => ( {
20
- id : section . id ,
21
- label : section . title ,
22
- path : section . urls . published ?? '' ,
23
- } ) ) ;
20
+ const { list : sections , index : currentIndex } = props ;
24
21
25
22
const currentTabRef = React . useRef < HTMLAnchorElement > ( null ) ;
26
23
const navRef = React . useRef < HTMLDivElement > ( null ) ;
@@ -43,7 +40,9 @@ export function SiteSectionTabs(props: {
43
40
} , [ ] ) ;
44
41
45
42
React . useEffect ( ( ) => {
46
- updateTabDimensions ( ) ;
43
+ if ( currentIndex >= 0 ) {
44
+ updateTabDimensions ( ) ;
45
+ }
47
46
} , [ currentIndex , updateTabDimensions ] ) ;
48
47
49
48
React . useLayoutEffect ( ( ) => {
@@ -55,11 +54,11 @@ export function SiteSectionTabs(props: {
55
54
} ;
56
55
} , [ updateTabDimensions ] ) ;
57
56
58
- const opacity = Boolean ( tabDimensions ) ? 1 : 0.0 ;
57
+ const opacity = tabDimensions ? 1 : 0.0 ;
59
58
const scale = ( tabDimensions ?. width ?? 0 ) * 0.01 ;
60
59
const startPos = `${ tabDimensions ?. left ?? 0 } px` ;
61
60
62
- return tabs . length > 0 ? (
61
+ return sections . length > 0 ? (
63
62
< nav
64
63
aria-label = "Sections"
65
64
ref = { navRef }
@@ -84,15 +83,24 @@ export function SiteSectionTabs(props: {
84
83
'md:px-5' ,
85
84
) }
86
85
>
87
- { tabs . map ( ( tab , index ) => (
88
- < Tab
89
- active = { currentIndex === index }
90
- key = { index + tab . path }
91
- label = { tab . label }
92
- href = { tab . path }
93
- ref = { currentIndex === index ? currentTabRef : null }
94
- />
95
- ) ) }
86
+ { sections . map ( ( section , index ) => {
87
+ const { id, urls, title, icon } = section ;
88
+ const isActive = index === currentIndex ;
89
+ return (
90
+ < Tab
91
+ active = { isActive }
92
+ key = { id }
93
+ label = { title }
94
+ href = { urls . published ?? '' }
95
+ ref = { isActive ? currentTabRef : null }
96
+ icon = {
97
+ icon ? (
98
+ < SectionIcon isActive = { isActive } icon = { icon as IconName } />
99
+ ) : null
100
+ }
101
+ />
102
+ ) ;
103
+ } ) }
96
104
</ div >
97
105
{ /* A container for a pseudo element for active tab indicator. A container is needed so we can set
98
106
a relative position without breaking the z-index of other parts of the header. */ }
@@ -117,7 +125,7 @@ export function SiteSectionTabs(props: {
117
125
'after:bg-primary' ,
118
126
'dark:after:bg-primary-400' ,
119
127
) }
120
- > </ div >
128
+ / >
121
129
</ div >
122
130
</ nav >
123
131
) : null ;
@@ -126,24 +134,26 @@ export function SiteSectionTabs(props: {
126
134
/**
127
135
* The tab item - a link to a site section
128
136
*/
129
- const Tab = React . forwardRef < HTMLSpanElement , { active : boolean ; href : string ; label : string } > (
130
- function Tab ( props , ref ) {
131
- const { active, href, label } = props ;
132
- return (
133
- < Link
134
- className = { tcls (
135
- 'px-3 py-1 my-2 rounded straight-corners:rounded-none transition-colors' ,
136
- active && 'text-primary dark:text-primary-400' ,
137
- ! active &&
138
- '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' ,
139
- ) }
140
- role = "tab"
141
- href = { href }
142
- >
143
- < span ref = { ref } className = { tcls ( 'inline-flex w-full truncate' ) } >
144
- { label }
145
- </ span >
146
- </ Link >
147
- ) ;
148
- } ,
149
- ) ;
137
+ const Tab = React . forwardRef <
138
+ HTMLSpanElement ,
139
+ { active : boolean ; href : string ; icon ?: React . ReactNode ; label : string }
140
+ > ( function Tab ( props , ref ) {
141
+ const { active, href, icon, label } = props ;
142
+ return (
143
+ < Link
144
+ className = { tcls (
145
+ 'group/tab px-3 py-1 my-2 rounded straight-corners:rounded-none transition-colors' ,
146
+ active && 'text-primary dark:text-primary-400' ,
147
+ ! active &&
148
+ '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' ,
149
+ ) }
150
+ role = "tab"
151
+ href = { href }
152
+ >
153
+ < span ref = { ref } className = { tcls ( 'inline-flex gap-2 items-center w-full truncate' ) } >
154
+ { icon }
155
+ { label }
156
+ </ span >
157
+ </ Link >
158
+ ) ;
159
+ } ) ;
0 commit comments