@etsoo/react
Version:
TypeScript ReactJs UI Independent Framework
93 lines (92 loc) • 3.34 kB
JavaScript
import { DomUtils } from "@etsoo/shared";
import React from "react";
import { useDelayedExecutor } from "./useDelayedExecutor";
/**
* Calculate element(s) dimensions
* @param elements Observed elments count
* @param updateCallback Update callback
* @param miliseconds Miliseconds to wait before update
* @param equalCallback Equall callback
*/
export function useDimensions(elements, updateCallback, miliseconds = 50, equalCallback = DomUtils.dimensionEqual) {
// State
const [state, setState] = React.useState({
count: 0,
indices: []
});
// Dimentions
const dimensions = React.useRef();
if (dimensions.current == null) {
// Init
const init = [];
for (let e = 0; e < elements; e++) {
init.push(((index) => {
return [
(instance) => {
if (instance != null) {
// Current element
const currentElement = init[index][1];
if (currentElement != null) {
// Same target, return
if (currentElement == instance)
return;
// Cancel observation
resizeObserver.unobserve(currentElement);
}
// Update element
init[index][1] = instance;
// Start observe
resizeObserver.observe(instance);
}
}
];
})(e));
}
dimensions.current = init;
}
const delayed = useDelayedExecutor(setState, miliseconds);
// Observer
const resizeObserver = new ResizeObserver((entries) => {
// Update Rect
const indices = [];
const init = dimensions.current;
entries.forEach((entry) => {
const index = init.findIndex((item) => item[1] === entry.target);
if (index !== -1) {
// Previous rect
const pre = init[index][2];
// New rect
const rect = entry.target.getBoundingClientRect();
// Check equal
if (!equalCallback(pre, rect)) {
// Update callback
if (updateCallback) {
// Return false means no further push
if (updateCallback(entry.target, rect) === false)
return;
}
// Update rect
init[index][2] = rect;
// Push for update
indices.push(index);
}
}
});
// Update state
if (indices.length > 0) {
// Count only for unique update
const update = { count: state.count + 1, indices };
delayed.call(undefined, update);
}
});
// Layout ready
React.useEffect(() => {
return () => {
// Clear the observer
resizeObserver.disconnect();
delayed.clear();
};
}, []);
// Return
return { dimensions: dimensions.current, state };
}