in docs-sdk/markdown-renderer/src/Layout/TableOfContent/index.tsx [76:153]
function useActiveHeading(
headings: ITocHeading[],
scrollContainer: string | undefined
) {
const [headingWithDomEl, setHeadingWithDomEl] = useState<
Array<IHeadingWithDomEl>
>([]);
const [activeId, setActiveId] = useState<string>("");
useEffect(() => {
setHeadingWithDomEl(
headings
.map(heading => {
const el = document.getElementById(heading.id);
if (!el) return (null as unknown) as IHeadingWithDomEl;
return { el, ...heading };
})
.filter(Boolean)
);
}, [headings]);
useEffect(() => {
let scrollContainerEl: Element | Window =
(scrollContainer && document.querySelector(scrollContainer)) ||
getScrollParent(headingWithDomEl[0]?.el) ||
window;
if (
scrollContainerEl === document.body ||
scrollContainerEl === document.documentElement
) {
scrollContainerEl = window;
}
const handle = () => {
if (!headingWithDomEl.length) return;
const scrollParent = scrollContainerEl;
const found = headingWithDomEl.find((heaidng, idx) => {
// 如果第一个heading都还没有滚动过去,那么高亮第一个heading
if (idx === 0 && !isHeadingScrolled(heaidng.el, scrollParent))
return true;
const nextHeading = headingWithDomEl[idx + 1];
if (nextHeading) {
return (
isHeadingScrolled(heaidng.el, scrollParent) &&
!isHeadingScrolled(nextHeading.el, scrollParent)
);
}
return isHeadingScrolled(heaidng.el, scrollParent);
});
setActiveId(found?.id ?? "");
};
handle();
scrollContainerEl.addEventListener("resize", handle);
scrollContainerEl.addEventListener("scroll", handle);
return () => {
scrollContainerEl.removeEventListener("resize", handle);
scrollContainerEl.removeEventListener("scroll", handle);
};
}, [headingWithDomEl, scrollContainer]);
return activeId;
function isHeadingScrolled(el: HTMLElement, scrollParent: Element | Window) {
if ("document" in scrollParent) {
return el.getBoundingClientRect().top <= 30;
}
return (
el.getBoundingClientRect().top -
scrollParent.getBoundingClientRect().top <=
30
);
}
}