diff --git a/components/StyledMarkdown.tsx b/components/StyledMarkdown.tsx index 8d89cc79b..aabcb9675 100644 --- a/components/StyledMarkdown.tsx +++ b/components/StyledMarkdown.tsx @@ -564,6 +564,38 @@ export function TableOfContentMarkdown({ markdown: string; depth?: number; }) { + // Add state to track the active section + const [activeSection, setActiveSection] = useState(null); + + // Set up intersection observer to track which section is in view + useEffect(() => { + const headings = document.querySelectorAll('h1, h2, h3, h4'); + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setActiveSection(entry.target.id); + } + }); + }, + { + rootMargin: '-100px 0px -80% 0px', + threshold: 0 + } + ); + + headings.forEach((heading) => { + observer.observe(heading); + }); + + return () => { + headings.forEach((heading) => { + observer.unobserve(heading); + }); + }; + }, []); + return ( { const slug = slugifyMarkdownHeadline(children); + const isActive = activeSection === slug; return ( {children} @@ -586,91 +622,107 @@ export function TableOfContentMarkdown({ h2: depth === 0 ? { + component: ({ children }) => { + const slug = slugifyMarkdownHeadline(children); + const isActive = activeSection === slug; + return ( + + {children} + + ); + }, + } + : depth >= 2 + ? { component: ({ children }) => { const slug = slugifyMarkdownHeadline(children); + const [isChrome, setIsChrome] = useState(false); + const isActive = activeSection === slug; + + useEffect(() => { + const chromeCheck = + /Chrome/.test(navigator.userAgent) && + /Google Inc/.test(navigator.vendor); + setIsChrome(chromeCheck); + }, []); + return ( + // chromeClass + + ● + {children} ); }, } - : depth >= 2 - ? { - component: ({ children }) => { - const slug = slugifyMarkdownHeadline(children); - const [isChrome, setIsChrome] = useState(false); - - useEffect(() => { - const chromeCheck = - /Chrome/.test(navigator.userAgent) && - /Google Inc/.test(navigator.vendor); - setIsChrome(chromeCheck); - }, []); - - return ( - // chromeClass - - - ● - - {children} - - ); - }, - } : { component: () => null }, h3: depth >= 3 ? { - component: ({ children }) => { - const slug = slugifyMarkdownHeadline(children); - return ( - - - —— - - - ● - + component: ({ children }) => { + const slug = slugifyMarkdownHeadline(children); + const isActive = activeSection === slug; + return ( + + + —— + + + ● + - {children} - - ); - }, - } + {children} + + ); + }, + } : { component: () => null }, h4: depth >= 4 ? { - component: ({ children }) => { - const slug = slugifyMarkdownHeadline(children); - return ( - - - ———— - - - ● - + component: ({ children }) => { + const slug = slugifyMarkdownHeadline(children); + const isActive = activeSection === slug; + return ( + + + ———— + + + ● + - {children} - - ); - }, - } /* eslint-enable */ + {children} + + ); + }, + } /* eslint-enable */ : { component: () => null }, ...hiddenElements( 'strong',