UNPKG

@awsui/components-react

Version:

On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en

74 lines 3.52 kB
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { useCallback, useEffect, useState } from 'react'; const isBrowser = typeof window !== 'undefined'; /** * Hook to implement scroll-spy functionality. * * @param hrefs An array of href strings that correspond to the IDs of the target elements on the page. * The hrefs should be sorted in the order they appear on the page for accurate scroll-spy behavior. * @param scrollSpyOffset The number of pixels to offset from the top of the document when activating anchors. * Useful for accommodating fixed or sticky headers. * @param activeHref The currently active href. If provided, the hook will operate in a controlled manner, * and the scroll-spy logic will be disabled. * * @returns {string | undefined} - The href of the currently active element as per scroll position, or undefined if none is active. */ export default function useScrollSpy({ hrefs, scrollSpyOffset, activeHref, }) { const [currentHref, setCurrentHref] = useState(activeHref); const [lastAnchorExists, setLastAnchorExists] = useState(false); useEffect(() => { setCurrentHref(activeHref); }, [activeHref]); useEffect(() => { var _a; setLastAnchorExists(isBrowser && !!document.getElementById((_a = hrefs[hrefs.length - 1]) === null || _a === void 0 ? void 0 : _a.slice(1))); }, [hrefs]); // Get the bounding rectangle of an element by href const getRectByHref = useCallback((href) => { var _a; return (_a = document.getElementById(href.slice(1))) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect(); }, []); // Check if we're scrolled to the bottom of the page const isPageBottom = useCallback(() => { return lastAnchorExists && Math.ceil(window.scrollY) >= Math.floor(document.body.scrollHeight - window.innerHeight); }, [lastAnchorExists]); // Find the first href for which the element is within the viewport const findHrefInView = useCallback(() => { return hrefs.find(href => { const rect = getRectByHref(href); return rect && rect.bottom <= window.innerHeight && rect.top >= scrollSpyOffset; }); }, [getRectByHref, scrollSpyOffset, hrefs]); // Find the last href where its element is above or within the viewport const findLastHrefInView = useCallback(() => { return [...hrefs].reverse().find(href => { const rect = getRectByHref(href); return rect && rect.bottom <= window.innerHeight; }); }, [getRectByHref, hrefs]); // Scroll event handler const handleScroll = useCallback(() => { if (activeHref || !isBrowser) { return; } const { scrollY } = window; if (document.body.scrollHeight > window.innerHeight && isPageBottom()) { setCurrentHref(hrefs[hrefs.length - 1]); } else { setCurrentHref(findHrefInView() || (scrollY > 0 ? findLastHrefInView() : undefined)); } }, [activeHref, isPageBottom, findHrefInView, findLastHrefInView, hrefs]); useEffect(() => { if (isBrowser) { handleScroll(); window.addEventListener('scroll', handleScroll, { passive: true }); return () => { window.removeEventListener('scroll', handleScroll); }; } }, [handleScroll]); return currentHref; } //# sourceMappingURL=use-scroll-spy.js.map