Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
2.3 kB ยท 75 lines
TSX
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576"use client";
import { useEffect, useState } from "react";
import { cn } from "../lib/utils";import type { TocItem } from "./toc";
function useActiveHeading(items: TocItem[]) { const [active, setActive] = useState<string>();
useEffect(() => { if (items.length === 0) return; const visible = new Set<string>(); const update = () => { const firstVisible = items.find((i) => visible.has(i.slug)); if (firstVisible) { setActive(firstVisible.slug); return; } // Nothing in the activation band (e.g. scrolled past the last heading): // fall back to the last heading already scrolled above it. let aboveBand: string | undefined; for (const { slug } of items) { const el = document.getElementById(slug); if (el && el.getBoundingClientRect().top < 0) aboveBand = slug; } setActive(aboveBand); }; const observer = new IntersectionObserver( (entries) => { for (const entry of entries) { if (entry.isIntersecting) visible.add(entry.target.id); else visible.delete(entry.target.id); } update(); }, { rootMargin: "0px 0px -70% 0px", threshold: 0 }, ); for (const { slug } of items) { const el = document.getElementById(slug); if (el) observer.observe(el); } return () => observer.disconnect(); }, [items]);
return active;}
export function TableOfContents({ items }: { items: TocItem[] }) { const active = useActiveHeading(items);
if (items.length === 0) return null;
return ( <nav aria-label="Table of contents" className="text-sm"> <p className="text-foreground py-2 font-mono font-medium">On this page</p> <ul> {items.map((item) => ( <li key={item.slug}> <a href={`#${item.slug}`} className={cn( "ease text-muted-foreground hover:bg-muted hover:text-foreground block py-2 break-words transition-colors duration-150 motion-reduce:transition-none", item.depth === 2 ? "pl-2" : "pl-4", active === item.slug && "bg-muted text-foreground", )} > {item.text} </a> </li> ))} </ul> </nav> );}