-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathToc.tsx
103 lines (92 loc) · 3.41 KB
/
Toc.tsx
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
"use client";
import { clsx } from "clsx";
import type { TocItem, HeadingDepth, HeadingParent } from "remark-flexible-toc";
import styles from "./Toc.module.css";
// toc - an array of table of contents items provided by the remark plugin "remark-flexible-toc"
// maxDepth (default: 6) — max heading depth to include in the table of contents; this is inclusive: when set to 3, level three headings are included
// indented (default: false) — whether to add indent to list items according to heading levels
// ordered (default: false) — whether to add numbering to list items as an ordered list
// tight (default: false) — whether to compile list items tightly, otherwise space is added around items
// exclude — headings to skip, wrapped in new RegExp('^(' + value + ')$', 'i'); any heading matching this expression will not be present in the table of contents
// skipLevels (default: [1]) — disallowed heading levels, by default the article h1 is not expected to be in the TOC
// skipParents — disallow headings to be children of certain node types,(if the parent is "root", it is not skipped)
type Props = {
toc: TocItem[];
maxDepth?: HeadingDepth;
indented?: boolean;
ordered?: boolean;
tight?: boolean;
exclude?: string | string[];
skipLevels?: HeadingDepth[];
skipParents?: Exclude<HeadingParent, "root">[];
};
const Toc = ({
toc,
maxDepth = 6,
ordered = false,
indented = false,
tight = false,
exclude,
skipLevels = [1],
skipParents = [],
}: Props) => {
if (!toc) return null;
// ********* filters **************
const exludeRegexFilter = exclude
? Array.isArray(exclude)
? new RegExp(exclude.join("|"), "i")
: new RegExp(exclude, "i")
: new RegExp("(?!.*)");
const skipLevelsFilter = (depth: TocItem["depth"]): boolean =>
skipLevels.includes(depth);
const skipParentsFilter = (parent: TocItem["parent"]): boolean =>
parent !== "root" && skipParents.includes(parent);
const maxDepthFilter = (depth: TocItem["depth"]): boolean => depth > maxDepth;
// ********************************
const filteredToc = toc.filter(
(heading) =>
!maxDepthFilter(heading.depth) &&
!skipLevelsFilter(heading.depth) &&
!skipParentsFilter(heading.parent) &&
!exludeRegexFilter.test(heading.value)
);
return (
<details
className={styles["toc-container"]}
onClick={(e) => {
e.currentTarget.classList.toggle(styles.close);
}}
open
>
<summary className={styles["toc-title"]}>
<strong>TABLE OF CONTENTS</strong>
</summary>
<ul className={styles["toc-list"]}>
{filteredToc.map((heading) => (
<li
key={heading.value}
className={clsx(
indented && styles[`h${heading.depth}indent`],
tight && styles["tight"]
)}
>
<a href={heading.href}>
<div className={`h${heading.depth}`}>
{ordered ? (
<strong>
<span className={styles["numbering"]}>
{heading.numbering.slice(1).join(".")}.
</span>
</strong>
) : null}
<span className={styles["heading"]}>{heading.value}</span>
<span className={styles["href"]}>{heading.href}</span>
</div>
</a>
</li>
))}
</ul>
</details>
);
};
export default Toc;