@redocly/theme
Version:
Shared UI components lib
123 lines • 6.01 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.useCodeWalkthroughSteps = useCodeWalkthroughSteps;
const react_1 = require("react");
const react_router_dom_1 = require("react-router-dom");
const utils_1 = require("../../../core/utils");
const constants_1 = require("../../../core/constants");
function useCodeWalkthroughSteps(steps, enableDeepLink) {
const location = (0, react_router_dom_1.useLocation)();
const navigate = (0, react_router_dom_1.useNavigate)();
const searchParams = (0, react_1.useMemo)(() => new URLSearchParams(location.search), [location.search]);
const observerRef = (0, react_1.useRef)(null);
const filtersElementRef = (0, react_1.useRef)(null);
const lockObserver = (0, react_1.useRef)(false);
// Track observed elements in case new observer needs to be created
const observedElementsRef = (0, react_1.useRef)(new Set());
const [activeStep, setActiveStep] = (0, react_1.useState)(enableDeepLink ? searchParams.get(constants_1.ACTIVE_STEP_QUERY_PARAM) : null);
// eslint-disable-next-line react-hooks/exhaustive-deps
const _steps = (0, react_1.useMemo)(() => steps, [JSON.stringify(steps)]);
const register = (0, react_1.useCallback)((element) => {
// for some reason, the observer is not ready immediately
setTimeout(() => {
if (observerRef.current) {
const stepKey = Number(element.dataset.stepKey);
if (Number.isInteger(stepKey) && stepKey >= 0 && _steps[stepKey]) {
_steps[stepKey].compRef = element;
}
observerRef.current.observe(element);
observedElementsRef.current.add(element);
}
}, 10);
}, [_steps]);
const unregister = (0, react_1.useCallback)((element) => {
if (observerRef.current) {
const stepKey = Number(element.dataset.stepKey);
if (Number.isInteger(stepKey) && stepKey >= 0 && _steps[stepKey]) {
_steps[stepKey].compRef = undefined;
}
observerRef.current.unobserve(element);
observedElementsRef.current.delete(element);
}
}, [_steps]);
const observerCallback = (0, react_1.useCallback)((entries) => {
var _a, _b, _c;
if (lockObserver.current) {
return;
}
const renderedSteps = _steps.filter((step) => Boolean(step.compRef));
if (renderedSteps.length < 2) {
setActiveStep(((_a = renderedSteps[0]) === null || _a === void 0 ? void 0 : _a.id) || null);
return;
}
for (const entry of entries) {
const stepKey = Number((_c = (_b = entry.target) === null || _b === void 0 ? void 0 : _b.dataset) === null || _c === void 0 ? void 0 : _c.stepKey);
if (!Number.isInteger(stepKey) || stepKey < 0) {
continue;
}
const { intersectionRatio, boundingClientRect, rootBounds, isIntersecting } = entry;
const step = _steps[stepKey];
const stepIndex = renderedSteps.findIndex((renderedStep) => renderedStep.stepKey === step.stepKey);
const { next } = (0, utils_1.getAdjacentValues)(renderedSteps, stepIndex);
const intersectionAtTop = (rootBounds === null || rootBounds === void 0 ? void 0 : rootBounds.bottom) !== undefined && boundingClientRect.top < rootBounds.top;
const stepGoesIn = isIntersecting;
if (intersectionRatio > 0.8 &&
intersectionRatio < 1 &&
intersectionAtTop &&
activeStep === null) {
setActiveStep(step.id);
break;
}
if (intersectionRatio < 1 && intersectionRatio !== 0 && intersectionAtTop) {
let newStep = null;
if (stepGoesIn) {
newStep = step.id;
}
else if (next) {
newStep = next.id;
}
if (newStep !== activeStep) {
setActiveStep(newStep);
}
break;
}
}
}, [_steps, activeStep]);
(0, react_1.useEffect)(() => {
var _a, _b, _c;
const filtersElementHeight = ((_a = filtersElementRef.current) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
const navbarHeight = ((_b = document.querySelector('nav')) === null || _b === void 0 ? void 0 : _b.clientHeight) || 0;
const newObserver = new IntersectionObserver(observerCallback, {
threshold: [0.8, 0.85, 0.9, 0.95],
rootMargin: `-${filtersElementHeight + navbarHeight}px 0px 0px 0px`,
});
for (const observedElement of observedElementsRef.current) {
newObserver.observe(observedElement);
}
// Unobserve all from the old observer
(_c = observerRef.current) === null || _c === void 0 ? void 0 : _c.disconnect();
observerRef.current = newObserver;
}, [observerCallback]);
/**
* Update the URL search params with the current state of the filters and inputs
*/
(0, react_1.useEffect)(() => {
if (!enableDeepLink) {
return;
}
const newSearchParams = new URLSearchParams(Array.from(searchParams.entries()));
if (activeStep) {
newSearchParams.set(constants_1.ACTIVE_STEP_QUERY_PARAM, activeStep);
}
else {
newSearchParams.delete(constants_1.ACTIVE_STEP_QUERY_PARAM);
}
const newSearch = newSearchParams.toString();
if (newSearch === location.search.substring(1))
return;
navigate({ search: newSearch }, { replace: true });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeStep]);
return { register, unregister, lockObserver, filtersElementRef, activeStep, setActiveStep };
}
//# sourceMappingURL=use-code-walkthrough-steps.js.map