@redocly/theme
Version:
Shared UI components lib
79 lines • 3.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.useActiveHeading = useActiveHeading;
const react_1 = require("react");
const react_router_dom_1 = require("react-router-dom");
function useActiveHeading(contentElement, displayedHeaders) {
const [heading, setHeading] = (0, react_1.useState)(displayedHeaders.length > 1 ? displayedHeaders[0] : undefined);
const [headingElements, setHeadingElements] = (0, react_1.useState)([]);
const headingElementsRef = (0, react_1.useRef)({});
const location = (0, react_router_dom_1.useLocation)();
const getVisibleHeadings = () => {
const visibleHeadings = [];
for (const key in headingElementsRef.current) {
const headingElement = headingElementsRef.current[key];
if (headingElement.isIntersecting) {
visibleHeadings.push(headingElement);
}
}
return visibleHeadings;
};
const getIndexFromId = (0, react_1.useCallback)((id) => {
return headingElements.findIndex((item) => item.id === id);
}, [headingElements]);
const findHeaders = (allContent) => {
const allHeaders = allContent.querySelectorAll('.heading-anchor');
return Array.from(allHeaders);
};
const intersectionCallback = (0, react_1.useCallback)((headings) => {
var _a;
headingElementsRef.current = headings.reduce((map, headingElement) => {
map[headingElement.target.id] = headingElement;
return map;
}, headingElementsRef.current);
const totalHeight = window.scrollY + window.innerHeight;
// handle bottom of the page
if (totalHeight >= document.body.scrollHeight) {
const newHeading = ((_a = headingElements[(headingElements === null || headingElements === void 0 ? void 0 : headingElements.length) - 1]) === null || _a === void 0 ? void 0 : _a.id) || undefined;
setHeading(newHeading);
return;
}
const visibleHeadings = getVisibleHeadings();
if (!visibleHeadings.length) {
return;
}
if (visibleHeadings.length === 1) {
setHeading(visibleHeadings[0].target.id);
return;
}
visibleHeadings.sort((a, b) => {
return getIndexFromId(a.target.id) - getIndexFromId(b.target.id);
});
setHeading(visibleHeadings[0].target.id);
}, [getIndexFromId, headingElements]);
(0, react_1.useEffect)(() => {
if (!contentElement) {
return;
}
setHeadingElements(findHeaders(contentElement));
}, [contentElement, location]);
(0, react_1.useEffect)(() => {
if (!(headingElements === null || headingElements === void 0 ? void 0 : headingElements.length)) {
return;
}
headingElementsRef.current = {};
// Bottom rootMargin -30% changes part of the view where IntersectionObserver starts to detect headers
const observer = new IntersectionObserver(intersectionCallback, {
rootMargin: '0px 0px -30% 0px',
threshold: 1,
});
headingElements === null || headingElements === void 0 ? void 0 : headingElements.forEach((element) => {
if (displayedHeaders.includes(element.id)) {
observer.observe(element);
}
});
return () => observer.disconnect();
}, [headingElements, displayedHeaders, intersectionCallback]);
return heading;
}
//# sourceMappingURL=use-active-heading.js.map