UNPKG

@tabula/use-size

Version:

Watches the DOM element's size with ResizeObserver

105 lines (101 loc) 2.6 kB
// src/useSize.ts import { useCallback, useLayoutEffect, useState } from "react"; // src/observe.ts var allListeners = /* @__PURE__ */ new WeakMap(); var isTicking = false; var queue = /* @__PURE__ */ new Map(); function flushQueue() { for (const [target, rect] of queue.entries()) { const listeners = allListeners.get(target); if (listeners == null) { continue; } for (const listener of listeners) { listener(rect); } } queue.clear(); isTicking = false; } function onResize(entries) { for (const entry of entries) { queue.set(entry.target, entry.contentRect); } if (!isTicking) { requestAnimationFrame(flushQueue); } isTicking = true; } var observer = null; function getObserver() { observer ??= new ResizeObserver(onResize); return observer; } function unobserve(target, listener) { const listeners = allListeners.get(target) ?? []; if (listeners.length === 1) { getObserver().unobserve(target); allListeners.delete(target); return; } const nextListeners = listeners.filter((it) => it === listener); allListeners.set(target, nextListeners); } function observe(target, listener) { getObserver().observe(target); const listeners = allListeners.get(target) ?? []; listeners.push(listener); allListeners.set(target, listeners); return () => { unobserve(target, listener); }; } // src/size.ts function rectToSize({ height, width }) { return { height: Math.round(height), width: Math.round(width) }; } function areEqual(left, right) { return left.height === right.height && left.width === right.width; } function updateSizeFromRect(current, rect) { const next = rectToSize(rect); return areEqual(current, next) ? current : next; } // src/useSize.ts var defaultSize = { width: 0, height: 0 }; function useSize(initialSize = defaultSize) { const [target, setTarget] = useState(null); const [size, setSize] = useState(initialSize); const handleRect = useCallback((next) => { setSize((current) => updateSizeFromRect(current, next)); }, []); useLayoutEffect(() => { if (target == null) { return; } handleRect(target.getBoundingClientRect()); let didUnsubscribe = false; const handleResize = (rect) => { if (didUnsubscribe) { return; } handleRect(rect); }; const unsubscribe = observe(target, handleResize); return () => { didUnsubscribe = true; unsubscribe(); }; }, [target, handleRect]); return [setTarget, size, target]; } export { useSize }; //# sourceMappingURL=index.js.map