1
- export interface Header {
2
- /**
3
- * The level of the header
4
- *
5
- * `1` to `6` for `<h1>` to `<h6>`
6
- */
7
- level : number
8
- /**
9
- * The title of the header
10
- */
11
- title : string
12
- /**
13
- * The slug of the header
14
- *
15
- * Typically the `id` attr of the header anchor
16
- */
17
- slug : string
18
- /**
19
- * Link of the header
20
- *
21
- * Typically using `#${slug}` as the anchor hash
22
- */
23
- link : string
24
- /**
25
- * The children of the header
26
- */
27
- children : Header [ ]
28
- }
1
+ import type { PageHeader } from 'vuepress/shared'
29
2
30
3
export type HeaderLevels = number | 'deep' | false | [ number , number ]
31
4
32
- export type MenuItem = Omit < Header , 'children' | 'slug '> & {
33
- element : HTMLHeadElement
34
- children ?: MenuItem [ ]
5
+ export type HeaderItem = Omit < PageHeader , 'children' > & {
6
+ element : HTMLHeadingElement
7
+ children ?: HeaderItem [ ]
35
8
}
36
9
37
10
export const resolveHeaders = (
38
- headers : MenuItem [ ] ,
11
+ headers : HeaderItem [ ] ,
39
12
levels : HeaderLevels = 2 ,
40
- ) : MenuItem [ ] => {
13
+ ) : HeaderItem [ ] => {
41
14
if ( levels === false ) {
42
15
return [ ]
43
16
}
@@ -49,29 +22,30 @@ export const resolveHeaders = (
49
22
? [ 2 , 6 ]
50
23
: levels
51
24
const allowedHeaders = headers . filter (
52
- ( h ) => h . level >= high && h . level <= low ,
25
+ ( header ) => header . level >= high && header . level <= low ,
53
26
)
54
27
55
- const res : MenuItem [ ] = [ ]
28
+ const result : HeaderItem [ ] = [ ]
56
29
57
30
// eslint-disable-next-line no-restricted-syntax
58
31
outer: for ( let i = 0 ; i < allowedHeaders . length ; i ++ ) {
59
- const cur = allowedHeaders [ i ]
32
+ const current = allowedHeaders [ i ]
33
+
60
34
if ( i === 0 ) {
61
- res . push ( cur )
35
+ result . push ( current )
62
36
} else {
63
37
for ( let j = i - 1 ; j >= 0 ; j -- ) {
64
38
const prev = allowedHeaders [ j ]
65
- if ( prev . level < cur . level ) {
66
- ; ( prev . children ??= [ ] ) . push ( cur )
39
+ if ( prev . level < current . level ) {
40
+ ; ( prev . children ??= [ ] ) . push ( current )
67
41
continue outer
68
42
}
69
43
}
70
- res . push ( cur )
44
+ result . push ( current )
71
45
}
72
46
}
73
47
74
- return res
48
+ return result
75
49
}
76
50
77
51
const serializeHeader = ( h : Element , ignore : string [ ] = [ ] ) : string => {
@@ -92,6 +66,20 @@ const serializeHeader = (h: Element, ignore: string[] = []): string => {
92
66
return text . trim ( )
93
67
}
94
68
69
+ export const getHeadersFromDom = (
70
+ selector : string ,
71
+ ignore : string [ ] ,
72
+ ) : HeaderItem [ ] =>
73
+ Array . from ( document . querySelectorAll ( selector ) )
74
+ . filter ( ( el ) => el . id && el . hasChildNodes ( ) )
75
+ . map ( ( el ) => ( {
76
+ element : el as HTMLHeadingElement ,
77
+ title : serializeHeader ( el , ignore ) ,
78
+ link : `#${ el . id } ` ,
79
+ slug : el . id ,
80
+ level : Number ( el . tagName [ 1 ] ) ,
81
+ } ) )
82
+
95
83
export interface GetHeadersOptions {
96
84
/**
97
85
* The selector of the headers.
@@ -134,18 +122,5 @@ export const getHeaders = ({
134
122
. join ( ',' ) ,
135
123
levels = 2 ,
136
124
ignore = [ ] ,
137
- } : GetHeadersOptions = { } ) : MenuItem [ ] => {
138
- const headers = Array . from ( document . querySelectorAll ( selector ) )
139
- . filter ( ( el ) => el . id && el . hasChildNodes ( ) )
140
- . map ( ( el ) => {
141
- const level = Number ( el . tagName [ 1 ] )
142
- return {
143
- element : el as HTMLHeadElement ,
144
- title : serializeHeader ( el , ignore ) ,
145
- link : `#${ el . id } ` ,
146
- slug : el . id ,
147
- level,
148
- }
149
- } )
150
- return resolveHeaders ( headers , levels )
151
- }
125
+ } : GetHeadersOptions = { } ) : HeaderItem [ ] =>
126
+ resolveHeaders ( getHeadersFromDom ( selector , ignore ) , levels )
0 commit comments