import clsx from "clsx";
import { useEffect, useMemo, useState } from "react";
import ButtonEvent from "../buttons/ButtonEvent";
interface Item {
title: string;
url: string;
items?: Item[];
}
interface Items {
items?: Item[];
}
interface TocProps {
title: string;
toc: Items;
}
export function DashboardTableOfContents({ title, toc }: TocProps) {
const itemIds = useMemo(
() =>
toc.items
? toc.items
.flatMap((item) => [item.url, item?.items?.map((item) => item.url)])
.flat()
.filter(Boolean)
.map((id) => id?.split("#")[1])
: [],
[toc]
);
const activeHeading = useActiveItem(itemIds.filter(Boolean) as string[]);
const mounted = useMounted();
if (!toc?.items || !mounted) {
return null;
}
return (
);
}
function useActiveItem(itemIds: string[]) {
const [activeId, setActiveId] = useState(undefined);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveId(entry.target.id ?? undefined);
}
});
},
{ rootMargin: `0% 0% -80% 0%` }
);
itemIds?.forEach((id) => {
const element = document.getElementById(id);
if (element) {
observer.observe(element);
}
});
return () => {
itemIds?.forEach((id) => {
const element = document.getElementById(id);
if (element) {
observer.unobserve(element);
}
});
};
}, [itemIds]);
return activeId;
}
interface TreeProps {
tree: Items;
level?: number;
activeItem?: string;
}
function Tree({ tree, level = 1, activeItem }: TreeProps) {
return tree?.items?.length && level < 3 ? (
{tree.items.map((item, index) => {
return (
-
{item.title}
{item.items?.length ? : null}
);
})}
) : null;
}
function useMounted() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return mounted;
}