{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "toc-minimap", "title": "TOC Minimap", "author": "tusharvarshney ", "description": "Navigate page sections with a compact, hoverable TOC minimap.", "registryDependencies": [ "hover-card", "@soundcn/u-mini-map-open" ], "files": [ { "path": "src/registry/components/toc-minimap/toc-minimap.tsx", "content": "\"use client\"\n\nimport { useEffect, useMemo, useState } from \"react\"\n\nimport {\n HoverCard,\n HoverCardContent,\n HoverCardTrigger,\n} from \"@/components/ui/hover-card\"\nimport { useSound } from \"@/hooks/soundcn/use-sound\"\nimport { uMiniMapOpenSound } from \"@/lib/soundcn/u-mini-map-open\"\nimport { cn } from \"@/lib/utils\"\n\nexport type TOCItemType = {\n title: React.ReactNode\n url: string\n depth: number\n}\n\nexport type TOCMinimapProps = {\n /** @fumadocsHref #tocitemtype */\n items: TOCItemType[]\n className?: string\n}\n\nexport function TOCMinimap({ items, className }: TOCMinimapProps) {\n const itemIds = useMemo(\n () => items.map((item) => item.url.replace(\"#\", \"\")),\n [items]\n )\n\n const activeHeading = useActiveHeading(itemIds)\n\n const [play] = useSound(uMiniMapOpenSound, { volume: 0.3 })\n\n if (!items.length) {\n return null\n }\n\n return (\n
\n {\n if (open) play()\n }}\n >\n \n
\n {items.map((item) => (\n \n ))}\n
\n
\n\n \n
\n
    \n {items.map((item) => (\n
  • \n \n {item.title}\n \n
  • \n ))}\n
\n
\n \n \n
\n )\n}\n\nexport function useActiveHeading(itemIds: string[]) {\n const [activeId, setActiveId] = useState(null)\n\n useEffect(() => {\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n setActiveId(entry.target.id)\n }\n }\n },\n { rootMargin: \"0% 0% -80% 0%\", threshold: 0.98 }\n )\n\n for (const id of itemIds ?? []) {\n const element = document.getElementById(id)\n if (element) {\n observer.observe(element)\n }\n }\n\n return () => {\n for (const id of itemIds ?? []) {\n const element = document.getElementById(id)\n if (element) {\n observer.unobserve(element)\n }\n }\n }\n }, [itemIds])\n\n return activeId\n}\n\nfunction handleItemClick(e: React.MouseEvent) {\n e.preventDefault()\n const url = e.currentTarget.getAttribute(\"href\") ?? \"\"\n scrollToHeading(url)\n}\n\nfunction scrollToHeading(url: string) {\n history.pushState(null, \"\", url)\n document.getElementById(url.replace(\"#\", \"\"))?.scrollIntoView({\n behavior: \"smooth\",\n })\n}\n", "type": "registry:component", "target": "@components/toc-minimap.tsx" } ], "docs": "https://tusharvarshney.com/components/toc-minimap", "type": "registry:component" }