1
1
import { cn } from "~/utils/cn" ;
2
- import { component$ , useSignal , $ , useOnWindow } from ' @qwik.dev/core' ;
3
- import type { ContentHeading } from ' @qwik.dev/router' ;
2
+ import { component$ , useSignal , $ , useOnWindow } from " @qwik.dev/core" ;
3
+ import type { ContentHeading } from " @qwik.dev/router" ;
4
4
5
5
export const Toc = component$ (
6
6
( { headings } : { headings : ContentHeading [ ] } ) => {
7
7
if ( headings . length === 0 ) {
8
8
return null ;
9
9
}
10
10
return (
11
- < div class = "space-y-2 sticky top-24 max-h-[calc(80vh)] p-1 dark:text-white text-black hidden xl:block" >
12
- < div class = "font-medium" > On This Page</ div >
11
+ < div class = "sticky top-24 hidden max-h-[calc(80vh)] space-y-2 p-1 text-black dark:text-white xl:block" >
12
+ < div class = "mb-2 block rounded bg-blue-700 px-4 py-1 text-base font-bold uppercase text-white no-underline dark:bg-blue-600" >
13
+ On this page
14
+ </ div >
13
15
< TableOfContents headings = { headings } />
14
16
</ div >
15
17
) ;
@@ -25,30 +27,39 @@ interface Node extends ContentHeading {
25
27
type Tree = Array < Node > ;
26
28
27
29
const TableOfContents = component$ < TableOfContentsProps > ( ( { headings } ) => {
28
- const sanitizedHeadings = headings . map ( ( { text, id, level } ) => ( { text, id, level } ) ) ;
30
+ const sanitizedHeadings = headings . map ( ( { text, id, level } ) => ( {
31
+ text,
32
+ id,
33
+ level,
34
+ } ) ) ;
29
35
const itemIds = headings . map ( ( { id } ) => id ) ;
30
36
const activeHeading = useActiveItem ( itemIds ) ;
31
37
const tree = buildTree ( sanitizedHeadings ) ;
32
38
const fixStartingBug : Node = { ...tree , children : [ tree ] } ;
33
- return < RecursiveList tree = { fixStartingBug } activeItem = { activeHeading . value ?? '' } /> ;
39
+ return (
40
+ < RecursiveList
41
+ tree = { fixStartingBug }
42
+ activeItem = { activeHeading . value ?? "" }
43
+ />
44
+ ) ;
34
45
} ) ;
35
46
36
47
function deltaToStrg (
37
48
currNode : Node ,
38
49
nextNode : Node ,
39
- ) : ' same level' | ' down one level' | ' up one level' | ' upwards discontinuous' {
50
+ ) : " same level" | " down one level" | " up one level" | " upwards discontinuous" {
40
51
const delta = currNode . level - nextNode . level ;
41
52
if ( delta > 1 ) {
42
- return ' upwards discontinuous' ;
53
+ return " upwards discontinuous" ;
43
54
}
44
55
if ( delta === 1 ) {
45
- return ' up one level' ;
56
+ return " up one level" ;
46
57
}
47
58
if ( delta === 0 ) {
48
- return ' same level' ;
59
+ return " same level" ;
49
60
}
50
61
if ( delta === - 1 ) {
51
- return ' down one level' ;
62
+ return " down one level" ;
52
63
}
53
64
54
65
throw new Error (
@@ -68,25 +79,25 @@ function buildTree(nodes: ContentHeading[]) {
68
79
childrenMap . set ( nextNode . level , nextNode . children ) ;
69
80
const deltaStrg = deltaToStrg ( currNode , nextNode ) ;
70
81
switch ( deltaStrg ) {
71
- case ' upwards discontinuous' : {
82
+ case " upwards discontinuous" : {
72
83
const delta = currNode . level - nextNode . level ;
73
84
if ( childrenMap . has ( delta - 1 ) ) {
74
85
const nthParent = childrenMap . get ( delta - 1 ) ;
75
86
nthParent ?. push ( nextNode ) ;
76
87
}
77
88
break ;
78
89
}
79
- case ' up one level' : {
90
+ case " up one level" : {
80
91
const grandParent = childrenMap . get ( currNode . level - 2 ) ;
81
92
grandParent ?. push ( nextNode ) ;
82
93
break ;
83
94
}
84
- case ' same level' : {
95
+ case " same level" : {
85
96
const parent = childrenMap . get ( currNode . level - 1 ) ;
86
97
parent ?. push ( nextNode ) ;
87
98
break ;
88
99
}
89
- case ' down one level' : {
100
+ case " down one level" : {
90
101
currNode . children . push ( nextNode ) ;
91
102
break ;
92
103
}
@@ -107,7 +118,7 @@ type RecursiveListProps = {
107
118
const RecursiveList = component$ < RecursiveListProps > (
108
119
( { tree, activeItem, limit = 3 } ) => {
109
120
return tree . children . length && tree . level < limit ? (
110
- < ul class = { cn ( ' m-0 list-none' , { ' pl-4' : tree . level !== 1 } ) } >
121
+ < ul class = { cn ( " m-0 list-none" , { " pl-4" : tree . level !== 1 } ) } >
111
122
{ tree . children . map ( ( childNode ) => (
112
123
< li key = { childNode . id } class = "mt-0 list-none pt-2" >
113
124
< Anchor node = { childNode } activeItem = { activeItem } />
@@ -125,7 +136,7 @@ const useActiveItem = (itemIds: string[]) => {
125
136
const activeId = useSignal < string > ( ) ;
126
137
127
138
useOnWindow (
128
- ' scroll' ,
139
+ " scroll" ,
129
140
$ ( ( ) => {
130
141
const observer = new IntersectionObserver (
131
142
( entries ) => {
@@ -135,7 +146,7 @@ const useActiveItem = (itemIds: string[]) => {
135
146
}
136
147
} ) ;
137
148
} ,
138
- { rootMargin : ' 0% 0% -85% 0%' } ,
149
+ { rootMargin : " 0% 0% -85% 0%" } ,
139
150
) ;
140
151
141
152
itemIds . forEach ( ( id ) => {
@@ -175,18 +186,20 @@ const Anchor = component$<AnchorProps>(({ node, activeItem }) => {
175
186
if ( element ) {
176
187
const navbarHeight = 90 ;
177
188
const position =
178
- element . getBoundingClientRect ( ) . top + window . scrollY - navbarHeight ;
179
- window . scrollTo ( { top : position , behavior : 'auto' } ) ;
189
+ element . getBoundingClientRect ( ) . top +
190
+ window . scrollY -
191
+ navbarHeight ;
192
+ window . scrollTo ( { top : position , behavior : "auto" } ) ;
180
193
}
181
194
} ) ,
182
195
] }
183
196
class = { cn (
184
- node . level > 2 && ' ml-2' ,
185
- ' inline-block no-underline transition-colors' ,
186
- isActive ? ' text-blue-500 dark:text-blue-300' : ' text-muted-foreground' ,
197
+ node . level > 2 && " ml-2" ,
198
+ " inline-block no-underline transition-colors" ,
199
+ isActive ? " text-blue-500 dark:text-blue-300" : " text-muted-foreground" ,
187
200
) }
188
201
>
189
202
{ node . text }
190
203
</ a >
191
204
) ;
192
- } ) ;
205
+ } ) ;
0 commit comments