180 lines
5.6 KiB
TypeScript
180 lines
5.6 KiB
TypeScript
import React, { DetailedHTMLProps, HTMLAttributes } from "react";
|
|
import { DocsLinkType } from ".";
|
|
import Stack from "../../layout/Stack";
|
|
import TWUIDocsLink from "./TWUIDocsLink";
|
|
import { twMerge } from "tailwind-merge";
|
|
import Span from "../../layout/Span";
|
|
import Row from "../../layout/Row";
|
|
import { ArrowUpRight, LinkIcon, ListIcon } from "lucide-react";
|
|
import Link from "../../layout/Link";
|
|
|
|
type Props = DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> & {
|
|
before?: React.ReactNode;
|
|
after?: React.ReactNode;
|
|
autoExpandAll?: boolean;
|
|
editPageURL?: string;
|
|
};
|
|
export default function TWUIDocsRightAside({
|
|
after,
|
|
before,
|
|
autoExpandAll,
|
|
editPageURL,
|
|
...props
|
|
}: Props) {
|
|
const [links, setLinks] = React.useState<DocsLinkType[]>([]);
|
|
const [ready, setReady] = React.useState(false);
|
|
|
|
React.useEffect(() => {
|
|
if (!ready) return;
|
|
|
|
const headerHrefs = document.querySelectorAll(
|
|
".twui-docs-header-anchor"
|
|
);
|
|
|
|
const linksArr: DocsLinkType[] = [];
|
|
|
|
for (let i = 0; i < headerHrefs.length; i++) {
|
|
const anchorEl = headerHrefs[i] as HTMLAnchorElement;
|
|
|
|
const isH2Element = anchorEl.querySelector("h2") !== null;
|
|
|
|
if (isH2Element) {
|
|
let newLink: DocsLinkType = {
|
|
title: anchorEl.textContent || "",
|
|
href: `#${anchorEl.id}`,
|
|
};
|
|
|
|
let nexElIndex = i + 1;
|
|
|
|
while (nexElIndex < headerHrefs.length) {
|
|
const nextElement = headerHrefs[
|
|
nexElIndex
|
|
] as HTMLAnchorElement;
|
|
|
|
const nextElementH3 = nextElement.querySelector("h3");
|
|
|
|
console.log("nextElement", nextElement);
|
|
|
|
const isNextElementH2 =
|
|
nextElement.querySelector("h2") !== null;
|
|
|
|
if (isNextElementH2) {
|
|
break;
|
|
}
|
|
|
|
if (!nextElementH3) {
|
|
break;
|
|
}
|
|
|
|
if (!newLink.children) {
|
|
newLink.children = [];
|
|
}
|
|
|
|
newLink.children.push({
|
|
title: nextElementH3.textContent || "",
|
|
href: `#${nextElement.id}`,
|
|
});
|
|
|
|
nexElIndex++;
|
|
}
|
|
|
|
linksArr.push(newLink);
|
|
}
|
|
}
|
|
|
|
setLinks(linksArr);
|
|
}, [ready]);
|
|
|
|
React.useEffect(() => {
|
|
if (!links.length) return;
|
|
|
|
const headerHrefs = document.querySelectorAll(
|
|
"a.twui-docs-header-anchor"
|
|
);
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
const id = entry.target.id;
|
|
|
|
const link = document.querySelector(
|
|
`.twui-docs-right-aside a[href="#${id}"]`
|
|
);
|
|
if (link) {
|
|
link.classList.add("active");
|
|
}
|
|
} else {
|
|
const id = entry.target.id;
|
|
const link = document.querySelector(
|
|
`.twui-docs-right-aside a[href="#${id}"]`
|
|
);
|
|
if (link) {
|
|
link.classList.remove("active");
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
headerHrefs.forEach((headerHref) => {
|
|
observer.observe(headerHref);
|
|
});
|
|
}, [links]);
|
|
|
|
React.useEffect(() => {
|
|
setTimeout(() => {
|
|
setReady(true);
|
|
}, 100);
|
|
}, []);
|
|
|
|
return (
|
|
<aside
|
|
{...props}
|
|
className={twMerge(
|
|
"pb-10 hidden xl:flex min-w-[150px] max-w-[200px]",
|
|
"sticky top-6 twui-docs-right-aside",
|
|
props.className
|
|
)}
|
|
>
|
|
<Stack className="w-full overflow-hidden">
|
|
{before}
|
|
<Stack className="w-full">
|
|
<Row>
|
|
<ListIcon size={12} opacity={0.5} />
|
|
<Span size="smaller" variant="faded">
|
|
On this page
|
|
</Span>
|
|
</Row>
|
|
{links.map((link, index) => (
|
|
<TWUIDocsLink
|
|
docLink={link}
|
|
key={index}
|
|
autoExpandAll
|
|
childWrapperProps={{
|
|
className: "pl-2",
|
|
}}
|
|
wrapperProps={{
|
|
className:
|
|
"[&_svg]:hidden [&_a]:text-xs [&_a]:overflow-hidden [&_a]:overflow-ellipsis",
|
|
}}
|
|
/>
|
|
))}
|
|
</Stack>
|
|
{after}
|
|
{editPageURL && (
|
|
<Link
|
|
href={editPageURL}
|
|
target="_blank"
|
|
className="text-[12px] mt-2"
|
|
>
|
|
<Row className="gap-2">
|
|
<LinkIcon size={12} opacity={0.5} />
|
|
<span>Edit This Page</span>
|
|
<ArrowUpRight size={15} className="-ml-1" />
|
|
</Row>
|
|
</Link>
|
|
)}
|
|
</Stack>
|
|
</aside>
|
|
);
|
|
}
|