react-resize-detector
Version:
React resize detector
106 lines (103 loc) • 4.19 kB
JavaScript
import * as React from 'react';
import debounce from 'lodash/debounce.js';
import throttle from 'lodash/throttle.js';
/**
* Wraps the resize callback with a lodash debounce / throttle based on the refresh mode
*/
const patchResizeCallback = (resizeCallback, refreshMode, refreshRate, refreshOptions) => {
switch (refreshMode) {
case 'debounce':
return debounce(resizeCallback, refreshRate, refreshOptions);
case 'throttle':
return throttle(resizeCallback, refreshRate, refreshOptions);
default:
return resizeCallback;
}
};
/**
* A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a
* prop or avoid re-executing effects when passed as a dependency
*/
const useCallbackRef =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(callback) => {
const callbackRef = React.useRef(callback);
React.useEffect(() => {
callbackRef.current = callback;
});
return React.useMemo(() => ((...args) => { var _a; return (_a = callbackRef.current) === null || _a === void 0 ? void 0 : _a.call(callbackRef, ...args); }), []);
};
/** `useRef` hook doesn't handle conditional rendering or dynamic ref changes.
* This hook creates a proxy that ensures that `refElement` is updated whenever the ref is changed. */
const useRefProxy =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(targetRef) => {
// we are going to use this ref to store the last element that was passed to the hook
const [refElement, setRefElement] = React.useState((targetRef === null || targetRef === void 0 ? void 0 : targetRef.current) || null);
// if targetRef is passed, we need to update the refElement
// we have to use setTimeout because ref get assigned after the hook is called
// in the future releases we are going to remove targetRef and force users to use ref returned by the hook
if (targetRef) {
setTimeout(() => {
if (targetRef.current !== refElement) {
setRefElement(targetRef.current);
}
}, 0);
}
// this is a memo that will be called every time the ref is changed
// This proxy will properly call setState either when the ref is called as a function or when `.current` is set
// we call setState inside to trigger rerender
const refProxy = React.useMemo(() => new Proxy((node) => {
if (node !== refElement) {
setRefElement(node);
}
}, {
get(target, prop) {
if (prop === 'current') {
return refElement;
}
return target[prop];
},
set(target, prop, value) {
if (prop === 'current') {
setRefElement(value);
}
else {
target[prop] = value;
}
return true;
},
}), [refElement]);
return { refProxy, refElement, setRefElement };
};
/** Calculates the dimensions of the element based on the current box model.
* @see https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model
*/
const getDimensions = (entry, box) => {
// Value Border Padding Inner Content
// ---------------------------------------------------
// 'border-box' Yes Yes Yes
// 'content-box' No No Yes
// undefined No No? Yes
var _a, _b;
const borderBox = (_a = entry.borderBoxSize) === null || _a === void 0 ? void 0 : _a[0];
const contentBox = (_b = entry.contentBoxSize) === null || _b === void 0 ? void 0 : _b[0];
if (box === 'border-box' && borderBox) {
return {
width: borderBox.inlineSize,
height: borderBox.blockSize,
};
}
if (box === 'content-box' && contentBox) {
return {
width: contentBox.inlineSize,
height: contentBox.blockSize,
};
}
return {
width: entry.contentRect.width,
height: entry.contentRect.height,
};
};
export { getDimensions, patchResizeCallback, useCallbackRef, useRefProxy };
//# sourceMappingURL=utils.js.map