feat(nx-dev): add TOC markdoc component for blog posts
This commit is contained in:
parent
90e12a77af
commit
3e564864fd
@ -56,6 +56,8 @@ import { pill } from './lib/tags/pill.schema';
|
|||||||
import { fence } from './lib/nodes/fence.schema';
|
import { fence } from './lib/nodes/fence.schema';
|
||||||
import { FenceWrapper } from './lib/nodes/fence-wrapper.component';
|
import { FenceWrapper } from './lib/nodes/fence-wrapper.component';
|
||||||
import { VideoPlayer, videoPlayer } from './lib/tags/video-player.component';
|
import { VideoPlayer, videoPlayer } from './lib/tags/video-player.component';
|
||||||
|
import { TableOfContents } from './lib/tags/table-of-contents.component';
|
||||||
|
import { tableOfContents } from './lib/tags/table-of-contents.schema';
|
||||||
// TODO fix this export
|
// TODO fix this export
|
||||||
export { GithubRepository } from './lib/tags/github-repository.component';
|
export { GithubRepository } from './lib/tags/github-repository.component';
|
||||||
|
|
||||||
@ -92,6 +94,7 @@ export const getMarkdocCustomConfig = (
|
|||||||
tab,
|
tab,
|
||||||
tabs,
|
tabs,
|
||||||
'terminal-video': terminalVideo,
|
'terminal-video': terminalVideo,
|
||||||
|
toc: tableOfContents,
|
||||||
tweet,
|
tweet,
|
||||||
youtube,
|
youtube,
|
||||||
'video-link': videoLink,
|
'video-link': videoLink,
|
||||||
@ -121,6 +124,7 @@ export const getMarkdocCustomConfig = (
|
|||||||
SideBySide,
|
SideBySide,
|
||||||
Tab,
|
Tab,
|
||||||
Tabs,
|
Tabs,
|
||||||
|
TableOfContents,
|
||||||
TerminalVideo,
|
TerminalVideo,
|
||||||
Tweet,
|
Tweet,
|
||||||
YouTube,
|
YouTube,
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
interface TocItem {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
level: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TableOfContentsProps {
|
||||||
|
maxDepth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TableOfContents({
|
||||||
|
maxDepth = 3,
|
||||||
|
}: TableOfContentsProps): JSX.Element {
|
||||||
|
const [headings, setHeadings] = useState<TocItem[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Find the main content wrapper where markdown content is rendered
|
||||||
|
const content = document.querySelector('[data-document="main"]');
|
||||||
|
if (!content) return;
|
||||||
|
|
||||||
|
// Get all headings h1-h6 within the content
|
||||||
|
const headingElements = content.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||||
|
|
||||||
|
const items: TocItem[] = Array.from(headingElements)
|
||||||
|
.map((heading) => {
|
||||||
|
const level = parseInt(heading.tagName[1]);
|
||||||
|
if (level > maxDepth) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: heading.id,
|
||||||
|
text: heading.textContent || '',
|
||||||
|
level,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((item): item is TocItem => item !== null);
|
||||||
|
|
||||||
|
setHeadings(items);
|
||||||
|
}, [maxDepth]);
|
||||||
|
|
||||||
|
if (headings.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="toc not-prose mb-8 rounded-lg border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-900/50">
|
||||||
|
<p className="mb-3 text-sm font-semibold text-slate-900 dark:text-slate-100">
|
||||||
|
Table of Contents
|
||||||
|
</p>
|
||||||
|
<ul className="space-y-2 text-sm">
|
||||||
|
{headings.map((heading) => (
|
||||||
|
<li
|
||||||
|
key={heading.id}
|
||||||
|
style={{ paddingLeft: `${(heading.level - 1) * 1}rem` }}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={`#${heading.id}`}
|
||||||
|
className="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200"
|
||||||
|
>
|
||||||
|
{heading.text}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
11
nx-dev/ui-markdoc/src/lib/tags/table-of-contents.schema.ts
Normal file
11
nx-dev/ui-markdoc/src/lib/tags/table-of-contents.schema.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Schema } from '@markdoc/markdoc';
|
||||||
|
|
||||||
|
export const tableOfContents: Schema = {
|
||||||
|
render: 'TableOfContents',
|
||||||
|
attributes: {
|
||||||
|
maxDepth: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user