pswui-docs/src/DynamicLayout.tsx

64 lines
2.1 KiB
TypeScript

import type { Toc } from "@stefanprobst/rehype-extract-toc";
import { Fragment, type ReactNode, useContext, useState } from "react";
import { useLocation } from "react-router-dom";
import { HeadingContext } from "./HeadingContext";
function RecursivelyToc({ toc }: { toc: Toc }) {
const location = useLocation();
const [activeHeadings] = useContext(HeadingContext);
return (
<ul>
{toc.map((tocEntry) => {
return (
<Fragment key={tocEntry.id}>
<li
key={tocEntry.id}
data-id={tocEntry.id}
className="text-neutral-500 data-[active='true']:text-black dark:data-[active='true']:text-white text-sm font-medium"
style={{ paddingLeft: `${tocEntry.depth - 1}rem` }}
data-active={
activeHeadings.includes(tocEntry.id ?? "")
? true
: location.hash.length > 0
? location.hash === `#${tocEntry.id}`
: false
}
>
<a href={`#${tocEntry.id}`}>{tocEntry.value}</a>
</li>
{Array.isArray(tocEntry.children) && (
<RecursivelyToc toc={tocEntry.children} />
)}
</Fragment>
);
})}
</ul>
);
}
export default function DynamicLayout({
children,
toc,
}: {
children: ReactNode;
toc: Toc;
}) {
const [activeHeadings, setActiveHeadings] = useState<string[]>([]);
return (
<HeadingContext.Provider value={[activeHeadings, setActiveHeadings]}>
<div className="w-full flex flex-col items-center">
<main className="w-full [:not(:where([class~='not-prose'],[class~='not-prose']_*))]:prose-sm prose lg:[:not(:where([class~='not-prose'],_[class~='not-prose']_*))]:prose-lg p-8 dark:prose-invert">
{children}
</main>
</div>
<nav className="hidden lg:flex flex-col gap-2 py-8 px-4 sticky top-16 overflow-auto max-h-[calc(100vh-4rem)]">
<span className="font-bold text-sm">On This Page</span>
<RecursivelyToc toc={toc} />
</nav>
</HeadingContext.Provider>
);
}