81 lines
2.6 KiB
JavaScript
81 lines
2.6 KiB
JavaScript
import * as React from 'react';
|
|
import { observe, unobserve } from "./utils/observerUtil";
|
|
import { useEvent } from '@rc-component/util';
|
|
export default function useResizeObserver(enabled, getTarget, onDelayResize, onSyncResize) {
|
|
// ============================= Size =============================
|
|
const sizeRef = React.useRef({
|
|
width: -1,
|
|
height: -1,
|
|
offsetWidth: -1,
|
|
offsetHeight: -1
|
|
});
|
|
|
|
// =========================== Observe ============================
|
|
|
|
// Handler
|
|
const onInternalResize = useEvent(target => {
|
|
const {
|
|
width,
|
|
height
|
|
} = target.getBoundingClientRect();
|
|
const {
|
|
offsetWidth,
|
|
offsetHeight
|
|
} = target;
|
|
|
|
/**
|
|
* Resize observer trigger when content size changed.
|
|
* In most case we just care about element size,
|
|
* let's use `boundary` instead of `contentRect` here to avoid shaking.
|
|
*/
|
|
const fixedWidth = Math.floor(width);
|
|
const fixedHeight = Math.floor(height);
|
|
if (sizeRef.current.width !== fixedWidth || sizeRef.current.height !== fixedHeight || sizeRef.current.offsetWidth !== offsetWidth || sizeRef.current.offsetHeight !== offsetHeight) {
|
|
const size = {
|
|
width: fixedWidth,
|
|
height: fixedHeight,
|
|
offsetWidth,
|
|
offsetHeight
|
|
};
|
|
sizeRef.current = size;
|
|
|
|
// IE is strange, right?
|
|
const mergedOffsetWidth = offsetWidth === Math.round(width) ? width : offsetWidth;
|
|
const mergedOffsetHeight = offsetHeight === Math.round(height) ? height : offsetHeight;
|
|
const sizeInfo = {
|
|
...size,
|
|
offsetWidth: mergedOffsetWidth,
|
|
offsetHeight: mergedOffsetHeight
|
|
};
|
|
|
|
// Call the callback immediately, let the caller decide whether to defer
|
|
// onResize(sizeInfo, target);
|
|
onSyncResize?.(sizeInfo, target);
|
|
|
|
// defer the callback but not defer to next frame
|
|
Promise.resolve().then(() => {
|
|
onDelayResize?.(sizeInfo, target);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Dynamic observe
|
|
const isFuncTarget = typeof getTarget === 'function';
|
|
const funcTargetIdRef = React.useRef(0);
|
|
React.useEffect(() => {
|
|
const target = isFuncTarget ? getTarget() : getTarget;
|
|
if (target && enabled) {
|
|
observe(target, onInternalResize);
|
|
} else if (enabled && isFuncTarget) {
|
|
funcTargetIdRef.current += 1;
|
|
}
|
|
return () => {
|
|
if (target) {
|
|
unobserve(target, onInternalResize);
|
|
}
|
|
};
|
|
}, [enabled,
|
|
// If function target resolves after a parent render, the bumped ref value
|
|
// lets the next render re-run this effect without watching the function identity.
|
|
isFuncTarget ? funcTargetIdRef.current : getTarget]);
|
|
} |