import { Menu } from "@headlessui/react"; import { type MarkdownHeading } from "astro"; import clsx from "clsx"; import { useEffect, useMemo, useState } from "react"; interface OnThisPageLinksProps { headings: MarkdownHeading[]; title: string; isRtl: boolean; } export default function OnThisPageLinks({ headings, title, isRtl, }: OnThisPageLinksProps) { const isLtr = !isRtl; const memoedHeadings = useMemo(() => { // add isVisible flag in headers const headers = [ { depth: 2, slug: "overview", text: title, isVisible: true }, ...headings.map((h) => ({ ...h, isVisible: false })), ]; return headers.filter(({ depth }) => depth > 1 && depth < 4); }, [headings, title]); const [headingWithIsVisible, setHeadingWithIsVisible] = useState(memoedHeadings); useEffect(() => { const articleHeadings = Array.from( document.querySelectorAll("article :is(h1,h2,h3)"), ); const allObservers = articleHeadings.map((h) => { const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { const id = entry.target.id; const tocItem = Array.from( document.querySelectorAll(`a[href="#${id}"]`), ).at(-1); // occurs when the id = "toc-heading" if (!tocItem) return; if (entry.isIntersecting) { const headings = headingWithIsVisible.map((h) => h.slug === id ? { ...h, isVisible: true } : { ...h, isVisible: false }, ); setHeadingWithIsVisible(headings); } }); }, { rootMargin: "-100px 0% -66%", threshold: 1, }, ); observer.observe(h); return observer; }); return () => { // unobserve elements articleHeadings.map((h, index) => { allObservers[index]?.unobserve(h); }); }; }, [headings, title]); return (
{({ open }) => (
On this page
    {headingWithIsVisible.map((heading) => (
  • {({ active }) => ( {heading.text} )}
  • ))}
)}
); }