Repository navigation
Expand file tree
/
Copy pathPageNav.tsx
More file actions
112 lines (95 loc) · 2.49 KB
/
Copy pathPageNav.tsx
File metadata and controls
112 lines (95 loc) · 2.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import tw from "twin.macro";
import React, { useEffect, useState } from "react";
import { Link } from "./Link";
export interface Props {
title: string;
}
interface IHeader {
level: number;
title: string;
id: string;
subHeaders: IHeader[];
}
const nodeNameToLevel = { H1: 1, H2: 2, H3: 3, H4: 4 };
const nodeToHeader = (node: HTMLHeadingElement): IHeader => ({
level: nodeNameToLevel[node.nodeName],
title: node.innerText,
id: node.id,
subHeaders: [],
});
const buildHeaderTreeRec = (
nodes: HTMLHeadingElement[],
elm: IHeader,
level: number,
): IHeader[] => {
const headers: IHeader[] = [];
while (nodes.length > 0) {
const h = nodeToHeader(nodes[0]);
if (h.level === level) {
headers.push(h);
nodes.shift();
elm = h;
} else if (h.level > level) {
elm.subHeaders = buildHeaderTreeRec(nodes, elm, h.level);
} else {
break;
}
}
return headers;
};
const buildHeaderTree = (nodes: HTMLHeadingElement[]): IHeader[] => {
if (nodes.length === 0) {
return [];
}
const n = nodes[0];
const h = nodeToHeader(n);
return buildHeaderTreeRec(nodes, h, h.level);
};
export const PageNav: React.FC<Props> = ({ title }) => {
const [headers, setHeaders] = useState<IHeader[]>([]);
useEffect(() => {
const documentHeaders = Array.from(
document.querySelectorAll(".docs-content h1, h2, h3, h4"),
) as HTMLHeadingElement[];
setHeaders(buildHeaderTree(documentHeaders));
}, [title]);
return (
<div tw="flex-col w-[250px] pt-8 pl-12 xl:pl-16 pr-0 pb-6 min-w-pageNav hidden lg:flex">
{headers.length !== 0 && (
<aside tw="sticky top-24">
<h5 tw="text-sm text-gray-900 font-medium mb-3">On This Page</h5>
<ul tw="space-y-3">
<HeaderList headers={headers} nesting={0} />
</ul>
</aside>
)}
</div>
);
};
const nestingTw = {
0: tw`ml-0`,
1: tw`ml-6`,
2: tw`ml-10`,
};
const HeaderList: React.FC<{ headers: IHeader[]; nesting: number }> = ({
headers,
nesting,
}) => (
<>
{headers.map((h, i) => (
<React.Fragment key={`${h.id}-${i}`}>
<li key={h.id} css={[nestingTw[nesting]]}>
<Link
css={[tw`inline-block text-gray-600 text-sm`, tw`hover:underline`]}
href={`#${h.id}`}
>
{h.title}
</Link>
</li>
{h.subHeaders.length > 0 && (
<HeaderList headers={h.subHeaders} nesting={nesting + 1} />
)}
</React.Fragment>
))}
</>
);