From 5e11d3e13e1252ab69f2fdc9c595660df7b04aae Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Tue, 31 Mar 2026 02:27:05 +0200 Subject: [PATCH] feat: add TableOfContents component with IntersectionObserver --- components/table-of-contents.tsx | 63 ++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 components/table-of-contents.tsx diff --git a/components/table-of-contents.tsx b/components/table-of-contents.tsx new file mode 100644 index 0000000..a0e42db --- /dev/null +++ b/components/table-of-contents.tsx @@ -0,0 +1,63 @@ +"use client"; + +import { useEffect, useState } from "react"; +import type { Heading } from "@/lib/posts"; + +interface TableOfContentsProps { + headings: Heading[]; +} + +export default function TableOfContents({ headings }: TableOfContentsProps) { + const [activeSlug, setActiveSlug] = useState(""); + + useEffect(() => { + if (headings.length === 0) return; + + const observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + setActiveSlug(entry.target.id); + } + } + }, + { rootMargin: "0% 0% -70% 0%", threshold: 0 } + ); + + headings.forEach(({ slug }) => { + const el = document.getElementById(slug); + if (el) observer.observe(el); + }); + + return () => observer.disconnect(); + }, [headings]); + + if (headings.length === 0) return null; + + return ( + + ); +}