Skip to content

Commit 6960aa4

Browse files
committed
Add TableOfContents component
1 parent 26eaa66 commit 6960aa4

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script lang="ts">
2+
import { cn } from '@/utils';
3+
import { onMount } from 'svelte';
4+
import type { TOC, TOCElem } from '@/lib/tableOfContents';
5+
6+
let {
7+
tableOfContents,
8+
class: className,
9+
...restProps
10+
}: {
11+
tableOfContents: TOC;
12+
class?: string;
13+
} = $props();
14+
15+
let container: HTMLDivElement;
16+
let stack: Array<HTMLUListElement | HTMLLIElement> = [];
17+
18+
function createAnchor(elem: TOCElem, listItem: HTMLLIElement) {
19+
let anchor = document.createElement('a');
20+
anchor.textContent = elem.title;
21+
anchor.href = '#' + elem.id;
22+
anchor.classList += 'text-blue-500';
23+
listItem.appendChild(anchor);
24+
}
25+
26+
function applyListStyles(elem: HTMLLIElement | HTMLUListElement) {
27+
elem.classList += 'list-decimal';
28+
}
29+
30+
function processTOCLayer(layer: TOC) {
31+
let currentElement = stack.at(-1);
32+
if (currentElement == null) return;
33+
34+
if (layer.length > 0) {
35+
for (const elem of layer) {
36+
let listItem = document.createElement('li');
37+
applyListStyles(listItem);
38+
if ('title' in elem && 'id' in elem) {
39+
createAnchor(elem, listItem);
40+
41+
currentElement.appendChild(listItem);
42+
} else {
43+
createAnchor(elem[0], listItem);
44+
45+
let list = document.createElement('ul');
46+
applyListStyles(list);
47+
stack.push(list);
48+
processTOCLayer(elem[1]);
49+
listItem.appendChild(list);
50+
currentElement.appendChild(listItem);
51+
}
52+
stack.pop();
53+
}
54+
}
55+
}
56+
57+
onMount(() => {
58+
let tocRoot = document.createElement('ul');
59+
applyListStyles(tocRoot);
60+
stack.push(tocRoot);
61+
processTOCLayer(tableOfContents);
62+
container.appendChild(tocRoot);
63+
});
64+
</script>
65+
66+
<div class={cn('', className)} {...restProps}>
67+
<p class="text-foreground text-xl font-bold">Table of Contents</p>
68+
<div bind:this={container}></div>
69+
</div>

src/lib/lib/tableOfContents.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type TOCElem = {
2+
title: string;
3+
id: string;
4+
};
5+
6+
export type TOC = Array<TOCElem | [TOCElem, TOC]>;

0 commit comments

Comments
 (0)