-
-
Notifications
You must be signed in to change notification settings - Fork 93
Expand file tree
/
Copy pathDynamicTOC.tsx
More file actions
83 lines (65 loc) · 2 KB
/
DynamicTOC.tsx
File metadata and controls
83 lines (65 loc) · 2 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
import { useEffect } from "react";
interface Props {
minHeadingLevel?: number;
maxHeadingLevel?: number;
}
export function DynamicTOC({
minHeadingLevel = 2,
maxHeadingLevel = 3,
}: Props) {
useEffect(() => {
const row = document.querySelector("main .row");
if (row == null) {
console.error("No row found in the main element");
return;
}
const toc = getTOC(minHeadingLevel, maxHeadingLevel);
// Remove existing TOC if it exists
if (row.childNodes.length > 1) {
row.replaceChild(toc, row.childNodes[1]);
} else {
row.appendChild(toc);
}
}, [minHeadingLevel, maxHeadingLevel]);
return null;
}
function getTOC(minHeadingLevel: number, maxHeadingLevel: number) {
const col = document.createElement("div");
col.className = "col col--3";
const toc = document.createElement("div");
toc.className = "tableOfContents_TDAO thin-scrollbar";
const ul = document.createElement("ul");
ul.className = "table-of-contents table-of-contents__left-border";
let currentLevel: number | undefined = undefined;
let indent = 0;
getHeaderElements(minHeadingLevel, maxHeadingLevel).forEach((header) => {
const level = parseInt(header.tagName[1], 10);
if (level !== currentLevel) {
if (currentLevel != null) {
indent += level < currentLevel ? -1 : 1;
}
currentLevel = level;
}
const li = document.createElement("li");
const a = document.createElement("a");
a.href = `#${header.id}`;
a.className = "table-of-contents__link";
a.textContent = header.textContent;
a.style.paddingLeft = `${indent}rem`;
li.appendChild(a);
ul.appendChild(li);
});
toc.appendChild(ul);
col.appendChild(toc);
return col;
}
function getHeaderElements(
minHeadingLevel: number,
maxHeadingLevel: number,
): NodeListOf<HTMLHeadingElement> {
const queryParts = [];
for (let i = minHeadingLevel; i <= maxHeadingLevel; i++) {
queryParts.push(`h${i}`);
}
return document.querySelectorAll(queryParts.join(", "));
}