@tabula/use-size
Version:
Watches the DOM element's size with ResizeObserver
105 lines (101 loc) • 2.6 kB
JavaScript
// 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