nonebot2/website/src/libs/resize.ts

68 lines
1.6 KiB
TypeScript
Raw Normal View History

2021-12-30 18:06:20 +00:00
import { useLayoutEffect, useRef } from "react";
import ResizeObserver from "resize-observer-polyfill";
export function useResizeNotifier(
element: HTMLElement | undefined,
callback: () => void
) {
const callBackRef = useRef(callback);
useLayoutEffect(() => {
callBackRef.current = callback;
}, [callback]);
useLayoutEffect(() => {
if (!element) return;
const resizeObserver = new ResizeObserver(
withResizeLoopDetection(() => {
callBackRef.current!();
})
);
resizeObserver.observe(element);
return () => {
resizeObserver.disconnect();
};
}, [element]);
}
function withResizeLoopDetection(callback: () => void) {
return (entries: ResizeObserverEntry[], resizeObserver: ResizeObserver) => {
const elements = entries.map((entry) => entry.target);
const rectsBefore = elements.map((element) =>
element.getBoundingClientRect()
);
callback();
const rectsAfter = elements.map((element) =>
element.getBoundingClientRect()
);
const changedElements = elements.filter(
(_, i) => !areRectSizesEqual(rectsBefore[i], rectsAfter[i])
);
changedElements.forEach((element) =>
unobserveUntilNextFrame(element, resizeObserver)
);
};
}
function unobserveUntilNextFrame(
element: Element,
resizeObserver: ResizeObserver
) {
resizeObserver.unobserve(element);
requestAnimationFrame(() => {
resizeObserver.observe(element);
});
}
function areRectSizesEqual(rect1: DOMRect, rect2: DOMRect) {
return rect1.width === rect2.width && rect1.height === rect2.height;
}