diff options
author | 2022-11-17 21:18:57 +0530 | |
---|---|---|
committer | 2022-11-17 10:48:57 -0500 | |
commit | fcfd166f2d52dcf800fbaff6d618ad57eaf3cc9b (patch) | |
tree | b9f79e2c4ab451e3e3f8706806bc92d9be64931c /examples/docs/src/components/RightSidebar/TableOfContents.tsx | |
parent | 12236dbc06e1e43618b61d180020a67cb31499f8 (diff) | |
download | astro-fcfd166f2d52dcf800fbaff6d618ad57eaf3cc9b.tar.gz astro-fcfd166f2d52dcf800fbaff6d618ad57eaf3cc9b.tar.zst astro-fcfd166f2d52dcf800fbaff6d618ad57eaf3cc9b.zip |
fix: Docs Site - Table of contents highlight not working (#5411)
* fix: Docs Site - Table of contents highlight not working
* Add html-escaper devDep
* add html-escaper via pnpm
Diffstat (limited to 'examples/docs/src/components/RightSidebar/TableOfContents.tsx')
-rw-r--r-- | examples/docs/src/components/RightSidebar/TableOfContents.tsx | 59 |
1 files changed, 48 insertions, 11 deletions
diff --git a/examples/docs/src/components/RightSidebar/TableOfContents.tsx b/examples/docs/src/components/RightSidebar/TableOfContents.tsx index 5c6851462..34b0ab732 100644 --- a/examples/docs/src/components/RightSidebar/TableOfContents.tsx +++ b/examples/docs/src/components/RightSidebar/TableOfContents.tsx @@ -1,6 +1,7 @@ +import { unescape } from 'html-escaper'; +import type { MarkdownHeading } from 'astro'; import type { FunctionalComponent } from 'preact'; import { useState, useEffect, useRef } from 'preact/hooks'; -import type { MarkdownHeading } from 'astro'; type ItemOffsets = { id: string; @@ -10,9 +11,10 @@ type ItemOffsets = { const TableOfContents: FunctionalComponent<{ headings: MarkdownHeading[] }> = ({ headings = [], }) => { + const toc = useRef<HTMLUListElement>(); + const onThisPageID = 'on-this-page-heading'; const itemOffsets = useRef<ItemOffsets[]>([]); - // FIXME: Not sure what this state is doing. It was never set to anything truthy. - const [activeId] = useState<string>(''); + const [currentID, setCurrentID] = useState('overview'); useEffect(() => { const getItemOffsets = () => { const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)'); @@ -30,22 +32,57 @@ const TableOfContents: FunctionalComponent<{ headings: MarkdownHeading[] }> = ({ }; }, []); + useEffect(() => { + if (!toc.current) return; + + const setCurrent: IntersectionObserverCallback = (entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + const { id } = entry.target; + if (id === onThisPageID) continue; + setCurrentID(entry.target.id); + break; + } + } + }; + + const observerOptions: IntersectionObserverInit = { + // Negative top margin accounts for `scroll-margin`. + // Negative bottom margin means heading needs to be towards top of viewport to trigger intersection. + rootMargin: '-100px 0% -66%', + threshold: 1, + }; + + const headingsObserver = new IntersectionObserver(setCurrent, observerOptions); + + // Observe all the headings in the main page content. + document.querySelectorAll('article :is(h1,h2,h3)').forEach((h) => headingsObserver.observe(h)); + + // Stop observing when the component is unmounted. + return () => headingsObserver.disconnect(); + }, [toc.current]); + + const onLinkClick = (e) => { + setCurrentID(e.target.getAttribute('href').replace('#', '')); + }; + return ( <> - <h2 className="heading">On this page</h2> - <ul> - <li className={`heading-link depth-2 ${activeId === 'overview' ? 'active' : ''}`.trim()}> - <a href="#overview">Overview</a> - </li> + <h2 id={onThisPageID} className="heading"> + On this page + </h2> + <ul ref={toc}> {headings .filter(({ depth }) => depth > 1 && depth < 4) .map((heading) => ( <li - className={`heading-link depth-${heading.depth} ${ - activeId === heading.slug ? 'active' : '' + className={`header-link depth-${heading.depth} ${ + currentID === heading.slug ? 'current-header-link' : '' }`.trim()} > - <a href={`#${heading.slug}`}>{heading.text}</a> + <a href={`#${heading.slug}`} onClick={onLinkClick}> + {unescape(heading.text)} + </a> </li> ))} </ul> |