mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-27 21:20:58 +00:00
93 lines
2.3 KiB
JavaScript
93 lines
2.3 KiB
JavaScript
'use client';
|
|
import { useState, useRef, useEffect } from 'react';
|
|
import { randomId } from '../utils/random-id/random-id.mjs';
|
|
|
|
function getHeadingsData(headings, getDepth, getValue) {
|
|
const result = [];
|
|
for (let i = 0; i < headings.length; i += 1) {
|
|
const heading = headings[i];
|
|
result.push({
|
|
depth: getDepth(heading),
|
|
value: getValue(heading),
|
|
id: heading.id || randomId(),
|
|
getNode: () => heading.id ? document.getElementById(heading.id) : heading
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
function getActiveElement(rects, offset = 0) {
|
|
if (rects.length === 0) {
|
|
return -1;
|
|
}
|
|
const closest = rects.reduce(
|
|
(acc, item, index) => {
|
|
if (Math.abs(acc.position - offset) < Math.abs(item.y - offset)) {
|
|
return acc;
|
|
}
|
|
return {
|
|
index,
|
|
position: item.y
|
|
};
|
|
},
|
|
{ index: 0, position: rects[0].y }
|
|
);
|
|
return closest.index;
|
|
}
|
|
function getDefaultDepth(element) {
|
|
return Number(element.tagName[1]);
|
|
}
|
|
function getDefaultValue(element) {
|
|
return element.textContent || "";
|
|
}
|
|
function useScrollSpy({
|
|
selector = "h1, h2, h3, h4, h5, h6",
|
|
getDepth = getDefaultDepth,
|
|
getValue = getDefaultValue,
|
|
offset = 0,
|
|
scrollHost
|
|
} = {}) {
|
|
const [active, setActive] = useState(-1);
|
|
const [initialized, setInitialized] = useState(false);
|
|
const [data, setData] = useState([]);
|
|
const headingsRef = useRef([]);
|
|
const handleScroll = () => {
|
|
setActive(
|
|
getActiveElement(
|
|
headingsRef.current.map((d) => d.getNode().getBoundingClientRect()),
|
|
offset
|
|
)
|
|
);
|
|
};
|
|
const initialize = () => {
|
|
const headings = getHeadingsData(
|
|
Array.from(document.querySelectorAll(selector)),
|
|
getDepth,
|
|
getValue
|
|
);
|
|
headingsRef.current = headings;
|
|
setInitialized(true);
|
|
setData(headings);
|
|
setActive(
|
|
getActiveElement(
|
|
headings.map((d) => d.getNode().getBoundingClientRect()),
|
|
offset
|
|
)
|
|
);
|
|
};
|
|
useEffect(() => {
|
|
initialize();
|
|
const _scrollHost = scrollHost || window;
|
|
_scrollHost.addEventListener("scroll", handleScroll);
|
|
return () => _scrollHost.removeEventListener("scroll", handleScroll);
|
|
}, [scrollHost]);
|
|
return {
|
|
reinitialize: initialize,
|
|
active,
|
|
initialized,
|
|
data
|
|
};
|
|
}
|
|
|
|
export { useScrollSpy };
|
|
//# sourceMappingURL=use-scroll-spy.mjs.map
|