Documentation site of NextGraph.org
https://docs.nextgraph.org
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
56 lines
1.5 KiB
56 lines
1.5 KiB
import type { FunctionalComponent } from 'preact';
|
|
import { useState, useEffect, useRef } from 'preact/hooks';
|
|
import type { MarkdownHeading } from 'astro';
|
|
|
|
type ItemOffsets = {
|
|
id: string;
|
|
topOffset: number;
|
|
};
|
|
|
|
const TableOfContents: FunctionalComponent<{ headings: MarkdownHeading[] }> = ({
|
|
headings = [],
|
|
}) => {
|
|
const itemOffsets = useRef<ItemOffsets[]>([]);
|
|
// FIXME: Not sure what this state is doing. It was never set to anything truthy.
|
|
const [activeId] = useState<string>('');
|
|
useEffect(() => {
|
|
const getItemOffsets = () => {
|
|
const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
|
|
itemOffsets.current = Array.from(titles).map((title) => ({
|
|
id: title.id,
|
|
topOffset: title.getBoundingClientRect().top + window.scrollY,
|
|
}));
|
|
};
|
|
|
|
getItemOffsets();
|
|
window.addEventListener('resize', getItemOffsets);
|
|
|
|
return () => {
|
|
window.removeEventListener('resize', getItemOffsets);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
<h2 className="heading">On this page</h2>
|
|
<ul>
|
|
<li className={`heading-link depth-2 ${activeId === 'overview' ? 'active' : ''}`.trim()}>
|
|
<a href="#overview">Overview</a>
|
|
</li>
|
|
{headings
|
|
.filter(({ depth }) => depth > 1 && depth < 4)
|
|
.map((heading) => (
|
|
<li
|
|
className={`heading-link depth-${heading.depth} ${
|
|
activeId === heading.slug ? 'active' : ''
|
|
}`.trim()}
|
|
>
|
|
<a href={`#${heading.slug}`}>{heading.text}</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default TableOfContents;
|
|
|