UNPKG

@intility/bifrost-react

Version:

React library for Intility's design system, Bifrost.

87 lines (86 loc) 3.41 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ "use client"; import { useEffect, useRef } from "react"; import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect.js"; // stolen from https://github.com/jaredLunde/react-hook/tree/master/packages/resize-observer // reworked slightly since it caused crashes when building static site for next.js // for some reason they think polyfilling should be the responsibility of the consumer for SSR/RSC // but we want bifrost to be ssr friendly out of the box https://github.com/jaredLunde/react-hook/issues/310 const useLatest = (current)=>{ const storedValue = useRef(current); useEffect(()=>{ storedValue.current = current; }); return storedValue; }; /** * A React hook that fires a callback whenever ResizeObserver detects a change to its size * * @param target A React ref created by `useRef()` or an HTML element * @param callback Invoked with a single `ResizeObserverEntry` any time * the `target` resizes */ function useResizeObserver(target, callback, options = {}) { const storedCallback = useLatest(callback); useIsomorphicLayoutEffect(()=>{ const resizeObserver = getResizeObserver(options.polyfill); let didUnsubscribe = false; const targetEl = target && "current" in target ? target.current : target; if (!targetEl) return; function cb(entry, observer) { if (didUnsubscribe) return; storedCallback.current(entry, observer); } resizeObserver.subscribe(targetEl, cb); return ()=>{ didUnsubscribe = true; resizeObserver.unsubscribe(targetEl, cb); }; }, [ target, storedCallback ]); } function createResizeObserver(polyfill) { let ticking = false; let allEntries = []; const callbacks = new Map(); const observer = new (polyfill || window.ResizeObserver)((entries, obs)=>{ allEntries = allEntries.concat(entries); if (!ticking) { window.requestAnimationFrame(()=>{ const triggered = new Set(); for(let i = 0; i < allEntries.length; i++){ if (triggered.has(allEntries[i].target)) continue; triggered.add(allEntries[i].target); const cbs = callbacks.get(allEntries[i].target); cbs?.forEach((cb)=>cb(allEntries[i], obs)); } allEntries = []; ticking = false; }); } ticking = true; }); return { observer, subscribe (target, callback) { observer.observe(target); const cbs = callbacks.get(target) ?? []; cbs.push(callback); callbacks.set(target, cbs); }, unsubscribe (target, callback) { const cbs = callbacks.get(target) ?? []; if (cbs.length === 1) { observer.unobserve(target); callbacks.delete(target); return; } const cbIndex = cbs.indexOf(callback); if (cbIndex !== -1) cbs.splice(cbIndex, 1); callbacks.set(target, cbs); } }; } let _resizeObserver; const getResizeObserver = (polyfill)=>!_resizeObserver ? _resizeObserver = createResizeObserver(polyfill) : _resizeObserver; export default useResizeObserver;