forked from toggle-corp/re-map
-
Notifications
You must be signed in to change notification settings - Fork 0
/
useDimension.tsx
102 lines (89 loc) · 2.97 KB
/
useDimension.tsx
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
90
91
92
93
94
95
96
97
98
99
100
101
102
import { useRef, useState, useEffect } from 'react';
import ResizeObserver from 'resize-observer-polyfill';
import { randomString } from '@togglecorp/fujs';
interface ResizeHandler {
(rect: ClientRect): void;
}
interface ResizeHandlers {
[key: string]: ResizeHandler;
}
const resizeHandlers: ResizeHandlers = {};
const handleResize: ResizeObserverCallback = (entries) => {
entries.forEach((entry) => {
const element = entry.target;
const key = element.dataset.resizeHandlerKey;
if (key && resizeHandlers[key]) {
const { contentRect: rect } = entry;
resizeHandlers[key](rect);
}
});
};
const observer = new ResizeObserver(handleResize);
const addResizeHandler = (ref: React.RefObject<HTMLDivElement>, callback: ResizeHandler) => {
const key = randomString(16);
if (!ref.current) {
return;
}
// eslint-disable-next-line no-param-reassign
ref.current.dataset.resizeHandlerKey = key;
resizeHandlers[key] = callback;
observer.observe(ref.current);
};
const removeResizeHandler = (ref: React.RefObject<HTMLDivElement>) => {
if (!ref.current) {
return;
}
observer.unobserve(ref.current);
const key = ref.current.dataset.resizeHandlerKey;
if (!key || !resizeHandlers[key]) {
return;
}
delete resizeHandlers[key];
};
const useDimension = (
targetRef: React.RefObject<HTMLDivElement> | undefined,
debounceDuration = 200,
) => {
const timeoutRef = useRef<number | undefined>();
const [rect, setRect] = useState<ClientRect | undefined>(undefined);
useEffect(
() => {
if (!targetRef) {
return () => {
// noop
};
}
addResizeHandler(
targetRef,
(r: ClientRect) => {
window.clearTimeout(timeoutRef.current);
timeoutRef.current = window.setTimeout(
() => {
const different = (
!rect
// || r.x !== rect.x
// || r.y !== rect.y
|| r.width !== rect.width
|| r.height !== rect.height
|| r.top !== rect.top
|| r.right !== rect.right
|| r.bottom !== rect.bottom
|| r.left !== rect.left
);
if (different) {
setRect(r);
}
},
debounceDuration,
);
},
);
return () => {
window.clearTimeout(timeoutRef.current);
removeResizeHandler(targetRef);
};
},
);
return rect;
};
export default useDimension;