UNPKG

react-hooks-global-scrollspy

Version:
95 lines (94 loc) 3.8 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; import { useEffect, useState, useRef, useCallback } from "react"; import { throttle } from "../utils/throttle"; /** * @param ScrollSpyParams Optional params * @param ScrollSpyParams.offsetPx Distance from Y coordinate of the base (px) * @param ScrollSpyParams.throttleMs Interval of update processing (ms) * @returns [ activeElement, actions ] - Two return values are returned as an array * @returns activeElement - DOM element activated based on scroll position (only one) * @returns actions - Actions are used to control registered DOM elements */ export var useScrollSpy = function (_a) { var _b = _a === void 0 ? {} : _a, _c = _b.offsetPx, offsetPx = _c === void 0 ? 0 : _c, _d = _b.throttleMs, throttleMs = _d === void 0 ? 50 : _d; var _e = useState(null), activeElement = _e[0], setActiveElement = _e[1]; var elements = useRef({}); var registerElement = useCallback(function (_a) { var _b; var key = _a.key, element = _a.element; var prevElements = elements.current; elements.current = __assign(__assign({}, prevElements), (_b = {}, _b[key] = element, _b)); }, []); var unregisterElement = useCallback(function (_a) { var key = _a.key; var deletedElements = __assign({}, elements.current); delete deletedElements[key]; elements.current = deletedElements; }, []); var resetElements = useCallback(function () { elements.current = {}; }, []); var updateActiveElement = useCallback(function () { var currentElements = elements.current; var elementKeys = Object.keys(currentElements); var activeKey = elementKeys.reduce(function (prevActive, key) { var elementElement = currentElements[key].current; if (!elementElement) { return prevActive; } var elementPositionTop = elementElement.getBoundingClientRect().top; var isPassed = elementPositionTop + offsetPx < 0; if (!isPassed) { return prevActive; } if (!prevActive.key) { return { key: key, elementPositionTop: elementPositionTop, }; } var _a = prevActive.elementPositionTop, prevElementPositionTop = _a === void 0 ? 0 : _a; var isHigherThanPrev = elementPositionTop >= prevElementPositionTop; if (isHigherThanPrev) { return { key: key, elementPositionTop: elementPositionTop, }; } else { return prevActive; } }, {}).key; if (!activeKey) { setActiveElement(null); return; } setActiveElement({ key: activeKey, value: currentElements[activeKey], }); }, [offsetPx]); useEffect(function () { var InvokeUpdate = throttle(updateActiveElement, throttleMs); window.addEventListener("scroll", InvokeUpdate); return function () { window.removeEventListener("scroll", InvokeUpdate); }; }, [throttleMs, updateActiveElement]); var actions = { registerElement: registerElement, unregisterElement: unregisterElement, resetElements: resetElements, }; return [activeElement, actions]; };