mirror of
https://github.com/supabase/supabase.git
synced 2026-09-08 19:08:44 +08:00
* test fuma toc * make toc work * ok toc * ui-patterns toc * fix types * cleanup * fix overflow issues * cleanup * fix loading w * Update apps/docs/components/GuidesTableOfContents.tsx * fix prettier * format * install deps * target ES2018 * tweak regex * update docs test snapshots * fix useSubscribeTocRerender
65 lines
1.9 KiB
TypeScript
65 lines
1.9 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
|
|
/**
|
|
* Find the active heading of page
|
|
*
|
|
* It selects the top heading by default, and the last item when reached the bottom of page.
|
|
*
|
|
* @param watch - An array of element ids to watch
|
|
* @param single - only one active item at most
|
|
* @returns Active anchor
|
|
*/
|
|
export function useAnchorObserver(watch: string[], single: boolean): string[] {
|
|
const [activeAnchor, setActiveAnchor] = useState<string[]>([])
|
|
|
|
useEffect(() => {
|
|
let visible: string[] = []
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
for (const entry of entries) {
|
|
if (entry.isIntersecting && !visible.includes(entry.target.id)) {
|
|
visible = [...visible, entry.target.id]
|
|
} else if (!entry.isIntersecting && visible.includes(entry.target.id)) {
|
|
visible = visible.filter((v) => v !== entry.target.id)
|
|
}
|
|
}
|
|
|
|
if (visible.length > 0) setActiveAnchor(visible)
|
|
},
|
|
{
|
|
rootMargin: single ? '-80px 0% -70% 0%' : `-20px 0% -40% 0%`,
|
|
threshold: 1,
|
|
}
|
|
)
|
|
|
|
function onScroll(): void {
|
|
const element = document.scrollingElement
|
|
if (!element) return
|
|
|
|
if (element.scrollTop === 0 && single) setActiveAnchor(watch.slice(0, 1))
|
|
else if (element.scrollTop + element.clientHeight >= element.scrollHeight - 6) {
|
|
setActiveAnchor((active) => {
|
|
return active.length > 0 && !single
|
|
? watch.slice(watch.indexOf(active[0]))
|
|
: watch.slice(-1)
|
|
})
|
|
}
|
|
}
|
|
|
|
for (const heading of watch) {
|
|
const element = document.getElementById(heading)
|
|
|
|
if (element) observer.observe(element)
|
|
}
|
|
|
|
onScroll()
|
|
window.addEventListener('scroll', onScroll)
|
|
return () => {
|
|
window.removeEventListener('scroll', onScroll)
|
|
observer.disconnect()
|
|
}
|
|
}, [single, watch])
|
|
|
|
return single ? activeAnchor.slice(0, 1) : activeAnchor
|
|
}
|