Files
supabase/apps/docs/features/docs/GuidesMdx.client.tsx
Maksym Ionutsa e8e91234e7 fix: use heading id for TOC anchors instead of querying anchor tags (#41702)
The previous implementation would break for headers containing markdown
links, incorrectly grabbing external link hrefs instead of anchor
fragments.
2026-01-06 16:15:05 +00:00

67 lines
1.9 KiB
TypeScript

'use client'
import { useEffect, useState, type PropsWithChildren } from 'react'
import { AnchorProvider } from 'ui-patterns'
import { TocAnchorsContext, useSubscribeTocRerender } from './GuidesMdx.state'
interface TOCHeader {
id?: string
text: string
link: string
level: number
}
const TocAnchorsProvider = ({ children }: PropsWithChildren) => {
const [tocList, setTocList] = useState<TOCHeader[]>([])
useSubscribeTocRerender()
const displayedList = tocList
const toc = displayedList.map((item) => ({
title: item.text,
url: item.link,
depth: item.level,
}))
useEffect(() => {
/**
* Because we're directly querying the DOM, needs the setTimeout so the DOM
* update will happen first.
*/
const timeoutHandle = setTimeout(() => {
const headings = Array.from(
document.querySelector('#sb-docs-guide-main-article')?.querySelectorAll('h2, h3') ?? []
)
const newHeadings = headings
.filter((heading) => heading.id)
.map((heading) => {
const text = heading.textContent?.replace('#', '') || ''
const link = heading.id ? `#${heading.id}` : null
const level = heading.tagName === 'H2' ? 2 : 3
return { text, link, level } as Partial<TOCHeader>
})
.filter((x): x is TOCHeader => !!x && !!x.text && !!x.link && !!x.level)
setTocList(newHeadings)
})
return () => clearTimeout(timeoutHandle)
/**
* window.location.href needed to recalculate toc when page changes,
* `useSubscribeTocRerender` above will trigger the rerender
*/
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [typeof window !== 'undefined' && window.location.href])
return (
<TocAnchorsContext.Provider value={{ toc }}>
<AnchorProvider toc={toc} single={false}>
{children}
</AnchorProvider>
</TocAnchorsContext.Provider>
)
}
export { TocAnchorsProvider }