-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathuseResizeObserver.ts
More file actions
89 lines (76 loc) · 2.75 KB
/
useResizeObserver.ts
File metadata and controls
89 lines (76 loc) · 2.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import * as React from 'react';
import type { OnResize, SizeInfo } from '.';
import { observe, unobserve } from './utils/observerUtil';
import { useEvent } from '@rc-component/util';
export default function useResizeObserver(
enabled: boolean,
getTarget: HTMLElement | (() => HTMLElement),
onDelayResize?: OnResize,
onSyncResize?: OnResize,
) {
// ============================= Size =============================
const sizeRef = React.useRef<SizeInfo>({
width: -1,
height: -1,
offsetWidth: -1,
offsetHeight: -1,
});
// =========================== Observe ============================
// Handler
const onInternalResize = useEvent((target: HTMLElement) => {
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,
]);
}