@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
JavaScript
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