import { ACTIVE_CLASS } from "#configuration/constants/element.constants"; import { ANCHOR_PREFIX } from "#configuration/constants/document.constants"; import type { Disposer } from "#types/base.types"; const ROOT_MARGIN = "-20% 0px -60% 0px"; const THRESHOLD = [0, 0.25, 0.5, 0.75, 1]; const markActive = function markActive(links: readonly HTMLAnchorElement[], id: string): void { for (const link of links) { link.classList.toggle(ACTIVE_CLASS, link.hash === ANCHOR_PREFIX + id); } }; const bestEntry = function bestEntry(ratios: ReadonlyMap): string | null { let best: string | null = null; let bestRatio = 0; for (const [id, ratio] of ratios) { if (ratio > bestRatio) { bestRatio = ratio; best = id; } } return best; }; export const observeSections = function observeSections( sections: readonly Element[], links: readonly HTMLAnchorElement[], ): Disposer { const ratios = new Map(); const observer = new IntersectionObserver( (entries) => { for (const entry of entries) { ratios.set(entry.target.id, entry.isIntersecting ? entry.intersectionRatio : 0); } const active = bestEntry(ratios); if (active !== null) { markActive(links, active); } }, { rootMargin: ROOT_MARGIN, threshold: THRESHOLD }, ); const track = observer.observe.bind(observer); for (const section of sections) { track(section); } return () => { observer.disconnect(); }; };