UNPKG

@acusti/use-bounding-client-rect

Version:

React hook that returns the boundingClientRect for an element and triggers an update when those dimensions change

167 lines (166 loc) 4.49 kB
import { c } from "react/compiler-runtime"; import * as React from "react"; const { useEffect, useState } = React; const noop = () => { }; const RESIZE_OBSERVER_STUB = { disconnect: noop, observe: noop, unobserve: noop }; const EMPTY_RECT = Object.freeze({ bottom: void 0, left: void 0, right: void 0, top: void 0 }); const EMPTY_REFS = Object.freeze({ boundingClientRect: EMPTY_RECT, maybeCleanupElement: noop, maybeCleanupTimer: null, renderTimeSetters: /* @__PURE__ */ new Set(), retryCount: 0, scheduleUpdate: noop, updateBoundingClientRect: noop, updaterFrameID: null }); const MINUTE = 60 * 1e3; const refsByElement = /* @__PURE__ */ new WeakMap(); let resizeObserver = RESIZE_OBSERVER_STUB; if (typeof ResizeObserver === "function") { resizeObserver = new ResizeObserver((entries, observer) => { for (const entry of entries) { const element = entry.target; const refs = refsByElement.get(element); if (!refs) { observer.unobserve(element); return; } refs.scheduleUpdate(); } }); } const initializeUpdateHandlers = (element) => { const refs = refsByElement.get(element); if (!refs) return; if (refs.scheduleUpdate != null && refs.scheduleUpdate !== noop) return; refs.updateBoundingClientRect = () => { refs.updaterFrameID = null; const rect = element.getBoundingClientRect(); if (!rect.height && !rect.width) { if (refs.retryCount < 10) { refs.retryCount++; refs.updaterFrameID = requestAnimationFrame(refs.updateBoundingClientRect); } return; } if (refs.retryCount) { refs.retryCount = 0; } if (refs.boundingClientRect.bottom === rect.bottom && refs.boundingClientRect.left === rect.left && refs.boundingClientRect.right === rect.right && refs.boundingClientRect.top === rect.top) { return; } refs.boundingClientRect = { bottom: rect.bottom, left: rect.left, right: rect.right, top: rect.top }; const renderTime = typeof Date.now === "function" ? Date.now() : 0; for (const setRenderTime of refs.renderTimeSetters) { setRenderTime(renderTime); } }; refs.scheduleUpdate = () => { if (refs.updaterFrameID != null) return; refs.updaterFrameID = requestAnimationFrame(refs.updateBoundingClientRect); }; refs.maybeCleanupElement = () => { if (refs.maybeCleanupTimer != null) { clearTimeout(refs.maybeCleanupTimer); refs.maybeCleanupTimer = null; } if (refs.renderTimeSetters.size && element.closest("html")) { refs.maybeCleanupTimer = setTimeout(refs.maybeCleanupElement, MINUTE); return; } refsByElement.delete(element); resizeObserver.unobserve(element); }; refs.maybeCleanupTimer = setTimeout(refs.maybeCleanupElement, MINUTE); }; const cleanupHookInstance = (element, setRenderTime) => { const refs = refsByElement.get(element); if (!refs) return; refs.renderTimeSetters.delete(setRenderTime); refs.maybeCleanupElement(); }; const useBoundingClientRect = (element) => { const $ = c(9); const [, setRenderTime] = useState(0); let isInitializing = false; let t0; if ($[0] !== element) { t0 = (element && refsByElement.get(element)) ?? null; $[0] = element; $[1] = t0; } else { t0 = $[1]; } let refs = t0; if (element && !refs) { isInitializing = true; if ($[2] !== element) { refs = { ...EMPTY_REFS, renderTimeSetters: /* @__PURE__ */ new Set() }; refsByElement.set(element, refs); $[2] = element; $[3] = refs; } else { refs = $[3]; } initializeUpdateHandlers(element); resizeObserver.observe(element); } if (refs) { refs.renderTimeSetters.add(setRenderTime); if (isInitializing) { refs.updateBoundingClientRect(); } } let t1; if ($[4] !== element || $[5] !== setRenderTime) { t1 = () => { if (!element) { return noop; } return () => { cleanupHookInstance(element, setRenderTime); }; }; $[4] = element; $[5] = setRenderTime; $[6] = t1; } else { t1 = $[6]; } let t2; if ($[7] !== element) { t2 = [element]; $[7] = element; $[8] = t2; } else { t2 = $[8]; } useEffect(t1, t2); return (refs == null ? void 0 : refs.boundingClientRect) ?? EMPTY_RECT; }; export { useBoundingClientRect as default }; //# sourceMappingURL=useBoundingClientRect.js.map