fumadocs-ui
Version:
The framework for building a documentation website in Next.js
53 lines (52 loc) • 2 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { useEffect, useRef } from 'react';
import * as Primitive from 'fumadocs-core/toc';
import { useOnChange } from 'fumadocs-core/utils/use-on-change';
import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
function calc(container, active) {
if (active.length === 0 || container.clientHeight === 0) {
return [0, 0];
}
let upper = Number.MAX_VALUE, lower = 0;
for (const item of active) {
const element = container.querySelector(`a[href="#${item}"]`);
if (!element)
continue;
const styles = getComputedStyle(element);
upper = Math.min(upper, element.offsetTop + parseFloat(styles.paddingTop));
lower = Math.max(lower, element.offsetTop +
element.clientHeight -
parseFloat(styles.paddingBottom));
}
return [upper, lower - upper];
}
function update(element, info) {
element.style.setProperty('--fd-top', `${info[0]}px`);
element.style.setProperty('--fd-height', `${info[1]}px`);
}
export function TocThumb({ containerRef, ...props }) {
const active = Primitive.useActiveAnchors();
const thumbRef = useRef(null);
const onResize = useEffectEvent(() => {
if (!containerRef.current || !thumbRef.current)
return;
update(thumbRef.current, calc(containerRef.current, active));
});
useEffect(() => {
if (!containerRef.current)
return;
const container = containerRef.current;
onResize();
const observer = new ResizeObserver(onResize);
observer.observe(container);
return () => {
observer.disconnect();
};
}, [containerRef, onResize]);
useOnChange(active, () => {
if (!containerRef.current || !thumbRef.current)
return;
update(thumbRef.current, calc(containerRef.current, active));
});
return _jsx("div", { ref: thumbRef, role: "none", ...props });
}