@koine/react 2.0.0-beta.73 → 2.0.0-beta.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FaviconTags.d.ts +9 -0
- package/FaviconTags.js +5 -0
- package/Meta.d.ts +5 -0
- package/Meta.js +5 -0
- package/NoJs.d.ts +3 -0
- package/NoJs.js +7 -0
- package/Polymorphic.d.ts +26 -0
- package/Polymorphic.js +1 -0
- package/classed.d.ts +8 -0
- package/classed.js +41 -0
- package/createUseMediaQueryWidth.d.ts +6 -0
- package/createUseMediaQueryWidth.js +38 -0
- package/extendComponent.d.ts +16 -0
- package/extendComponent.js +9 -0
- package/index.cjs.js +33 -23
- package/index.d.ts +28 -3
- package/index.esm.js +33 -26
- package/index.js +26 -3
- package/mergeRefs.d.ts +2 -0
- package/mergeRefs.js +13 -0
- package/package.json +245 -5
- package/useAsyncFn.d.ts +24 -0
- package/useAsyncFn.js +26 -0
- package/useFirstMountState.d.ts +2 -0
- package/useFirstMountState.js +10 -0
- package/useFixedOffset.d.ts +2 -0
- package/useFixedOffset.js +42 -0
- package/useFocus.d.ts +2 -0
- package/useFocus.js +9 -0
- package/useInterval.d.ts +2 -0
- package/useInterval.js +20 -0
- package/useIsomorphicLayoutEffect.d.ts +3 -0
- package/useIsomorphicLayoutEffect.js +4 -0
- package/useKeyUp.d.ts +2 -0
- package/useKeyUp.js +16 -0
- package/useMeasure.d.ts +22 -0
- package/useMeasure.js +119 -0
- package/useMountedState.d.ts +2 -0
- package/useMountedState.js +13 -0
- package/useNavigateAway.d.ts +3 -0
- package/useNavigateAway.js +25 -0
- package/usePrevious.d.ts +2 -0
- package/usePrevious.js +9 -0
- package/usePreviousRef.d.ts +2 -0
- package/usePreviousRef.js +9 -0
- package/useReveal.d.ts +13 -0
- package/useReveal.js +42 -0
- package/useScrollPosition.d.ts +7 -0
- package/useScrollPosition.js +58 -0
- package/useScrollThreshold.d.ts +2 -0
- package/useScrollThreshold.js +26 -0
- package/useScrollTo.d.ts +2 -0
- package/useScrollTo.js +18 -0
- package/useSmoothScroll.d.ts +2 -0
- package/useSmoothScroll.js +32 -0
- package/useSpinDelay.d.ts +2 -0
- package/useSpinDelay.js +36 -0
- package/useTraceUpdate.d.ts +2 -0
- package/useTraceUpdate.js +17 -0
- package/useUpdateEffect.d.ts +3 -0
- package/useUpdateEffect.js +11 -0
- package/useWindowSize.d.ts +3 -0
- package/useWindowSize.js +20 -0
package/package.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"name": "@koine/react",
|
|
3
3
|
"sideEffects": false,
|
|
4
4
|
"dependencies": {
|
|
5
|
-
"@koine/dom": "2.0.0-beta.
|
|
6
|
-
"@koine/utils": "2.0.0-beta.
|
|
5
|
+
"@koine/dom": "2.0.0-beta.75",
|
|
6
|
+
"@koine/utils": "2.0.0-beta.75"
|
|
7
7
|
},
|
|
8
8
|
"peerDependencies": {
|
|
9
9
|
"@kuus/yup": "1.0.0-beta.7",
|
|
@@ -26,24 +26,264 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"exports": {
|
|
29
|
-
"./package.json": "./package.json",
|
|
30
29
|
".": {
|
|
31
30
|
"module": "./index.esm.js",
|
|
32
31
|
"import": "./index.cjs.mjs",
|
|
33
32
|
"default": "./index.cjs.js"
|
|
34
33
|
},
|
|
34
|
+
"./FaviconTags": {
|
|
35
|
+
"import": "./FaviconTags.js"
|
|
36
|
+
},
|
|
37
|
+
"./Meta": {
|
|
38
|
+
"import": "./Meta.js"
|
|
39
|
+
},
|
|
40
|
+
"./NoJs": {
|
|
41
|
+
"import": "./NoJs.js"
|
|
42
|
+
},
|
|
43
|
+
"./Polymorphic": {
|
|
44
|
+
"import": "./Polymorphic.js"
|
|
45
|
+
},
|
|
46
|
+
"./calendar.cjs.default": {
|
|
47
|
+
"import": "./calendar.cjs.default.js"
|
|
48
|
+
},
|
|
49
|
+
"./calendar.cjs": {
|
|
50
|
+
"import": "./calendar.cjs.js"
|
|
51
|
+
},
|
|
52
|
+
"./calendar.esm": {
|
|
53
|
+
"import": "./calendar.esm.js"
|
|
54
|
+
},
|
|
35
55
|
"./calendar": {
|
|
36
56
|
"module": "./calendar.esm.js",
|
|
37
57
|
"import": "./calendar.cjs.mjs",
|
|
38
58
|
"default": "./calendar.cjs.js"
|
|
39
59
|
},
|
|
60
|
+
"./calendar/CalendarDaygridCell": {
|
|
61
|
+
"import": "./calendar/CalendarDaygridCell.js"
|
|
62
|
+
},
|
|
63
|
+
"./calendar/CalendarDaygridNav": {
|
|
64
|
+
"import": "./calendar/CalendarDaygridNav.js"
|
|
65
|
+
},
|
|
66
|
+
"./calendar/CalendarDaygridTable": {
|
|
67
|
+
"import": "./calendar/CalendarDaygridTable.js"
|
|
68
|
+
},
|
|
69
|
+
"./calendar/CalendarLegend": {
|
|
70
|
+
"import": "./calendar/CalendarLegend.js"
|
|
71
|
+
},
|
|
72
|
+
"./calendar/calendar-api-google": {
|
|
73
|
+
"import": "./calendar/calendar-api-google.js"
|
|
74
|
+
},
|
|
75
|
+
"./calendar/types": {
|
|
76
|
+
"import": "./calendar/types.js"
|
|
77
|
+
},
|
|
78
|
+
"./calendar/useCalendar": {
|
|
79
|
+
"import": "./calendar/useCalendar.js"
|
|
80
|
+
},
|
|
81
|
+
"./calendar/useDateLocale": {
|
|
82
|
+
"import": "./calendar/useDateLocale.js"
|
|
83
|
+
},
|
|
84
|
+
"./calendar/utils": {
|
|
85
|
+
"import": "./calendar/utils.js"
|
|
86
|
+
},
|
|
87
|
+
"./classed": {
|
|
88
|
+
"import": "./classed.js"
|
|
89
|
+
},
|
|
90
|
+
"./components/FaviconTags": {
|
|
91
|
+
"import": "./components/FaviconTags.js"
|
|
92
|
+
},
|
|
93
|
+
"./components/Meta": {
|
|
94
|
+
"import": "./components/Meta.js"
|
|
95
|
+
},
|
|
96
|
+
"./components/NoJs": {
|
|
97
|
+
"import": "./components/NoJs.js"
|
|
98
|
+
},
|
|
99
|
+
"./createUseMediaQueryWidth": {
|
|
100
|
+
"import": "./createUseMediaQueryWidth.js"
|
|
101
|
+
},
|
|
102
|
+
"./extendComponent": {
|
|
103
|
+
"import": "./extendComponent.js"
|
|
104
|
+
},
|
|
105
|
+
"./forms.cjs.default": {
|
|
106
|
+
"import": "./forms.cjs.default.js"
|
|
107
|
+
},
|
|
108
|
+
"./forms.cjs": {
|
|
109
|
+
"import": "./forms.cjs.js"
|
|
110
|
+
},
|
|
111
|
+
"./forms.esm": {
|
|
112
|
+
"import": "./forms.esm.js"
|
|
113
|
+
},
|
|
40
114
|
"./forms": {
|
|
41
115
|
"module": "./forms.esm.js",
|
|
42
116
|
"import": "./forms.cjs.mjs",
|
|
43
117
|
"default": "./forms.cjs.js"
|
|
44
|
-
}
|
|
118
|
+
},
|
|
119
|
+
"./forms/antispam": {
|
|
120
|
+
"import": "./forms/antispam.js"
|
|
121
|
+
},
|
|
122
|
+
"./hooks": {
|
|
123
|
+
"import": "./hooks/index.js"
|
|
124
|
+
},
|
|
125
|
+
"./hooks/useAsyncFn": {
|
|
126
|
+
"import": "./hooks/useAsyncFn.js"
|
|
127
|
+
},
|
|
128
|
+
"./hooks/useFirstMountState": {
|
|
129
|
+
"import": "./hooks/useFirstMountState.js"
|
|
130
|
+
},
|
|
131
|
+
"./hooks/useFixedOffset": {
|
|
132
|
+
"import": "./hooks/useFixedOffset.js"
|
|
133
|
+
},
|
|
134
|
+
"./hooks/useFocus": {
|
|
135
|
+
"import": "./hooks/useFocus.js"
|
|
136
|
+
},
|
|
137
|
+
"./hooks/useInterval": {
|
|
138
|
+
"import": "./hooks/useInterval.js"
|
|
139
|
+
},
|
|
140
|
+
"./hooks/useIsomorphicLayoutEffect": {
|
|
141
|
+
"import": "./hooks/useIsomorphicLayoutEffect.js"
|
|
142
|
+
},
|
|
143
|
+
"./hooks/useKeyUp": {
|
|
144
|
+
"import": "./hooks/useKeyUp.js"
|
|
145
|
+
},
|
|
146
|
+
"./hooks/useMeasure": {
|
|
147
|
+
"import": "./hooks/useMeasure.js"
|
|
148
|
+
},
|
|
149
|
+
"./hooks/useMountedState": {
|
|
150
|
+
"import": "./hooks/useMountedState.js"
|
|
151
|
+
},
|
|
152
|
+
"./hooks/useNavigateAway": {
|
|
153
|
+
"import": "./hooks/useNavigateAway.js"
|
|
154
|
+
},
|
|
155
|
+
"./hooks/usePrevious": {
|
|
156
|
+
"import": "./hooks/usePrevious.js"
|
|
157
|
+
},
|
|
158
|
+
"./hooks/usePreviousRef": {
|
|
159
|
+
"import": "./hooks/usePreviousRef.js"
|
|
160
|
+
},
|
|
161
|
+
"./hooks/useReveal": {
|
|
162
|
+
"import": "./hooks/useReveal.js"
|
|
163
|
+
},
|
|
164
|
+
"./hooks/useScrollPosition": {
|
|
165
|
+
"import": "./hooks/useScrollPosition.js"
|
|
166
|
+
},
|
|
167
|
+
"./hooks/useScrollThreshold": {
|
|
168
|
+
"import": "./hooks/useScrollThreshold.js"
|
|
169
|
+
},
|
|
170
|
+
"./hooks/useScrollTo": {
|
|
171
|
+
"import": "./hooks/useScrollTo.js"
|
|
172
|
+
},
|
|
173
|
+
"./hooks/useSmoothScroll": {
|
|
174
|
+
"import": "./hooks/useSmoothScroll.js"
|
|
175
|
+
},
|
|
176
|
+
"./hooks/useSpinDelay": {
|
|
177
|
+
"import": "./hooks/useSpinDelay.js"
|
|
178
|
+
},
|
|
179
|
+
"./hooks/useTraceUpdate": {
|
|
180
|
+
"import": "./hooks/useTraceUpdate.js"
|
|
181
|
+
},
|
|
182
|
+
"./hooks/useUpdateEffect": {
|
|
183
|
+
"import": "./hooks/useUpdateEffect.js"
|
|
184
|
+
},
|
|
185
|
+
"./hooks/useWindowSize": {
|
|
186
|
+
"import": "./hooks/useWindowSize.js"
|
|
187
|
+
},
|
|
188
|
+
"./index.cjs.default": {
|
|
189
|
+
"import": "./index.cjs.default.js"
|
|
190
|
+
},
|
|
191
|
+
"./index.cjs": {
|
|
192
|
+
"import": "./index.cjs.js"
|
|
193
|
+
},
|
|
194
|
+
"./index.esm": {
|
|
195
|
+
"import": "./index.esm.js"
|
|
196
|
+
},
|
|
197
|
+
"./mergeRefs": {
|
|
198
|
+
"import": "./mergeRefs.js"
|
|
199
|
+
},
|
|
200
|
+
"./types": {
|
|
201
|
+
"import": "./types.js"
|
|
202
|
+
},
|
|
203
|
+
"./useAsyncFn": {
|
|
204
|
+
"import": "./useAsyncFn.js"
|
|
205
|
+
},
|
|
206
|
+
"./useFirstMountState": {
|
|
207
|
+
"import": "./useFirstMountState.js"
|
|
208
|
+
},
|
|
209
|
+
"./useFixedOffset": {
|
|
210
|
+
"import": "./useFixedOffset.js"
|
|
211
|
+
},
|
|
212
|
+
"./useFocus": {
|
|
213
|
+
"import": "./useFocus.js"
|
|
214
|
+
},
|
|
215
|
+
"./useInterval": {
|
|
216
|
+
"import": "./useInterval.js"
|
|
217
|
+
},
|
|
218
|
+
"./useIsomorphicLayoutEffect": {
|
|
219
|
+
"import": "./useIsomorphicLayoutEffect.js"
|
|
220
|
+
},
|
|
221
|
+
"./useKeyUp": {
|
|
222
|
+
"import": "./useKeyUp.js"
|
|
223
|
+
},
|
|
224
|
+
"./useMeasure": {
|
|
225
|
+
"import": "./useMeasure.js"
|
|
226
|
+
},
|
|
227
|
+
"./useMountedState": {
|
|
228
|
+
"import": "./useMountedState.js"
|
|
229
|
+
},
|
|
230
|
+
"./useNavigateAway": {
|
|
231
|
+
"import": "./useNavigateAway.js"
|
|
232
|
+
},
|
|
233
|
+
"./usePrevious": {
|
|
234
|
+
"import": "./usePrevious.js"
|
|
235
|
+
},
|
|
236
|
+
"./usePreviousRef": {
|
|
237
|
+
"import": "./usePreviousRef.js"
|
|
238
|
+
},
|
|
239
|
+
"./useReveal": {
|
|
240
|
+
"import": "./useReveal.js"
|
|
241
|
+
},
|
|
242
|
+
"./useScrollPosition": {
|
|
243
|
+
"import": "./useScrollPosition.js"
|
|
244
|
+
},
|
|
245
|
+
"./useScrollThreshold": {
|
|
246
|
+
"import": "./useScrollThreshold.js"
|
|
247
|
+
},
|
|
248
|
+
"./useScrollTo": {
|
|
249
|
+
"import": "./useScrollTo.js"
|
|
250
|
+
},
|
|
251
|
+
"./useSmoothScroll": {
|
|
252
|
+
"import": "./useSmoothScroll.js"
|
|
253
|
+
},
|
|
254
|
+
"./useSpinDelay": {
|
|
255
|
+
"import": "./useSpinDelay.js"
|
|
256
|
+
},
|
|
257
|
+
"./useTraceUpdate": {
|
|
258
|
+
"import": "./useTraceUpdate.js"
|
|
259
|
+
},
|
|
260
|
+
"./useUpdateEffect": {
|
|
261
|
+
"import": "./useUpdateEffect.js"
|
|
262
|
+
},
|
|
263
|
+
"./useWindowSize": {
|
|
264
|
+
"import": "./useWindowSize.js"
|
|
265
|
+
},
|
|
266
|
+
"./utils/Polymorphic": {
|
|
267
|
+
"import": "./utils/Polymorphic.js"
|
|
268
|
+
},
|
|
269
|
+
"./utils/classed": {
|
|
270
|
+
"import": "./utils/classed.js"
|
|
271
|
+
},
|
|
272
|
+
"./utils/createUseMediaQueryWidth": {
|
|
273
|
+
"import": "./utils/createUseMediaQueryWidth.js"
|
|
274
|
+
},
|
|
275
|
+
"./utils/extendComponent": {
|
|
276
|
+
"import": "./utils/extendComponent.js"
|
|
277
|
+
},
|
|
278
|
+
"./utils": {
|
|
279
|
+
"import": "./utils/index.js"
|
|
280
|
+
},
|
|
281
|
+
"./utils/mergeRefs": {
|
|
282
|
+
"import": "./utils/mergeRefs.js"
|
|
283
|
+
},
|
|
284
|
+
"./package.json": "./package.json"
|
|
45
285
|
},
|
|
46
286
|
"module": "./index.esm.js",
|
|
47
287
|
"main": "./index.cjs.js",
|
|
48
|
-
"version": "2.0.0-beta.
|
|
288
|
+
"version": "2.0.0-beta.75"
|
|
49
289
|
}
|
package/useAsyncFn.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
type PromiseType<P extends Promise<any>> = P extends Promise<infer T> ? T : never;
|
|
3
|
+
type FunctionReturningPromise = (...args: any[]) => Promise<any>;
|
|
4
|
+
export type UseAsyncState<T> = {
|
|
5
|
+
loading: boolean;
|
|
6
|
+
error?: undefined;
|
|
7
|
+
value?: undefined;
|
|
8
|
+
} | {
|
|
9
|
+
loading: true;
|
|
10
|
+
error?: Error | undefined;
|
|
11
|
+
value?: T;
|
|
12
|
+
} | {
|
|
13
|
+
loading: false;
|
|
14
|
+
error: Error;
|
|
15
|
+
value?: undefined;
|
|
16
|
+
} | {
|
|
17
|
+
loading: false;
|
|
18
|
+
error?: undefined;
|
|
19
|
+
value: T;
|
|
20
|
+
};
|
|
21
|
+
type StateFromFunctionReturningPromise<T extends FunctionReturningPromise> = UseAsyncState<PromiseType<ReturnType<T>>>;
|
|
22
|
+
export type UseAsyncFnReturn<T extends FunctionReturningPromise = FunctionReturningPromise> = [StateFromFunctionReturningPromise<T>, T];
|
|
23
|
+
export declare let useAsyncFn: <T extends FunctionReturningPromise>(fn: T, deps?: React.DependencyList, initialState?: StateFromFunctionReturningPromise<T>) => UseAsyncFnReturn<T>;
|
|
24
|
+
export default useAsyncFn;
|
package/useAsyncFn.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React, { useCallback, useRef, useState } from "react";
|
|
2
|
+
import { useMountedState } from "./useMountedState";
|
|
3
|
+
export let useAsyncFn = (fn, deps = [], initialState = { loading: false }) => {
|
|
4
|
+
const lastCallId = useRef(0);
|
|
5
|
+
const isMounted = useMountedState();
|
|
6
|
+
const [state, set] = useState(initialState);
|
|
7
|
+
const callback = useCallback((...args) => {
|
|
8
|
+
const callId = ++lastCallId.current;
|
|
9
|
+
if (!state.loading) {
|
|
10
|
+
set((prevState) => ({ ...prevState, loading: true }));
|
|
11
|
+
}
|
|
12
|
+
return fn(...args).then((value) => {
|
|
13
|
+
isMounted() &&
|
|
14
|
+
callId === lastCallId.current &&
|
|
15
|
+
set({ value, loading: false });
|
|
16
|
+
return value;
|
|
17
|
+
}, (error) => {
|
|
18
|
+
isMounted() &&
|
|
19
|
+
callId === lastCallId.current &&
|
|
20
|
+
set({ error, loading: false });
|
|
21
|
+
return error;
|
|
22
|
+
});
|
|
23
|
+
}, deps);
|
|
24
|
+
return [state, callback];
|
|
25
|
+
};
|
|
26
|
+
export default useAsyncFn;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
import { debounce } from "@koine/utils";
|
|
3
|
+
import { $each, calculateFixedOffset, injectCss, listenResizeDebounced, } from "@koine/dom";
|
|
4
|
+
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
|
|
5
|
+
const inject = (value) => {
|
|
6
|
+
injectCss("useFixedOffset", `html{scroll-padding-top: ${value}px}`);
|
|
7
|
+
};
|
|
8
|
+
export let useFixedOffset = (selector) => {
|
|
9
|
+
const fixedOffset = useRef(0);
|
|
10
|
+
useIsomorphicLayoutEffect(() => {
|
|
11
|
+
const update = () => {
|
|
12
|
+
const newFixedOffset = calculateFixedOffset();
|
|
13
|
+
fixedOffset.current = newFixedOffset;
|
|
14
|
+
inject(newFixedOffset);
|
|
15
|
+
};
|
|
16
|
+
update();
|
|
17
|
+
if (ResizeObserver) {
|
|
18
|
+
const observer = new ResizeObserver((entries) => {
|
|
19
|
+
let newFixedOffset = 0;
|
|
20
|
+
entries.forEach((entry) => {
|
|
21
|
+
newFixedOffset += entry.contentRect.height;
|
|
22
|
+
});
|
|
23
|
+
fixedOffset.current = newFixedOffset;
|
|
24
|
+
const updateOnResize = debounce(() => inject(newFixedOffset), 400, true);
|
|
25
|
+
updateOnResize();
|
|
26
|
+
});
|
|
27
|
+
$each(selector || "[data-fixed]", ($el) => {
|
|
28
|
+
if (observer)
|
|
29
|
+
observer.observe($el);
|
|
30
|
+
});
|
|
31
|
+
return () => {
|
|
32
|
+
observer?.disconnect();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const listener = listenResizeDebounced(0, update);
|
|
37
|
+
return listener;
|
|
38
|
+
}
|
|
39
|
+
}, [selector]);
|
|
40
|
+
return fixedOffset;
|
|
41
|
+
};
|
|
42
|
+
export default useFixedOffset;
|
package/useFocus.d.ts
ADDED
package/useFocus.js
ADDED
package/useInterval.d.ts
ADDED
package/useInterval.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { noop } from "@koine/utils";
|
|
3
|
+
export let useInterval = (callback, delay, deps = []) => {
|
|
4
|
+
const savedCallback = useRef();
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
savedCallback.current = callback;
|
|
7
|
+
}, [callback, ...deps]);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
function tick() {
|
|
10
|
+
if (savedCallback.current)
|
|
11
|
+
savedCallback.current();
|
|
12
|
+
}
|
|
13
|
+
if (delay !== null) {
|
|
14
|
+
const id = setInterval(tick, delay);
|
|
15
|
+
return () => clearInterval(id);
|
|
16
|
+
}
|
|
17
|
+
return noop;
|
|
18
|
+
}, [delay]);
|
|
19
|
+
};
|
|
20
|
+
export default useInterval;
|
package/useKeyUp.d.ts
ADDED
package/useKeyUp.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { on } from "@koine/dom";
|
|
3
|
+
export let useKeyUp = (callback, deps = []) => {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const listener = on(window, "keyup", (event) => {
|
|
6
|
+
if (!event.ctrlKey &&
|
|
7
|
+
!event.altKey &&
|
|
8
|
+
!event.shiftKey &&
|
|
9
|
+
!event.metaKey) {
|
|
10
|
+
callback(event);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
return listener;
|
|
14
|
+
}, [callback, ...deps]);
|
|
15
|
+
};
|
|
16
|
+
export default useKeyUp;
|
package/useMeasure.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface RectReadOnly {
|
|
2
|
+
readonly x: number;
|
|
3
|
+
readonly y: number;
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
readonly top: number;
|
|
7
|
+
readonly right: number;
|
|
8
|
+
readonly bottom: number;
|
|
9
|
+
readonly left: number;
|
|
10
|
+
[key: string]: number;
|
|
11
|
+
}
|
|
12
|
+
type HTMLOrSVGElement = HTMLElement | SVGElement;
|
|
13
|
+
export type UseMeasureOptions = {
|
|
14
|
+
scroll?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export type UseMeasureReturn = [
|
|
17
|
+
(element: HTMLOrSVGElement | null) => void,
|
|
18
|
+
RectReadOnly,
|
|
19
|
+
() => void
|
|
20
|
+
];
|
|
21
|
+
export declare let useMeasure: (options?: UseMeasureOptions) => UseMeasureReturn;
|
|
22
|
+
export default useMeasure;
|
package/useMeasure.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { debounce, noop } from "@koine/utils";
|
|
3
|
+
import { listenResizeDebounced, listenScrollDebounced, off, on, } from "@koine/dom";
|
|
4
|
+
let observer;
|
|
5
|
+
let findScrollContainers = (element) => {
|
|
6
|
+
const result = [];
|
|
7
|
+
if (!element || element === document.body)
|
|
8
|
+
return result;
|
|
9
|
+
const { overflow, overflowX, overflowY } = window.getComputedStyle(element);
|
|
10
|
+
if ([overflow, overflowX, overflowY].some((prop) => prop === "auto" || prop === "scroll"))
|
|
11
|
+
result.push(element);
|
|
12
|
+
return [...result, ...findScrollContainers(element.parentElement)];
|
|
13
|
+
};
|
|
14
|
+
const keys = [
|
|
15
|
+
"x",
|
|
16
|
+
"y",
|
|
17
|
+
"top",
|
|
18
|
+
"bottom",
|
|
19
|
+
"left",
|
|
20
|
+
"right",
|
|
21
|
+
"width",
|
|
22
|
+
"height",
|
|
23
|
+
];
|
|
24
|
+
const areBoundsEqual = (a, b) => keys.every((key) => a[key] === b[key]);
|
|
25
|
+
export let useMeasure = (options) => {
|
|
26
|
+
const { scroll = false } = options || {};
|
|
27
|
+
const [bounds, setBounds] = useState({
|
|
28
|
+
left: 0,
|
|
29
|
+
top: 0,
|
|
30
|
+
width: 0,
|
|
31
|
+
height: 0,
|
|
32
|
+
bottom: 0,
|
|
33
|
+
right: 0,
|
|
34
|
+
x: 0,
|
|
35
|
+
y: 0,
|
|
36
|
+
});
|
|
37
|
+
const state = useRef([
|
|
38
|
+
null,
|
|
39
|
+
null,
|
|
40
|
+
null,
|
|
41
|
+
bounds,
|
|
42
|
+
]);
|
|
43
|
+
const mounted = useRef(false);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
mounted.current = true;
|
|
46
|
+
return () => void (mounted.current = false);
|
|
47
|
+
}, []);
|
|
48
|
+
const [forceRefresh, , scrollChange] = useMemo(() => {
|
|
49
|
+
const callback = (..._args) => {
|
|
50
|
+
const [element, , , lastBounds] = state.current;
|
|
51
|
+
if (!element)
|
|
52
|
+
return;
|
|
53
|
+
const size = element.getBoundingClientRect();
|
|
54
|
+
Object.freeze(size);
|
|
55
|
+
if (mounted.current && !areBoundsEqual(lastBounds, size)) {
|
|
56
|
+
state.current[3] = size;
|
|
57
|
+
setBounds(size);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const debouncedCallback = debounce(callback);
|
|
61
|
+
return [callback, debouncedCallback, debouncedCallback];
|
|
62
|
+
}, [setBounds]);
|
|
63
|
+
function removeListeners() {
|
|
64
|
+
const [, scrollContainers, resizeObserver] = state.current;
|
|
65
|
+
if (scrollContainers) {
|
|
66
|
+
scrollContainers.forEach((element) => off(element, "scroll", scrollChange));
|
|
67
|
+
state.current[1] = null;
|
|
68
|
+
}
|
|
69
|
+
if (resizeObserver) {
|
|
70
|
+
resizeObserver.disconnect();
|
|
71
|
+
state.current[2] = null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function addListeners() {
|
|
75
|
+
const [element, scrollContainers] = state.current;
|
|
76
|
+
if (!element)
|
|
77
|
+
return;
|
|
78
|
+
if (!observer && ResizeObserver) {
|
|
79
|
+
observer = new ResizeObserver(scrollChange);
|
|
80
|
+
state.current[2] = observer;
|
|
81
|
+
observer.observe(element);
|
|
82
|
+
if (scroll && scrollContainers) {
|
|
83
|
+
scrollContainers.forEach((scrollContainer) => on(scrollContainer, "scroll", scrollChange, {
|
|
84
|
+
capture: true,
|
|
85
|
+
passive: true,
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const ref = (node) => {
|
|
91
|
+
if (!node || node === state.current[0])
|
|
92
|
+
return;
|
|
93
|
+
removeListeners();
|
|
94
|
+
state.current[0] = node;
|
|
95
|
+
state.current[1] = findScrollContainers(node);
|
|
96
|
+
addListeners();
|
|
97
|
+
};
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (scroll) {
|
|
100
|
+
const listener = listenScrollDebounced(0, forceRefresh, 100);
|
|
101
|
+
return listener;
|
|
102
|
+
}
|
|
103
|
+
return noop;
|
|
104
|
+
}, [scroll, forceRefresh]);
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
const listener = listenResizeDebounced(0, forceRefresh, 100);
|
|
107
|
+
return listener;
|
|
108
|
+
}, [forceRefresh]);
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
removeListeners();
|
|
111
|
+
addListeners();
|
|
112
|
+
}, [scroll]);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
forceRefresh();
|
|
115
|
+
return removeListeners;
|
|
116
|
+
}, []);
|
|
117
|
+
return [ref, bounds, forceRefresh];
|
|
118
|
+
};
|
|
119
|
+
export default useMeasure;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
+
export let useMountedState = () => {
|
|
3
|
+
const mountedRef = useRef(false);
|
|
4
|
+
const get = useCallback(() => mountedRef.current, []);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
mountedRef.current = true;
|
|
7
|
+
return () => {
|
|
8
|
+
mountedRef.current = false;
|
|
9
|
+
};
|
|
10
|
+
}, []);
|
|
11
|
+
return get;
|
|
12
|
+
};
|
|
13
|
+
export default useMountedState;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { on } from "@koine/dom";
|
|
3
|
+
export let useNavigateAway = (handler) => {
|
|
4
|
+
const beforeUnloadHandlerRef = useRef();
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
beforeUnloadHandlerRef.current = (event) => {
|
|
7
|
+
const customMessageOrCondition = handler(event);
|
|
8
|
+
if (customMessageOrCondition) {
|
|
9
|
+
event.preventDefault();
|
|
10
|
+
}
|
|
11
|
+
if (typeof customMessageOrCondition === "string") {
|
|
12
|
+
return (event.returnValue = customMessageOrCondition);
|
|
13
|
+
}
|
|
14
|
+
if (event.defaultPrevented) {
|
|
15
|
+
return (event.returnValue = "");
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
};
|
|
19
|
+
}, [handler]);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const listenerBeforeunload = on(window, "beforeunload", (event) => beforeUnloadHandlerRef.current?.(event));
|
|
22
|
+
return listenerBeforeunload;
|
|
23
|
+
}, []);
|
|
24
|
+
};
|
|
25
|
+
export default useNavigateAway;
|
package/usePrevious.d.ts
ADDED
package/usePrevious.js
ADDED