UNPKG

@redocly/theme

Version:

Shared UI components lib

123 lines 6.01 kB
"use strict"; 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