feat: Add support for nested sections in TableOfContents
This commit is contained in:
parent
1fb8ddc0a3
commit
2ee567303f
@ -16,19 +16,23 @@ export function TableOfContents() {
|
|||||||
|
|
||||||
const getHeadings = () => {
|
const getHeadings = () => {
|
||||||
return data.sections
|
return data.sections
|
||||||
|
.flatMap((section) => [section, ...section.children])
|
||||||
.map((section) => {
|
.map((section) => {
|
||||||
if (!section.hash) return null;
|
if (!section.hash) return null;
|
||||||
|
|
||||||
const el = document.getElementById(section.hash);
|
const el = document.getElementById(section.hash);
|
||||||
|
console.log(section.hash, el);
|
||||||
if (!el) return null;
|
if (!el) return null;
|
||||||
|
|
||||||
const style = window.getComputedStyle(el);
|
const style = window.getComputedStyle(el);
|
||||||
const scrollMt = Number.parseFloat(style.scrollMarginTop);
|
const scrollMt = Number.parseFloat(style.scrollMarginTop);
|
||||||
|
|
||||||
const top = window.scrollY + el.getBoundingClientRect().top - scrollMt;
|
const top = window.scrollY + el.getBoundingClientRect().top - scrollMt;
|
||||||
return { id: section.hash, top };
|
return { id: section.hash, top, level: section.level };
|
||||||
})
|
})
|
||||||
.filter((x): x is { id: string; top: number } => x !== null);
|
.filter(
|
||||||
|
(x): x is { id: string; top: number; level: number } => x !== null,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
@ -46,8 +50,11 @@ export function TableOfContents() {
|
|||||||
}
|
}
|
||||||
setCurrentSection(current);
|
setCurrentSection(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("scroll", onScroll, { passive: true });
|
window.addEventListener("scroll", onScroll, { passive: true });
|
||||||
|
|
||||||
onScroll();
|
onScroll();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("scroll", onScroll);
|
window.removeEventListener("scroll", onScroll);
|
||||||
};
|
};
|
||||||
@ -55,10 +62,14 @@ export function TableOfContents() {
|
|||||||
|
|
||||||
function isActive(section: DocSection) {
|
function isActive(section: DocSection) {
|
||||||
if (section.hash === currentSection()) return true;
|
if (section.hash === currentSection()) return true;
|
||||||
return false;
|
if (!section.children) return false;
|
||||||
// if (!section.children) return false;
|
|
||||||
|
|
||||||
// return section.children.findIndex(isActive) > -1;
|
return (
|
||||||
|
section.children.findIndex((child) => {
|
||||||
|
console.log(child.hash, currentSection());
|
||||||
|
return child.hash === currentSection();
|
||||||
|
}) !== -1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -87,6 +98,26 @@ export function TableOfContents() {
|
|||||||
{section.content}
|
{section.content}
|
||||||
</Link>
|
</Link>
|
||||||
</h3>
|
</h3>
|
||||||
|
{section.children.length > 0 && (
|
||||||
|
<ol class="mt-2 space-y-2 pl-4">
|
||||||
|
<For each={section.children}>
|
||||||
|
{(subsection) => (
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href={`#${subsection.hash}`}
|
||||||
|
class={clsx(
|
||||||
|
isActive(subsection)
|
||||||
|
? "text-violet-500"
|
||||||
|
: "font-normal text-slate-500 hover:text-slate-700",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{subsection.content}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</ol>
|
||||||
|
)}
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export type DocSection = {
|
|||||||
content: string;
|
content: string;
|
||||||
hash?: string;
|
hash?: string;
|
||||||
subsections: string[];
|
subsections: string[];
|
||||||
|
children: DocSection[];
|
||||||
level?: number;
|
level?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,15 +99,32 @@ class DocCache {
|
|||||||
if (["heading", "paragraph"].includes(node.type)) {
|
if (["heading", "paragraph"].includes(node.type)) {
|
||||||
const content = this.nodeToString(node).trim();
|
const content = this.nodeToString(node).trim();
|
||||||
|
|
||||||
if (node.type === "heading" && node.attributes?.level <= 2) {
|
if (node.type === "heading" && node.attributes?.level <= 3) {
|
||||||
const hash = (node.attributes?.id as string) ?? this.slugify(content);
|
const hash = (node.attributes?.id as string) ?? this.slugify(content);
|
||||||
const subsections: string[] = [];
|
const subsections: string[] = [];
|
||||||
sections.push({
|
|
||||||
content,
|
if (node.attributes?.level === 2) {
|
||||||
hash,
|
sections.push({
|
||||||
subsections,
|
content,
|
||||||
level: node.attributes?.level,
|
hash,
|
||||||
});
|
subsections,
|
||||||
|
children: [],
|
||||||
|
level: node.attributes?.level,
|
||||||
|
});
|
||||||
|
} else if (node.attributes?.level === 3) {
|
||||||
|
const lastSection = sections.at(-1);
|
||||||
|
|
||||||
|
if (lastSection) {
|
||||||
|
lastSection.subsections.push(hash);
|
||||||
|
lastSection.children.push({
|
||||||
|
content,
|
||||||
|
hash,
|
||||||
|
subsections,
|
||||||
|
children: [],
|
||||||
|
level: node.attributes?.level,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sections.at(-1)?.subsections.push(content);
|
sections.at(-1)?.subsections.push(content);
|
||||||
}
|
}
|
||||||
|
|||||||
1
temp.json
Normal file
1
temp.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"content":"qu-est-ce-que-le-hook-use-reducer","hash":"qu-est-ce-que-le-hook-use-reducer","level":2,"subsections":[{"content":"quand-utiliser-use-reducer","hash":"quand-utiliser-use-reducer","level":3,"subsections":[{"content":"a-quoi-ressemble-un-reducer","hash":"a-quoi-ressemble-un-reducer","level":2,"subsections":[{"content":"comment-utiliser-use-reducer","hash":"comment-utiliser-use-reducer","level":2,"subsections":[{"content":"on-nettoie-tout-ca","hash":"on-nettoie-tout-ca","level":2,"subsections":[{"content":"typage-des-actions","hash":"typage-des-actions","level":3,"subsections":[{"content":"action-creators","hash":"action-creators","level":3,"subsections":[{"content":"les-fichiers-complets","hash":"les-fichiers-complets","level":2,"subsections":[{"content":"fichier-counter-jsx-ou-counter-tsx","hash":"fichier-counter-jsx-ou-counter-tsx","level":3,"subsections":[{"content":"c-est-l-heure-des-questions","hash":"c-est-l-heure-des-questions","level":2,"subsections":[{"content":"conclusion","hash":"conclusion","level":2,"subsections":[]}]}]}]}]}]}]}]}]}]}]}]
|
||||||
Loading…
Reference in New Issue
Block a user