@omnitron-dev/prism 0.1.1
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/LICENSE +21 -0
- package/README.md +44 -0
- package/bin/prism.js +13 -0
- package/dist/accessibility/index.d.ts +157 -0
- package/dist/accessibility/index.js +186 -0
- package/dist/accessibility/index.js.map +1 -0
- package/dist/blocks/index.d.ts +1239 -0
- package/dist/blocks/index.js +1712 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/chunk-77KIJLPP.js +37 -0
- package/dist/chunk-77KIJLPP.js.map +1 -0
- package/dist/chunk-BTIT7WYH.js +339 -0
- package/dist/chunk-BTIT7WYH.js.map +1 -0
- package/dist/chunk-BV32OJ2A.js +1409 -0
- package/dist/chunk-BV32OJ2A.js.map +1 -0
- package/dist/chunk-DDY7TLUI.js +139 -0
- package/dist/chunk-DDY7TLUI.js.map +1 -0
- package/dist/chunk-DXXZ3ITR.js +4227 -0
- package/dist/chunk-DXXZ3ITR.js.map +1 -0
- package/dist/chunk-E3XANPTO.js +2319 -0
- package/dist/chunk-E3XANPTO.js.map +1 -0
- package/dist/chunk-FU52GL2E.js +3 -0
- package/dist/chunk-FU52GL2E.js.map +1 -0
- package/dist/chunk-GYDT5EKX.js +16505 -0
- package/dist/chunk-GYDT5EKX.js.map +1 -0
- package/dist/chunk-HO2GKTWJ.js +28 -0
- package/dist/chunk-HO2GKTWJ.js.map +1 -0
- package/dist/chunk-JDTC2SBY.js +140 -0
- package/dist/chunk-JDTC2SBY.js.map +1 -0
- package/dist/chunk-JNU4QHJK.js +6 -0
- package/dist/chunk-JNU4QHJK.js.map +1 -0
- package/dist/chunk-LAEY5USO.js +2621 -0
- package/dist/chunk-LAEY5USO.js.map +1 -0
- package/dist/chunk-UR6X3W5I.js +530 -0
- package/dist/chunk-UR6X3W5I.js.map +1 -0
- package/dist/chunk-WC2WM2JD.js +883 -0
- package/dist/chunk-WC2WM2JD.js.map +1 -0
- package/dist/chunk-XWL6J6IO.js +40 -0
- package/dist/chunk-XWL6J6IO.js.map +1 -0
- package/dist/cli/index.js +2695 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config-BA6IeTq9.d.ts +100 -0
- package/dist/core/index.d.ts +454 -0
- package/dist/core/index.js +9 -0
- package/dist/core/index.js.map +1 -0
- package/dist/forms/index.d.ts +562 -0
- package/dist/forms/index.js +545 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.js +10 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/http/index.d.ts +233 -0
- package/dist/http/index.js +282 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index-odhPSv9I.d.ts +2491 -0
- package/dist/index.css +131 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +9109 -0
- package/dist/index.js +13280 -0
- package/dist/index.js.map +1 -0
- package/dist/layouts/index.d.ts +854 -0
- package/dist/layouts/index.js +6 -0
- package/dist/layouts/index.js.map +1 -0
- package/dist/netron/index.d.ts +4 -0
- package/dist/netron/index.js +4 -0
- package/dist/netron/index.js.map +1 -0
- package/dist/registry/index.d.ts +352 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry-BrDNftd5.d.ts +574 -0
- package/dist/settings-plpR26Xk.d.ts +455 -0
- package/dist/state/index.d.ts +981 -0
- package/dist/state/index.js +5 -0
- package/dist/state/index.js.map +1 -0
- package/dist/theme/index.d.ts +1049 -0
- package/dist/theme/index.js +5 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme-CrXLfP1c.d.ts +389 -0
- package/dist/types/index.d.ts +146 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/use-timeout-D7U3EAtH.d.ts +808 -0
- package/package.json +194 -0
|
@@ -0,0 +1,2319 @@
|
|
|
1
|
+
import { useIsClient } from './chunk-WC2WM2JD.js';
|
|
2
|
+
import { PRESET_NAMES } from './chunk-DXXZ3ITR.js';
|
|
3
|
+
import { useLayoutEffect, useEffect, useState, useRef, useCallback, useMemo, useSyncExternalStore } from 'react';
|
|
4
|
+
import Popover from '@mui/material/Popover';
|
|
5
|
+
import { styled, alpha } from '@mui/material/styles';
|
|
6
|
+
import { listClasses } from '@mui/material/List';
|
|
7
|
+
import { menuItemClasses } from '@mui/material/MenuItem';
|
|
8
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
function useAsync(asyncFn, options = {}) {
|
|
11
|
+
const { initialData = null, immediate = false, onSuccess, onError } = options;
|
|
12
|
+
const [state, setState] = useState({
|
|
13
|
+
data: initialData,
|
|
14
|
+
loading: immediate,
|
|
15
|
+
error: null
|
|
16
|
+
});
|
|
17
|
+
const isMounted = useRef(true);
|
|
18
|
+
const executingRef = useRef(false);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
isMounted.current = true;
|
|
21
|
+
return () => {
|
|
22
|
+
isMounted.current = false;
|
|
23
|
+
};
|
|
24
|
+
}, []);
|
|
25
|
+
const execute = useCallback(
|
|
26
|
+
async (...args) => {
|
|
27
|
+
if (executingRef.current) {
|
|
28
|
+
return Promise.reject(new Error("Operation already in progress"));
|
|
29
|
+
}
|
|
30
|
+
executingRef.current = true;
|
|
31
|
+
if (isMounted.current) {
|
|
32
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const result = await asyncFn(...args);
|
|
36
|
+
if (isMounted.current) {
|
|
37
|
+
setState({ data: result, loading: false, error: null });
|
|
38
|
+
onSuccess?.(result);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
} catch (err) {
|
|
42
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
43
|
+
if (isMounted.current) {
|
|
44
|
+
setState((prev) => ({ ...prev, loading: false, error }));
|
|
45
|
+
onError?.(error);
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
} finally {
|
|
49
|
+
executingRef.current = false;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
[asyncFn, onSuccess, onError]
|
|
53
|
+
);
|
|
54
|
+
const reset = useCallback(() => {
|
|
55
|
+
setState({ data: initialData, loading: false, error: null });
|
|
56
|
+
}, [initialData]);
|
|
57
|
+
const setData = useCallback((data) => {
|
|
58
|
+
setState((prev) => ({ ...prev, data }));
|
|
59
|
+
}, []);
|
|
60
|
+
const setError = useCallback((error) => {
|
|
61
|
+
setState((prev) => ({ ...prev, error }));
|
|
62
|
+
}, []);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (immediate) {
|
|
65
|
+
execute(...[]).catch(() => {
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}, []);
|
|
69
|
+
return useMemo(
|
|
70
|
+
() => ({
|
|
71
|
+
...state,
|
|
72
|
+
execute,
|
|
73
|
+
reset,
|
|
74
|
+
setData,
|
|
75
|
+
setError
|
|
76
|
+
}),
|
|
77
|
+
[state, execute, reset, setData, setError]
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
function useAsyncFn(asyncFn) {
|
|
81
|
+
const [loading, setLoading] = useState(false);
|
|
82
|
+
const [error, setError] = useState(null);
|
|
83
|
+
const isMounted = useRef(true);
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
isMounted.current = true;
|
|
86
|
+
return () => {
|
|
87
|
+
isMounted.current = false;
|
|
88
|
+
};
|
|
89
|
+
}, []);
|
|
90
|
+
const execute = useCallback(
|
|
91
|
+
async (...args) => {
|
|
92
|
+
setLoading(true);
|
|
93
|
+
setError(null);
|
|
94
|
+
try {
|
|
95
|
+
const result = await asyncFn(...args);
|
|
96
|
+
if (isMounted.current) {
|
|
97
|
+
setLoading(false);
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
const caughtError = err instanceof Error ? err : new Error(String(err));
|
|
102
|
+
if (isMounted.current) {
|
|
103
|
+
setLoading(false);
|
|
104
|
+
setError(caughtError);
|
|
105
|
+
}
|
|
106
|
+
return void 0;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
[asyncFn]
|
|
110
|
+
);
|
|
111
|
+
return { execute, loading, error };
|
|
112
|
+
}
|
|
113
|
+
function useScrollPosition(options = {}) {
|
|
114
|
+
const { element = null, throttle = 100, threshold = 0, bottomOffset = 10 } = options;
|
|
115
|
+
const [position, setPosition] = useState({
|
|
116
|
+
x: 0,
|
|
117
|
+
y: 0,
|
|
118
|
+
directionX: 0,
|
|
119
|
+
directionY: 0,
|
|
120
|
+
isScrolled: false,
|
|
121
|
+
isAtTop: true,
|
|
122
|
+
isAtBottom: false
|
|
123
|
+
});
|
|
124
|
+
const lastPosition = useRef({ x: 0, y: 0 });
|
|
125
|
+
const ticking = useRef(false);
|
|
126
|
+
const updatePosition = useCallback(() => {
|
|
127
|
+
if (typeof window === "undefined") return;
|
|
128
|
+
const scrollX = element ? element.scrollLeft : window.scrollX;
|
|
129
|
+
const scrollY = element ? element.scrollTop : window.scrollY;
|
|
130
|
+
const maxScrollY = element ? element.scrollHeight - element.clientHeight : document.documentElement.scrollHeight - window.innerHeight;
|
|
131
|
+
const directionX = scrollX > lastPosition.current.x ? 1 : scrollX < lastPosition.current.x ? -1 : 0;
|
|
132
|
+
const directionY = scrollY > lastPosition.current.y ? 1 : scrollY < lastPosition.current.y ? -1 : 0;
|
|
133
|
+
lastPosition.current = { x: scrollX, y: scrollY };
|
|
134
|
+
setPosition({
|
|
135
|
+
x: scrollX,
|
|
136
|
+
y: scrollY,
|
|
137
|
+
directionX,
|
|
138
|
+
directionY,
|
|
139
|
+
isScrolled: scrollY > threshold,
|
|
140
|
+
isAtTop: scrollY <= 0,
|
|
141
|
+
isAtBottom: scrollY >= maxScrollY - bottomOffset
|
|
142
|
+
});
|
|
143
|
+
ticking.current = false;
|
|
144
|
+
}, [element, threshold, bottomOffset]);
|
|
145
|
+
const handleScroll = useCallback(() => {
|
|
146
|
+
if (!ticking.current) {
|
|
147
|
+
if (throttle > 0) {
|
|
148
|
+
window.requestAnimationFrame(() => {
|
|
149
|
+
updatePosition();
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
updatePosition();
|
|
153
|
+
}
|
|
154
|
+
ticking.current = true;
|
|
155
|
+
if (throttle > 0) {
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
ticking.current = false;
|
|
158
|
+
}, throttle);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}, [throttle, updatePosition]);
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (typeof window === "undefined") return void 0;
|
|
164
|
+
const target = element ?? window;
|
|
165
|
+
updatePosition();
|
|
166
|
+
target.addEventListener("scroll", handleScroll, { passive: true });
|
|
167
|
+
return () => {
|
|
168
|
+
target.removeEventListener("scroll", handleScroll);
|
|
169
|
+
};
|
|
170
|
+
}, [element, handleScroll, updatePosition]);
|
|
171
|
+
return position;
|
|
172
|
+
}
|
|
173
|
+
function useScrollToTop(options = {}) {
|
|
174
|
+
const { threshold = 300 } = options;
|
|
175
|
+
const { y } = useScrollPosition({ threshold });
|
|
176
|
+
const scrollToTop = useCallback(() => {
|
|
177
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
178
|
+
}, []);
|
|
179
|
+
return {
|
|
180
|
+
scrollToTop,
|
|
181
|
+
showButton: y > threshold
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function useScrollLock() {
|
|
185
|
+
const [isLocked, setIsLocked] = useState(false);
|
|
186
|
+
const scrollY = useRef(0);
|
|
187
|
+
const lock = useCallback(() => {
|
|
188
|
+
if (typeof document === "undefined") return;
|
|
189
|
+
scrollY.current = window.scrollY;
|
|
190
|
+
document.body.style.position = "fixed";
|
|
191
|
+
document.body.style.top = `-${scrollY.current}px`;
|
|
192
|
+
document.body.style.width = "100%";
|
|
193
|
+
document.body.style.overflow = "hidden";
|
|
194
|
+
setIsLocked(true);
|
|
195
|
+
}, []);
|
|
196
|
+
const unlock = useCallback(() => {
|
|
197
|
+
if (typeof document === "undefined") return;
|
|
198
|
+
document.body.style.position = "";
|
|
199
|
+
document.body.style.top = "";
|
|
200
|
+
document.body.style.width = "";
|
|
201
|
+
document.body.style.overflow = "";
|
|
202
|
+
window.scrollTo(0, scrollY.current);
|
|
203
|
+
setIsLocked(false);
|
|
204
|
+
}, []);
|
|
205
|
+
useEffect(
|
|
206
|
+
() => () => {
|
|
207
|
+
if (isLocked) {
|
|
208
|
+
document.body.style.position = "";
|
|
209
|
+
document.body.style.top = "";
|
|
210
|
+
document.body.style.width = "";
|
|
211
|
+
document.body.style.overflow = "";
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
[isLocked]
|
|
215
|
+
);
|
|
216
|
+
return { lock, unlock, isLocked };
|
|
217
|
+
}
|
|
218
|
+
var defaultBreakpoints = {
|
|
219
|
+
xs: 0,
|
|
220
|
+
sm: 600,
|
|
221
|
+
md: 900,
|
|
222
|
+
lg: 1200,
|
|
223
|
+
xl: 1536
|
|
224
|
+
};
|
|
225
|
+
function useWindowSize(options = {}) {
|
|
226
|
+
const { breakpoints: customBreakpoints, debounce = 100, initialWidth = 1200, initialHeight = 800 } = options;
|
|
227
|
+
const breakpoints = { ...defaultBreakpoints, ...customBreakpoints };
|
|
228
|
+
const getSize = useCallback(() => {
|
|
229
|
+
if (typeof window === "undefined") {
|
|
230
|
+
return {
|
|
231
|
+
width: initialWidth,
|
|
232
|
+
height: initialHeight,
|
|
233
|
+
isMobile: initialWidth < breakpoints.sm,
|
|
234
|
+
isTablet: initialWidth >= breakpoints.sm && initialWidth < breakpoints.md,
|
|
235
|
+
isDesktop: initialWidth >= breakpoints.md,
|
|
236
|
+
isLargeDesktop: initialWidth >= breakpoints.lg
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const width = window.innerWidth;
|
|
240
|
+
const height = window.innerHeight;
|
|
241
|
+
return {
|
|
242
|
+
width,
|
|
243
|
+
height,
|
|
244
|
+
isMobile: width < breakpoints.sm,
|
|
245
|
+
isTablet: width >= breakpoints.sm && width < breakpoints.md,
|
|
246
|
+
isDesktop: width >= breakpoints.md,
|
|
247
|
+
isLargeDesktop: width >= breakpoints.lg
|
|
248
|
+
};
|
|
249
|
+
}, [breakpoints.sm, breakpoints.md, breakpoints.lg, initialWidth, initialHeight]);
|
|
250
|
+
const [size, setSize] = useState(getSize);
|
|
251
|
+
const timeoutRef = useRef(null);
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
if (typeof window === "undefined") return void 0;
|
|
254
|
+
const handleResize = () => {
|
|
255
|
+
if (debounce > 0) {
|
|
256
|
+
if (timeoutRef.current) {
|
|
257
|
+
clearTimeout(timeoutRef.current);
|
|
258
|
+
}
|
|
259
|
+
timeoutRef.current = setTimeout(() => {
|
|
260
|
+
setSize(getSize());
|
|
261
|
+
}, debounce);
|
|
262
|
+
} else {
|
|
263
|
+
setSize(getSize());
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
setSize(getSize());
|
|
267
|
+
window.addEventListener("resize", handleResize);
|
|
268
|
+
return () => {
|
|
269
|
+
window.removeEventListener("resize", handleResize);
|
|
270
|
+
if (timeoutRef.current) {
|
|
271
|
+
clearTimeout(timeoutRef.current);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}, [debounce, getSize]);
|
|
275
|
+
return size;
|
|
276
|
+
}
|
|
277
|
+
function useResponsiveQuery(query) {
|
|
278
|
+
const [matches, setMatches] = useState(() => {
|
|
279
|
+
if (typeof window === "undefined") return false;
|
|
280
|
+
return window.matchMedia(query).matches;
|
|
281
|
+
});
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
if (typeof window === "undefined") return void 0;
|
|
284
|
+
const mediaQuery = window.matchMedia(query);
|
|
285
|
+
const handleChange = (event) => {
|
|
286
|
+
setMatches(event.matches);
|
|
287
|
+
};
|
|
288
|
+
setMatches(mediaQuery.matches);
|
|
289
|
+
if (mediaQuery.addEventListener) {
|
|
290
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
291
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
292
|
+
}
|
|
293
|
+
mediaQuery.addListener(handleChange);
|
|
294
|
+
return () => mediaQuery.removeListener(handleChange);
|
|
295
|
+
}, [query]);
|
|
296
|
+
return matches;
|
|
297
|
+
}
|
|
298
|
+
function useBreakpoint(customBreakpoints) {
|
|
299
|
+
const breakpoints = { ...defaultBreakpoints, ...customBreakpoints };
|
|
300
|
+
const { width } = useWindowSize({ breakpoints: customBreakpoints });
|
|
301
|
+
if (width >= breakpoints.xl) return "xl";
|
|
302
|
+
if (width >= breakpoints.lg) return "lg";
|
|
303
|
+
if (width >= breakpoints.md) return "md";
|
|
304
|
+
if (width >= breakpoints.sm) return "sm";
|
|
305
|
+
return "xs";
|
|
306
|
+
}
|
|
307
|
+
function useBreakpointChecks(customBreakpoints) {
|
|
308
|
+
const breakpoints = useMemo(() => ({ ...defaultBreakpoints, ...customBreakpoints }), [customBreakpoints]);
|
|
309
|
+
const { width } = useWindowSize({ breakpoints: customBreakpoints });
|
|
310
|
+
const orderedBreakpoints = ["xs", "sm", "md", "lg", "xl"];
|
|
311
|
+
const getNextBreakpoint = (bp) => {
|
|
312
|
+
const index = orderedBreakpoints.indexOf(bp);
|
|
313
|
+
return index < orderedBreakpoints.length - 1 ? orderedBreakpoints[index + 1] : null;
|
|
314
|
+
};
|
|
315
|
+
const isUp = useCallback(
|
|
316
|
+
(breakpoint) => width >= breakpoints[breakpoint],
|
|
317
|
+
[width, breakpoints]
|
|
318
|
+
);
|
|
319
|
+
const isDown = useCallback(
|
|
320
|
+
(breakpoint) => width < breakpoints[breakpoint],
|
|
321
|
+
[width, breakpoints]
|
|
322
|
+
);
|
|
323
|
+
const isOnly = useCallback(
|
|
324
|
+
(breakpoint) => {
|
|
325
|
+
const next = getNextBreakpoint(breakpoint);
|
|
326
|
+
const min = breakpoints[breakpoint];
|
|
327
|
+
const max = next ? breakpoints[next] : Infinity;
|
|
328
|
+
return width >= min && width < max;
|
|
329
|
+
},
|
|
330
|
+
[width, breakpoints]
|
|
331
|
+
);
|
|
332
|
+
const isBetween = useCallback(
|
|
333
|
+
(start, end) => width >= breakpoints[start] && width < breakpoints[end],
|
|
334
|
+
[width, breakpoints]
|
|
335
|
+
);
|
|
336
|
+
return { isUp, isDown, isOnly, isBetween };
|
|
337
|
+
}
|
|
338
|
+
var defaultValues = {
|
|
339
|
+
top: 0,
|
|
340
|
+
right: 0,
|
|
341
|
+
bottom: 0,
|
|
342
|
+
left: 0,
|
|
343
|
+
x: 0,
|
|
344
|
+
y: 0,
|
|
345
|
+
width: 0,
|
|
346
|
+
height: 0,
|
|
347
|
+
scrollWidth: 0,
|
|
348
|
+
scrollHeight: 0
|
|
349
|
+
};
|
|
350
|
+
function useClientRect(inputRef, eventType) {
|
|
351
|
+
const internalRef = useRef(null);
|
|
352
|
+
const elementRef = inputRef ?? internalRef;
|
|
353
|
+
const [rect, setRect] = useState(defaultValues);
|
|
354
|
+
const update = useCallback(() => {
|
|
355
|
+
const element = elementRef.current;
|
|
356
|
+
if (!element) {
|
|
357
|
+
setRect(defaultValues);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const domRect = element.getBoundingClientRect();
|
|
361
|
+
setRect({
|
|
362
|
+
top: domRect.top,
|
|
363
|
+
right: domRect.right,
|
|
364
|
+
bottom: domRect.bottom,
|
|
365
|
+
left: domRect.left,
|
|
366
|
+
x: domRect.x,
|
|
367
|
+
y: domRect.y,
|
|
368
|
+
width: domRect.width,
|
|
369
|
+
height: domRect.height,
|
|
370
|
+
scrollWidth: element.scrollWidth,
|
|
371
|
+
scrollHeight: element.scrollHeight
|
|
372
|
+
});
|
|
373
|
+
}, [elementRef]);
|
|
374
|
+
useEffect(() => {
|
|
375
|
+
const element = elementRef.current;
|
|
376
|
+
update();
|
|
377
|
+
if (!element || typeof window === "undefined") return void 0;
|
|
378
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
379
|
+
update();
|
|
380
|
+
});
|
|
381
|
+
resizeObserver.observe(element);
|
|
382
|
+
const target = eventType === "scroll" ? element : window;
|
|
383
|
+
const event = eventType ?? "resize";
|
|
384
|
+
const handleEvent = () => {
|
|
385
|
+
update();
|
|
386
|
+
};
|
|
387
|
+
target.addEventListener(event, handleEvent, { passive: true });
|
|
388
|
+
return () => {
|
|
389
|
+
resizeObserver.disconnect();
|
|
390
|
+
target.removeEventListener(event, handleEvent);
|
|
391
|
+
};
|
|
392
|
+
}, [elementRef, eventType, update]);
|
|
393
|
+
return {
|
|
394
|
+
...rect,
|
|
395
|
+
elementRef,
|
|
396
|
+
update
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function usePopoverHover(inputRef) {
|
|
400
|
+
const internalRef = useRef(null);
|
|
401
|
+
const elementRef = inputRef ?? internalRef;
|
|
402
|
+
const [open, setOpen] = useState(false);
|
|
403
|
+
const onOpen = useCallback(() => {
|
|
404
|
+
setOpen(true);
|
|
405
|
+
}, []);
|
|
406
|
+
const onClose = useCallback(() => {
|
|
407
|
+
setOpen(false);
|
|
408
|
+
}, []);
|
|
409
|
+
const anchorEl = open ? elementRef.current : null;
|
|
410
|
+
return {
|
|
411
|
+
open,
|
|
412
|
+
anchorEl,
|
|
413
|
+
onOpen,
|
|
414
|
+
onClose,
|
|
415
|
+
elementRef,
|
|
416
|
+
setOpen
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
var POPOVER_DISTANCE = 0.75;
|
|
420
|
+
function calculateAnchorOrigin(arrow = "top-right") {
|
|
421
|
+
const configs = {
|
|
422
|
+
"top-left": {
|
|
423
|
+
paperStyles: { ml: -POPOVER_DISTANCE },
|
|
424
|
+
anchorOrigin: { vertical: "bottom", horizontal: "left" },
|
|
425
|
+
transformOrigin: { vertical: "top", horizontal: "left" }
|
|
426
|
+
},
|
|
427
|
+
"top-center": {
|
|
428
|
+
paperStyles: void 0,
|
|
429
|
+
anchorOrigin: { vertical: "bottom", horizontal: "center" },
|
|
430
|
+
transformOrigin: { vertical: "top", horizontal: "center" }
|
|
431
|
+
},
|
|
432
|
+
"top-right": {
|
|
433
|
+
paperStyles: { ml: POPOVER_DISTANCE },
|
|
434
|
+
anchorOrigin: { vertical: "bottom", horizontal: "right" },
|
|
435
|
+
transformOrigin: { vertical: "top", horizontal: "right" }
|
|
436
|
+
},
|
|
437
|
+
"bottom-left": {
|
|
438
|
+
paperStyles: { ml: -POPOVER_DISTANCE },
|
|
439
|
+
anchorOrigin: { vertical: "top", horizontal: "left" },
|
|
440
|
+
transformOrigin: { vertical: "bottom", horizontal: "left" }
|
|
441
|
+
},
|
|
442
|
+
"bottom-center": {
|
|
443
|
+
paperStyles: void 0,
|
|
444
|
+
anchorOrigin: { vertical: "top", horizontal: "center" },
|
|
445
|
+
transformOrigin: { vertical: "bottom", horizontal: "center" }
|
|
446
|
+
},
|
|
447
|
+
"bottom-right": {
|
|
448
|
+
paperStyles: { ml: POPOVER_DISTANCE },
|
|
449
|
+
anchorOrigin: { vertical: "top", horizontal: "right" },
|
|
450
|
+
transformOrigin: { vertical: "bottom", horizontal: "right" }
|
|
451
|
+
},
|
|
452
|
+
"left-top": {
|
|
453
|
+
paperStyles: { mt: -POPOVER_DISTANCE },
|
|
454
|
+
anchorOrigin: { vertical: "top", horizontal: "right" },
|
|
455
|
+
transformOrigin: { vertical: "top", horizontal: "left" }
|
|
456
|
+
},
|
|
457
|
+
"left-center": {
|
|
458
|
+
paperStyles: void 0,
|
|
459
|
+
anchorOrigin: { vertical: "center", horizontal: "right" },
|
|
460
|
+
transformOrigin: { vertical: "center", horizontal: "left" }
|
|
461
|
+
},
|
|
462
|
+
"left-bottom": {
|
|
463
|
+
paperStyles: { mt: POPOVER_DISTANCE },
|
|
464
|
+
anchorOrigin: { vertical: "bottom", horizontal: "right" },
|
|
465
|
+
transformOrigin: { vertical: "bottom", horizontal: "left" }
|
|
466
|
+
},
|
|
467
|
+
"right-top": {
|
|
468
|
+
paperStyles: { mt: -POPOVER_DISTANCE },
|
|
469
|
+
anchorOrigin: { vertical: "top", horizontal: "left" },
|
|
470
|
+
transformOrigin: { vertical: "top", horizontal: "right" }
|
|
471
|
+
},
|
|
472
|
+
"right-center": {
|
|
473
|
+
paperStyles: void 0,
|
|
474
|
+
anchorOrigin: { vertical: "center", horizontal: "left" },
|
|
475
|
+
transformOrigin: { vertical: "center", horizontal: "right" }
|
|
476
|
+
},
|
|
477
|
+
"right-bottom": {
|
|
478
|
+
paperStyles: { mt: POPOVER_DISTANCE },
|
|
479
|
+
anchorOrigin: { vertical: "bottom", horizontal: "left" },
|
|
480
|
+
transformOrigin: { vertical: "bottom", horizontal: "right" }
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
return configs[arrow] || configs["top-right"];
|
|
484
|
+
}
|
|
485
|
+
var Arrow = styled("span", {
|
|
486
|
+
shouldForwardProp: (prop) => prop !== "ownerState"
|
|
487
|
+
})(({ theme, ownerState }) => {
|
|
488
|
+
const { size, offset, placement } = ownerState;
|
|
489
|
+
const directions = {
|
|
490
|
+
top: { top: 0, rotate: "135deg", translate: "0 -50%" },
|
|
491
|
+
bottom: { bottom: 0, rotate: "-45deg", translate: "0 50%" },
|
|
492
|
+
left: { left: 0, rotate: "45deg", translate: "-50% 0" },
|
|
493
|
+
right: { right: 0, rotate: "-135deg", translate: "50% 0" }
|
|
494
|
+
};
|
|
495
|
+
const getDirection = () => {
|
|
496
|
+
if (placement.startsWith("top-")) return directions.top;
|
|
497
|
+
if (placement.startsWith("bottom-")) return directions.bottom;
|
|
498
|
+
if (placement.startsWith("left-")) return directions.left;
|
|
499
|
+
if (placement.startsWith("right-")) return directions.right;
|
|
500
|
+
return {};
|
|
501
|
+
};
|
|
502
|
+
const getPosition = () => {
|
|
503
|
+
if (placement.endsWith("-left")) return { left: offset };
|
|
504
|
+
if (placement.endsWith("-right")) return { right: offset };
|
|
505
|
+
if (placement.endsWith("-top")) return { top: offset };
|
|
506
|
+
if (placement.endsWith("-bottom")) return { bottom: offset };
|
|
507
|
+
if (placement.endsWith("-center")) {
|
|
508
|
+
if (placement.startsWith("top-") || placement.startsWith("bottom-")) {
|
|
509
|
+
return { left: 0, right: 0, margin: "auto" };
|
|
510
|
+
}
|
|
511
|
+
return { top: 0, bottom: 0, margin: "auto" };
|
|
512
|
+
}
|
|
513
|
+
return {};
|
|
514
|
+
};
|
|
515
|
+
return {
|
|
516
|
+
width: size,
|
|
517
|
+
height: size,
|
|
518
|
+
position: "absolute",
|
|
519
|
+
borderBottomLeftRadius: size / 4,
|
|
520
|
+
clipPath: "polygon(0% 0%, 100% 100%, 0% 100%)",
|
|
521
|
+
backgroundColor: theme.palette.background.paper,
|
|
522
|
+
border: `solid 1px ${alpha(theme.palette.grey[500], 0.12)}`,
|
|
523
|
+
...getDirection(),
|
|
524
|
+
...getPosition()
|
|
525
|
+
};
|
|
526
|
+
});
|
|
527
|
+
function CustomPopover({
|
|
528
|
+
open,
|
|
529
|
+
onClose,
|
|
530
|
+
children,
|
|
531
|
+
anchorEl,
|
|
532
|
+
arrow,
|
|
533
|
+
minWidth = 140,
|
|
534
|
+
slotProps,
|
|
535
|
+
...other
|
|
536
|
+
}) {
|
|
537
|
+
const arrowSize = arrow?.size ?? 14;
|
|
538
|
+
const arrowOffset = arrow?.offset ?? 17;
|
|
539
|
+
const arrowPlacement = arrow?.placement ?? "top-right";
|
|
540
|
+
const hideArrow = arrow?.hide ?? false;
|
|
541
|
+
const { paperStyles, anchorOrigin, transformOrigin } = calculateAnchorOrigin(arrowPlacement);
|
|
542
|
+
const isOpen = Boolean(open);
|
|
543
|
+
return /* @__PURE__ */ jsxs(
|
|
544
|
+
Popover,
|
|
545
|
+
{
|
|
546
|
+
open: isOpen,
|
|
547
|
+
anchorEl,
|
|
548
|
+
onClose,
|
|
549
|
+
anchorOrigin,
|
|
550
|
+
transformOrigin,
|
|
551
|
+
slotProps: {
|
|
552
|
+
...slotProps,
|
|
553
|
+
paper: {
|
|
554
|
+
...typeof slotProps?.paper === "object" ? slotProps.paper : {},
|
|
555
|
+
sx: [
|
|
556
|
+
paperStyles,
|
|
557
|
+
{
|
|
558
|
+
overflow: "inherit",
|
|
559
|
+
[`& .${listClasses.root}`]: { minWidth },
|
|
560
|
+
[`& .${menuItemClasses.root}`]: { gap: 2 }
|
|
561
|
+
},
|
|
562
|
+
...typeof slotProps?.paper === "object" && slotProps.paper && "sx" in slotProps.paper ? Array.isArray(slotProps.paper.sx) ? slotProps.paper.sx : [slotProps.paper.sx] : []
|
|
563
|
+
]
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
...other,
|
|
567
|
+
children: [
|
|
568
|
+
!hideArrow && /* @__PURE__ */ jsx(
|
|
569
|
+
Arrow,
|
|
570
|
+
{
|
|
571
|
+
ownerState: {
|
|
572
|
+
size: arrowSize,
|
|
573
|
+
offset: arrowOffset,
|
|
574
|
+
placement: arrowPlacement
|
|
575
|
+
},
|
|
576
|
+
sx: arrow?.sx
|
|
577
|
+
}
|
|
578
|
+
),
|
|
579
|
+
children
|
|
580
|
+
]
|
|
581
|
+
}
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
function usePopover() {
|
|
585
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
586
|
+
const onOpen = useCallback((event) => {
|
|
587
|
+
setAnchorEl(event.currentTarget);
|
|
588
|
+
}, []);
|
|
589
|
+
const onClose = useCallback(() => {
|
|
590
|
+
setAnchorEl(null);
|
|
591
|
+
}, []);
|
|
592
|
+
return {
|
|
593
|
+
open: Boolean(anchorEl),
|
|
594
|
+
anchorEl,
|
|
595
|
+
onOpen,
|
|
596
|
+
onClose,
|
|
597
|
+
setAnchorEl
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/utils/cookies.ts
|
|
602
|
+
function cookiesAvailable() {
|
|
603
|
+
if (typeof document === "undefined") {
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
document.cookie = "__test__=1";
|
|
608
|
+
const result = document.cookie.includes("__test__=1");
|
|
609
|
+
document.cookie = "__test__=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
|
610
|
+
return result;
|
|
611
|
+
} catch {
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function getCookie(key) {
|
|
616
|
+
if (typeof document === "undefined") {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
const name = `${key}=`;
|
|
621
|
+
const decodedCookie = decodeURIComponent(document.cookie);
|
|
622
|
+
const cookies = decodedCookie.split(";");
|
|
623
|
+
for (const cookie of cookies) {
|
|
624
|
+
const c = cookie.trim();
|
|
625
|
+
if (c.startsWith(name)) {
|
|
626
|
+
const value = c.substring(name.length);
|
|
627
|
+
return JSON.parse(value);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return null;
|
|
631
|
+
} catch {
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function setCookie(key, value, options = {}) {
|
|
636
|
+
if (typeof document === "undefined") {
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const { secure = false, daysUntilExpiration = 30, sameSite = "lax", domain, path = "/" } = options;
|
|
640
|
+
try {
|
|
641
|
+
const serializedValue = JSON.stringify(value);
|
|
642
|
+
let cookieString = `${key}=${encodeURIComponent(serializedValue)}`;
|
|
643
|
+
if (daysUntilExpiration) {
|
|
644
|
+
const date = /* @__PURE__ */ new Date();
|
|
645
|
+
date.setTime(date.getTime() + daysUntilExpiration * 24 * 60 * 60 * 1e3);
|
|
646
|
+
cookieString += `; expires=${date.toUTCString()}`;
|
|
647
|
+
}
|
|
648
|
+
cookieString += `; path=${path}`;
|
|
649
|
+
cookieString += `; SameSite=${sameSite}`;
|
|
650
|
+
if (secure) {
|
|
651
|
+
cookieString += "; Secure";
|
|
652
|
+
}
|
|
653
|
+
if (domain) {
|
|
654
|
+
cookieString += `; domain=${domain}`;
|
|
655
|
+
}
|
|
656
|
+
document.cookie = cookieString;
|
|
657
|
+
} catch (error) {
|
|
658
|
+
console.warn(`Failed to set cookie "${key}":`, error);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
function removeCookie(key, options = {}) {
|
|
662
|
+
if (typeof document === "undefined") {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const { domain, path = "/" } = options;
|
|
666
|
+
let cookieString = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${path}`;
|
|
667
|
+
if (domain) {
|
|
668
|
+
cookieString += `; domain=${domain}`;
|
|
669
|
+
}
|
|
670
|
+
document.cookie = cookieString;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// src/hooks/use-cookies.ts
|
|
674
|
+
function useCookies(key, initialState, options) {
|
|
675
|
+
const { initializeWithValue = true, ...cookieOptions } = options ?? {};
|
|
676
|
+
const [state, setStateInternal] = useState(initialState);
|
|
677
|
+
useEffect(() => {
|
|
678
|
+
const storedValue = getCookie(key);
|
|
679
|
+
const isObject = initialState !== null && typeof initialState === "object";
|
|
680
|
+
if (storedValue !== null) {
|
|
681
|
+
if (isObject) {
|
|
682
|
+
setStateInternal((prev) => ({ ...prev, ...storedValue }));
|
|
683
|
+
} else {
|
|
684
|
+
setStateInternal(storedValue);
|
|
685
|
+
}
|
|
686
|
+
} else if (initializeWithValue) {
|
|
687
|
+
setCookie(key, initialState, cookieOptions);
|
|
688
|
+
}
|
|
689
|
+
}, []);
|
|
690
|
+
const isObjectState = initialState !== null && typeof initialState === "object";
|
|
691
|
+
const updateState = useCallback(
|
|
692
|
+
(newState) => {
|
|
693
|
+
if (isObjectState) {
|
|
694
|
+
setStateInternal((prev) => {
|
|
695
|
+
const updatedState = { ...prev, ...newState };
|
|
696
|
+
setCookie(key, updatedState, cookieOptions);
|
|
697
|
+
return updatedState;
|
|
698
|
+
});
|
|
699
|
+
} else {
|
|
700
|
+
setCookie(key, newState, cookieOptions);
|
|
701
|
+
setStateInternal(newState);
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
[cookieOptions, isObjectState, key]
|
|
705
|
+
);
|
|
706
|
+
const updateField = useCallback(
|
|
707
|
+
(fieldName, updateValue) => {
|
|
708
|
+
if (isObjectState) {
|
|
709
|
+
updateState({ [fieldName]: updateValue });
|
|
710
|
+
} else {
|
|
711
|
+
console.warn("[useCookies] setField is only available for object states. Use setState instead.");
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
[isObjectState, updateState]
|
|
715
|
+
);
|
|
716
|
+
const resetState = useCallback(
|
|
717
|
+
(defaultState) => {
|
|
718
|
+
setStateInternal(defaultState ?? initialState);
|
|
719
|
+
removeCookie(key, { path: cookieOptions.path, domain: cookieOptions.domain });
|
|
720
|
+
},
|
|
721
|
+
[initialState, key, cookieOptions.path, cookieOptions.domain]
|
|
722
|
+
);
|
|
723
|
+
return useMemo(
|
|
724
|
+
() => ({
|
|
725
|
+
state,
|
|
726
|
+
setState: updateState,
|
|
727
|
+
setField: updateField,
|
|
728
|
+
resetState
|
|
729
|
+
}),
|
|
730
|
+
[state, updateState, updateField, resetState]
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
function useDoubleClick({ click, doubleClick, timeout = 250 }) {
|
|
734
|
+
const clickTimeout = useRef(null);
|
|
735
|
+
const clearClickTimeout = useCallback(() => {
|
|
736
|
+
if (clickTimeout.current) {
|
|
737
|
+
clearTimeout(clickTimeout.current);
|
|
738
|
+
clickTimeout.current = null;
|
|
739
|
+
}
|
|
740
|
+
}, []);
|
|
741
|
+
const handleEvent = useCallback(
|
|
742
|
+
(event) => {
|
|
743
|
+
clearClickTimeout();
|
|
744
|
+
if (click && event.detail === 1) {
|
|
745
|
+
clickTimeout.current = setTimeout(() => {
|
|
746
|
+
click(event);
|
|
747
|
+
}, timeout);
|
|
748
|
+
}
|
|
749
|
+
if (event.detail % 2 === 0) {
|
|
750
|
+
doubleClick(event);
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
[click, doubleClick, timeout, clearClickTimeout]
|
|
754
|
+
);
|
|
755
|
+
useEffect(
|
|
756
|
+
() => () => {
|
|
757
|
+
clearClickTimeout();
|
|
758
|
+
},
|
|
759
|
+
[clearClickTimeout]
|
|
760
|
+
);
|
|
761
|
+
return handleEvent;
|
|
762
|
+
}
|
|
763
|
+
function parseThreshold(input) {
|
|
764
|
+
if (typeof input === "string") {
|
|
765
|
+
if (!input.endsWith("%")) {
|
|
766
|
+
throw new Error('[useBackToTop] String threshold must end with % (e.g., "90%")');
|
|
767
|
+
}
|
|
768
|
+
const value = Number(input.slice(0, -1));
|
|
769
|
+
if (isNaN(value)) {
|
|
770
|
+
throw new Error("[useBackToTop] Invalid percentage value");
|
|
771
|
+
}
|
|
772
|
+
return { value, type: "percentage" };
|
|
773
|
+
}
|
|
774
|
+
return { value: input, type: "pixels" };
|
|
775
|
+
}
|
|
776
|
+
function useBackToTop(threshold = "90%", debounce = false) {
|
|
777
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
778
|
+
const parsedThreshold = useMemo(() => parseThreshold(threshold), [threshold]);
|
|
779
|
+
const handleScroll = useCallback(() => {
|
|
780
|
+
if (typeof window === "undefined") return;
|
|
781
|
+
const windowHeight = window.innerHeight;
|
|
782
|
+
const scrollY = Math.round(window.scrollY);
|
|
783
|
+
const documentHeight = document.body.offsetHeight;
|
|
784
|
+
const maxScroll = documentHeight - windowHeight;
|
|
785
|
+
const scrollProgress = maxScroll > 0 ? Math.round(scrollY / maxScroll * 100) : 0;
|
|
786
|
+
if (parsedThreshold.type === "percentage") {
|
|
787
|
+
setIsVisible(scrollProgress >= parsedThreshold.value);
|
|
788
|
+
} else {
|
|
789
|
+
const distanceFromBottom = documentHeight - windowHeight - scrollY;
|
|
790
|
+
setIsVisible(distanceFromBottom <= parsedThreshold.value);
|
|
791
|
+
}
|
|
792
|
+
}, [parsedThreshold]);
|
|
793
|
+
const debounceTimeoutRef = useRef(null);
|
|
794
|
+
const debouncedHandleScroll = useCallback(() => {
|
|
795
|
+
if (debounceTimeoutRef.current !== null) clearTimeout(debounceTimeoutRef.current);
|
|
796
|
+
debounceTimeoutRef.current = setTimeout(handleScroll, 100);
|
|
797
|
+
}, [handleScroll]);
|
|
798
|
+
useEffect(() => {
|
|
799
|
+
if (typeof window === "undefined") return void 0;
|
|
800
|
+
const scrollHandler = debounce ? debouncedHandleScroll : handleScroll;
|
|
801
|
+
handleScroll();
|
|
802
|
+
window.addEventListener("scroll", scrollHandler, { passive: true });
|
|
803
|
+
return () => {
|
|
804
|
+
window.removeEventListener("scroll", scrollHandler);
|
|
805
|
+
if (debounceTimeoutRef.current !== null) clearTimeout(debounceTimeoutRef.current);
|
|
806
|
+
};
|
|
807
|
+
}, [debounce, debouncedHandleScroll, handleScroll]);
|
|
808
|
+
const onBackToTop = useCallback(() => {
|
|
809
|
+
if (typeof window === "undefined") return;
|
|
810
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
811
|
+
}, []);
|
|
812
|
+
return { isVisible, onBackToTop, setIsVisible };
|
|
813
|
+
}
|
|
814
|
+
function useCountdownSeconds(initialSeconds) {
|
|
815
|
+
const [value, setValue] = useState(initialSeconds);
|
|
816
|
+
const [isCounting, setIsCounting] = useState(false);
|
|
817
|
+
const start = useCallback(() => {
|
|
818
|
+
setIsCounting(true);
|
|
819
|
+
}, []);
|
|
820
|
+
const reset = useCallback(() => {
|
|
821
|
+
setIsCounting(false);
|
|
822
|
+
setValue(initialSeconds);
|
|
823
|
+
}, [initialSeconds]);
|
|
824
|
+
useEffect(() => {
|
|
825
|
+
if (!isCounting) return void 0;
|
|
826
|
+
const intervalId = setInterval(() => {
|
|
827
|
+
setValue((prev) => {
|
|
828
|
+
if (prev <= 1) {
|
|
829
|
+
setIsCounting(false);
|
|
830
|
+
return 0;
|
|
831
|
+
}
|
|
832
|
+
return prev - 1;
|
|
833
|
+
});
|
|
834
|
+
}, 1e3);
|
|
835
|
+
return () => {
|
|
836
|
+
clearInterval(intervalId);
|
|
837
|
+
};
|
|
838
|
+
}, [isCounting]);
|
|
839
|
+
return useMemo(
|
|
840
|
+
() => ({
|
|
841
|
+
value,
|
|
842
|
+
setValue,
|
|
843
|
+
isCounting,
|
|
844
|
+
start,
|
|
845
|
+
reset
|
|
846
|
+
}),
|
|
847
|
+
[value, isCounting, start, reset]
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
function formatTime(value) {
|
|
851
|
+
return String(value).length === 1 ? `0${value}` : `${value}`;
|
|
852
|
+
}
|
|
853
|
+
function calculateTimeDifference(futureDate, currentDate) {
|
|
854
|
+
const distance = futureDate.getTime() - currentDate.getTime();
|
|
855
|
+
if (distance < 0) {
|
|
856
|
+
return { days: 0, hours: 0, minutes: 0, seconds: 0 };
|
|
857
|
+
}
|
|
858
|
+
return {
|
|
859
|
+
days: Math.floor(distance / (1e3 * 60 * 60 * 24)),
|
|
860
|
+
hours: Math.floor(distance % (1e3 * 60 * 60 * 24) / (1e3 * 60 * 60)),
|
|
861
|
+
minutes: Math.floor(distance % (1e3 * 60 * 60) / (1e3 * 60)),
|
|
862
|
+
seconds: Math.floor(distance % (1e3 * 60) / 1e3)
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
function useCountdownDate(targetDate, placeholder = "- -") {
|
|
866
|
+
const [value, setValue] = useState({
|
|
867
|
+
days: placeholder,
|
|
868
|
+
hours: placeholder,
|
|
869
|
+
minutes: placeholder,
|
|
870
|
+
seconds: placeholder
|
|
871
|
+
});
|
|
872
|
+
const handleUpdate = useCallback(() => {
|
|
873
|
+
const now = /* @__PURE__ */ new Date();
|
|
874
|
+
const { days, hours, minutes, seconds } = calculateTimeDifference(targetDate, now);
|
|
875
|
+
setValue({
|
|
876
|
+
days: formatTime(days),
|
|
877
|
+
hours: formatTime(hours),
|
|
878
|
+
minutes: formatTime(minutes),
|
|
879
|
+
seconds: formatTime(seconds)
|
|
880
|
+
});
|
|
881
|
+
}, [targetDate]);
|
|
882
|
+
useEffect(() => {
|
|
883
|
+
handleUpdate();
|
|
884
|
+
const interval = setInterval(handleUpdate, 1e3);
|
|
885
|
+
return () => clearInterval(interval);
|
|
886
|
+
}, [handleUpdate]);
|
|
887
|
+
return value;
|
|
888
|
+
}
|
|
889
|
+
function useScrollOffsetTop(defaultValue = 0) {
|
|
890
|
+
const elementRef = useRef(null);
|
|
891
|
+
const [offsetTop, setOffsetTop] = useState(false);
|
|
892
|
+
const handleScroll = useCallback(() => {
|
|
893
|
+
if (typeof window === "undefined") return;
|
|
894
|
+
const windowScrollY = window.scrollY;
|
|
895
|
+
if (elementRef.current) {
|
|
896
|
+
const elementOffsetTop = elementRef.current.offsetTop;
|
|
897
|
+
setOffsetTop(windowScrollY > elementOffsetTop - defaultValue);
|
|
898
|
+
} else {
|
|
899
|
+
setOffsetTop(windowScrollY > defaultValue);
|
|
900
|
+
}
|
|
901
|
+
}, [defaultValue]);
|
|
902
|
+
useEffect(() => {
|
|
903
|
+
if (typeof window === "undefined") return void 0;
|
|
904
|
+
handleScroll();
|
|
905
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
906
|
+
return () => {
|
|
907
|
+
window.removeEventListener("scroll", handleScroll);
|
|
908
|
+
};
|
|
909
|
+
}, [handleScroll]);
|
|
910
|
+
return useMemo(
|
|
911
|
+
() => ({
|
|
912
|
+
elementRef,
|
|
913
|
+
offsetTop
|
|
914
|
+
}),
|
|
915
|
+
[offsetTop]
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
var DEFAULT_PARAM_NAMES = {
|
|
919
|
+
preset: "preset",
|
|
920
|
+
mode: "mode",
|
|
921
|
+
primary: "primary",
|
|
922
|
+
direction: "dir",
|
|
923
|
+
density: "density",
|
|
924
|
+
contrast: "contrast"
|
|
925
|
+
};
|
|
926
|
+
var VALID_MODES = ["light", "dark", "system"];
|
|
927
|
+
var VALID_DIRECTIONS = ["ltr", "rtl"];
|
|
928
|
+
var VALID_DENSITIES = ["compact", "standard", "comfortable"];
|
|
929
|
+
var VALID_CONTRASTS = ["default", "high"];
|
|
930
|
+
function isValidPreset(value) {
|
|
931
|
+
return value !== null && PRESET_NAMES.includes(value);
|
|
932
|
+
}
|
|
933
|
+
function isValidMode(value) {
|
|
934
|
+
return value !== null && VALID_MODES.includes(value);
|
|
935
|
+
}
|
|
936
|
+
function isValidDirection(value) {
|
|
937
|
+
return value !== null && VALID_DIRECTIONS.includes(value);
|
|
938
|
+
}
|
|
939
|
+
function isValidDensity(value) {
|
|
940
|
+
return value !== null && VALID_DENSITIES.includes(value);
|
|
941
|
+
}
|
|
942
|
+
function isValidContrast(value) {
|
|
943
|
+
return value !== null && VALID_CONTRASTS.includes(value);
|
|
944
|
+
}
|
|
945
|
+
function isValidHexColor(value) {
|
|
946
|
+
if (!value) return false;
|
|
947
|
+
return /^#[0-9A-Fa-f]{6}$/.test(value);
|
|
948
|
+
}
|
|
949
|
+
function useConfigFromQuery(options = {}) {
|
|
950
|
+
const { paramNames = DEFAULT_PARAM_NAMES, onConfigChange, syncToUrl = false } = options;
|
|
951
|
+
const params = { ...DEFAULT_PARAM_NAMES, ...paramNames };
|
|
952
|
+
const configFromUrl = useMemo(() => {
|
|
953
|
+
if (typeof window === "undefined") return {};
|
|
954
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
955
|
+
const config = {};
|
|
956
|
+
const presetValue = searchParams.get(params.preset);
|
|
957
|
+
if (isValidPreset(presetValue)) {
|
|
958
|
+
config.preset = presetValue;
|
|
959
|
+
}
|
|
960
|
+
const modeValue = searchParams.get(params.mode);
|
|
961
|
+
if (isValidMode(modeValue)) {
|
|
962
|
+
config.mode = modeValue;
|
|
963
|
+
}
|
|
964
|
+
const primaryValue = searchParams.get(params.primary);
|
|
965
|
+
if (isValidHexColor(primaryValue)) {
|
|
966
|
+
config.primary = primaryValue;
|
|
967
|
+
}
|
|
968
|
+
const directionValue = searchParams.get(params.direction);
|
|
969
|
+
if (isValidDirection(directionValue)) {
|
|
970
|
+
config.direction = directionValue;
|
|
971
|
+
}
|
|
972
|
+
const densityValue = searchParams.get(params.density);
|
|
973
|
+
if (isValidDensity(densityValue)) {
|
|
974
|
+
config.density = densityValue;
|
|
975
|
+
}
|
|
976
|
+
const contrastValue = searchParams.get(params.contrast);
|
|
977
|
+
if (isValidContrast(contrastValue)) {
|
|
978
|
+
config.contrast = contrastValue;
|
|
979
|
+
}
|
|
980
|
+
return config;
|
|
981
|
+
}, [params.preset, params.mode, params.primary, params.direction, params.density, params.contrast]);
|
|
982
|
+
useEffect(() => {
|
|
983
|
+
if (onConfigChange && Object.keys(configFromUrl).length > 0) {
|
|
984
|
+
onConfigChange(configFromUrl);
|
|
985
|
+
}
|
|
986
|
+
}, [configFromUrl, onConfigChange]);
|
|
987
|
+
const updateUrl = useCallback(
|
|
988
|
+
(values) => {
|
|
989
|
+
if (typeof window === "undefined") return;
|
|
990
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
991
|
+
if (values.preset !== void 0) {
|
|
992
|
+
if (values.preset) {
|
|
993
|
+
searchParams.set(params.preset, values.preset);
|
|
994
|
+
} else {
|
|
995
|
+
searchParams.delete(params.preset);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (values.mode !== void 0) {
|
|
999
|
+
if (values.mode) {
|
|
1000
|
+
searchParams.set(params.mode, values.mode);
|
|
1001
|
+
} else {
|
|
1002
|
+
searchParams.delete(params.mode);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
if (values.primary !== void 0) {
|
|
1006
|
+
if (values.primary) {
|
|
1007
|
+
searchParams.set(params.primary, values.primary);
|
|
1008
|
+
} else {
|
|
1009
|
+
searchParams.delete(params.primary);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if (values.direction !== void 0) {
|
|
1013
|
+
if (values.direction) {
|
|
1014
|
+
searchParams.set(params.direction, values.direction);
|
|
1015
|
+
} else {
|
|
1016
|
+
searchParams.delete(params.direction);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
if (values.density !== void 0) {
|
|
1020
|
+
if (values.density) {
|
|
1021
|
+
searchParams.set(params.density, values.density);
|
|
1022
|
+
} else {
|
|
1023
|
+
searchParams.delete(params.density);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
if (values.contrast !== void 0) {
|
|
1027
|
+
if (values.contrast) {
|
|
1028
|
+
searchParams.set(params.contrast, values.contrast);
|
|
1029
|
+
} else {
|
|
1030
|
+
searchParams.delete(params.contrast);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
const newUrl = searchParams.toString() ? `${window.location.pathname}?${searchParams.toString()}` : window.location.pathname;
|
|
1034
|
+
window.history.replaceState({}, "", newUrl);
|
|
1035
|
+
},
|
|
1036
|
+
[params]
|
|
1037
|
+
);
|
|
1038
|
+
const clearUrl = useCallback(() => {
|
|
1039
|
+
if (typeof window === "undefined") return;
|
|
1040
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
1041
|
+
searchParams.delete(params.preset);
|
|
1042
|
+
searchParams.delete(params.mode);
|
|
1043
|
+
searchParams.delete(params.primary);
|
|
1044
|
+
searchParams.delete(params.direction);
|
|
1045
|
+
searchParams.delete(params.density);
|
|
1046
|
+
searchParams.delete(params.contrast);
|
|
1047
|
+
const newUrl = searchParams.toString() ? `${window.location.pathname}?${searchParams.toString()}` : window.location.pathname;
|
|
1048
|
+
window.history.replaceState({}, "", newUrl);
|
|
1049
|
+
}, [params]);
|
|
1050
|
+
const generateUrl = useCallback(
|
|
1051
|
+
(values, baseUrl) => {
|
|
1052
|
+
const base = baseUrl ?? (typeof window !== "undefined" ? window.location.origin + window.location.pathname : "");
|
|
1053
|
+
const searchParams = new URLSearchParams();
|
|
1054
|
+
if (values.preset) {
|
|
1055
|
+
searchParams.set(params.preset, values.preset);
|
|
1056
|
+
}
|
|
1057
|
+
if (values.mode) {
|
|
1058
|
+
searchParams.set(params.mode, values.mode);
|
|
1059
|
+
}
|
|
1060
|
+
if (values.primary) {
|
|
1061
|
+
searchParams.set(params.primary, values.primary);
|
|
1062
|
+
}
|
|
1063
|
+
if (values.direction) {
|
|
1064
|
+
searchParams.set(params.direction, values.direction);
|
|
1065
|
+
}
|
|
1066
|
+
if (values.density) {
|
|
1067
|
+
searchParams.set(params.density, values.density);
|
|
1068
|
+
}
|
|
1069
|
+
if (values.contrast) {
|
|
1070
|
+
searchParams.set(params.contrast, values.contrast);
|
|
1071
|
+
}
|
|
1072
|
+
const queryString = searchParams.toString();
|
|
1073
|
+
return queryString ? `${base}?${queryString}` : base;
|
|
1074
|
+
},
|
|
1075
|
+
[params]
|
|
1076
|
+
);
|
|
1077
|
+
const hasUrlConfig = Object.keys(configFromUrl).length > 0;
|
|
1078
|
+
return {
|
|
1079
|
+
configFromUrl,
|
|
1080
|
+
updateUrl,
|
|
1081
|
+
clearUrl,
|
|
1082
|
+
generateUrl,
|
|
1083
|
+
hasUrlConfig
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
function useImageDimensions(src, options = {}) {
|
|
1087
|
+
const { crossOrigin, timeout = 1e4, immediate = true } = options;
|
|
1088
|
+
const [dimensions, setDimensions] = useState(null);
|
|
1089
|
+
const [loading, setLoading] = useState(false);
|
|
1090
|
+
const [error, setError] = useState(null);
|
|
1091
|
+
const loadImage = useCallback(() => {
|
|
1092
|
+
if (!src) {
|
|
1093
|
+
setDimensions(null);
|
|
1094
|
+
setLoading(false);
|
|
1095
|
+
setError(null);
|
|
1096
|
+
return void 0;
|
|
1097
|
+
}
|
|
1098
|
+
setLoading(true);
|
|
1099
|
+
setError(null);
|
|
1100
|
+
const img = new Image();
|
|
1101
|
+
let timeoutId = null;
|
|
1102
|
+
let cancelled = false;
|
|
1103
|
+
const cleanup = () => {
|
|
1104
|
+
cancelled = true;
|
|
1105
|
+
if (timeoutId) {
|
|
1106
|
+
clearTimeout(timeoutId);
|
|
1107
|
+
}
|
|
1108
|
+
img.onload = null;
|
|
1109
|
+
img.onerror = null;
|
|
1110
|
+
};
|
|
1111
|
+
img.onload = () => {
|
|
1112
|
+
if (cancelled) return;
|
|
1113
|
+
cleanup();
|
|
1114
|
+
const { naturalWidth: width, naturalHeight: height } = img;
|
|
1115
|
+
setDimensions({
|
|
1116
|
+
width,
|
|
1117
|
+
height,
|
|
1118
|
+
aspectRatio: height > 0 ? width / height : 1
|
|
1119
|
+
});
|
|
1120
|
+
setLoading(false);
|
|
1121
|
+
setError(null);
|
|
1122
|
+
};
|
|
1123
|
+
img.onerror = () => {
|
|
1124
|
+
if (cancelled) return;
|
|
1125
|
+
cleanup();
|
|
1126
|
+
setDimensions(null);
|
|
1127
|
+
setLoading(false);
|
|
1128
|
+
setError(new Error(`Failed to load image: ${src}`));
|
|
1129
|
+
};
|
|
1130
|
+
timeoutId = setTimeout(() => {
|
|
1131
|
+
if (cancelled) return;
|
|
1132
|
+
cleanup();
|
|
1133
|
+
setDimensions(null);
|
|
1134
|
+
setLoading(false);
|
|
1135
|
+
setError(new Error(`Image load timeout: ${src}`));
|
|
1136
|
+
}, timeout);
|
|
1137
|
+
if (crossOrigin) {
|
|
1138
|
+
img.crossOrigin = crossOrigin;
|
|
1139
|
+
}
|
|
1140
|
+
img.src = src;
|
|
1141
|
+
return cleanup;
|
|
1142
|
+
}, [src, crossOrigin, timeout]);
|
|
1143
|
+
useEffect(() => {
|
|
1144
|
+
if (!immediate) return void 0;
|
|
1145
|
+
return loadImage();
|
|
1146
|
+
}, [loadImage, immediate]);
|
|
1147
|
+
const retry = useCallback(() => {
|
|
1148
|
+
loadImage();
|
|
1149
|
+
}, [loadImage]);
|
|
1150
|
+
return {
|
|
1151
|
+
dimensions,
|
|
1152
|
+
loading,
|
|
1153
|
+
error,
|
|
1154
|
+
retry
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
function useMultipleImageDimensions(urls, options = {}) {
|
|
1158
|
+
const { crossOrigin, timeout = 1e4, immediate = true } = options;
|
|
1159
|
+
const [results, setResults] = useState(/* @__PURE__ */ new Map());
|
|
1160
|
+
useEffect(() => {
|
|
1161
|
+
if (!immediate) return void 0;
|
|
1162
|
+
const cleanups = [];
|
|
1163
|
+
urls.forEach((url) => {
|
|
1164
|
+
if (!url) return;
|
|
1165
|
+
setResults((prev) => {
|
|
1166
|
+
const next = new Map(prev);
|
|
1167
|
+
next.set(url, { dimensions: null, loading: true, error: null });
|
|
1168
|
+
return next;
|
|
1169
|
+
});
|
|
1170
|
+
const img = new Image();
|
|
1171
|
+
let timeoutId = null;
|
|
1172
|
+
let cancelled = false;
|
|
1173
|
+
const cleanup = () => {
|
|
1174
|
+
cancelled = true;
|
|
1175
|
+
if (timeoutId) {
|
|
1176
|
+
clearTimeout(timeoutId);
|
|
1177
|
+
}
|
|
1178
|
+
img.onload = null;
|
|
1179
|
+
img.onerror = null;
|
|
1180
|
+
};
|
|
1181
|
+
cleanups.push(cleanup);
|
|
1182
|
+
img.onload = () => {
|
|
1183
|
+
if (cancelled) return;
|
|
1184
|
+
cleanup();
|
|
1185
|
+
const { naturalWidth: width, naturalHeight: height } = img;
|
|
1186
|
+
setResults((prev) => {
|
|
1187
|
+
const next = new Map(prev);
|
|
1188
|
+
next.set(url, {
|
|
1189
|
+
dimensions: { width, height, aspectRatio: height > 0 ? width / height : 1 },
|
|
1190
|
+
loading: false,
|
|
1191
|
+
error: null
|
|
1192
|
+
});
|
|
1193
|
+
return next;
|
|
1194
|
+
});
|
|
1195
|
+
};
|
|
1196
|
+
img.onerror = () => {
|
|
1197
|
+
if (cancelled) return;
|
|
1198
|
+
cleanup();
|
|
1199
|
+
setResults((prev) => {
|
|
1200
|
+
const next = new Map(prev);
|
|
1201
|
+
next.set(url, {
|
|
1202
|
+
dimensions: null,
|
|
1203
|
+
loading: false,
|
|
1204
|
+
error: new Error(`Failed to load: ${url}`)
|
|
1205
|
+
});
|
|
1206
|
+
return next;
|
|
1207
|
+
});
|
|
1208
|
+
};
|
|
1209
|
+
timeoutId = setTimeout(() => {
|
|
1210
|
+
if (cancelled) return;
|
|
1211
|
+
cleanup();
|
|
1212
|
+
setResults((prev) => {
|
|
1213
|
+
const next = new Map(prev);
|
|
1214
|
+
next.set(url, {
|
|
1215
|
+
dimensions: null,
|
|
1216
|
+
loading: false,
|
|
1217
|
+
error: new Error(`Timeout: ${url}`)
|
|
1218
|
+
});
|
|
1219
|
+
return next;
|
|
1220
|
+
});
|
|
1221
|
+
}, timeout);
|
|
1222
|
+
if (crossOrigin) {
|
|
1223
|
+
img.crossOrigin = crossOrigin;
|
|
1224
|
+
}
|
|
1225
|
+
img.src = url;
|
|
1226
|
+
});
|
|
1227
|
+
return () => {
|
|
1228
|
+
cleanups.forEach((cleanup) => cleanup());
|
|
1229
|
+
};
|
|
1230
|
+
}, [urls.join(","), crossOrigin, timeout, immediate]);
|
|
1231
|
+
return urls.map((url) => {
|
|
1232
|
+
if (!url) {
|
|
1233
|
+
return {
|
|
1234
|
+
dimensions: null,
|
|
1235
|
+
loading: false,
|
|
1236
|
+
error: null,
|
|
1237
|
+
retry: () => {
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
const result = results.get(url);
|
|
1242
|
+
return {
|
|
1243
|
+
dimensions: result?.dimensions ?? null,
|
|
1244
|
+
loading: result?.loading ?? false,
|
|
1245
|
+
error: result?.error ?? null,
|
|
1246
|
+
retry: () => {
|
|
1247
|
+
setResults((prev) => {
|
|
1248
|
+
const next = new Map(prev);
|
|
1249
|
+
next.delete(url);
|
|
1250
|
+
return next;
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
function usePasswordVisibility(options = {}) {
|
|
1257
|
+
const { initialVisible = false } = options;
|
|
1258
|
+
const [visible, setVisible] = useState(initialVisible);
|
|
1259
|
+
const toggle = useCallback(() => {
|
|
1260
|
+
setVisible((prev) => !prev);
|
|
1261
|
+
}, []);
|
|
1262
|
+
const show = useCallback(() => {
|
|
1263
|
+
setVisible(true);
|
|
1264
|
+
}, []);
|
|
1265
|
+
const hide = useCallback(() => {
|
|
1266
|
+
setVisible(false);
|
|
1267
|
+
}, []);
|
|
1268
|
+
return useMemo(
|
|
1269
|
+
() => ({
|
|
1270
|
+
visible,
|
|
1271
|
+
toggle,
|
|
1272
|
+
show,
|
|
1273
|
+
hide,
|
|
1274
|
+
type: visible ? "text" : "password"
|
|
1275
|
+
}),
|
|
1276
|
+
[visible, toggle, show, hide]
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
function useThrottle(value, delay, options = {}) {
|
|
1280
|
+
const { leading = true, trailing = true } = options;
|
|
1281
|
+
const [throttledValue, setThrottledValue] = useState(value);
|
|
1282
|
+
const lastExecuted = useRef(0);
|
|
1283
|
+
const timeoutRef = useRef(null);
|
|
1284
|
+
const lastValue = useRef(value);
|
|
1285
|
+
useEffect(() => {
|
|
1286
|
+
lastValue.current = value;
|
|
1287
|
+
const now = Date.now();
|
|
1288
|
+
const elapsed = now - lastExecuted.current;
|
|
1289
|
+
const execute = () => {
|
|
1290
|
+
lastExecuted.current = Date.now();
|
|
1291
|
+
setThrottledValue(lastValue.current);
|
|
1292
|
+
};
|
|
1293
|
+
if (timeoutRef.current) {
|
|
1294
|
+
clearTimeout(timeoutRef.current);
|
|
1295
|
+
timeoutRef.current = null;
|
|
1296
|
+
}
|
|
1297
|
+
if (elapsed >= delay) {
|
|
1298
|
+
if (leading) {
|
|
1299
|
+
execute();
|
|
1300
|
+
} else if (trailing) {
|
|
1301
|
+
timeoutRef.current = setTimeout(execute, delay);
|
|
1302
|
+
}
|
|
1303
|
+
} else if (trailing) {
|
|
1304
|
+
timeoutRef.current = setTimeout(execute, delay - elapsed);
|
|
1305
|
+
}
|
|
1306
|
+
return () => {
|
|
1307
|
+
if (timeoutRef.current) {
|
|
1308
|
+
clearTimeout(timeoutRef.current);
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
}, [value, delay, leading, trailing]);
|
|
1312
|
+
return throttledValue;
|
|
1313
|
+
}
|
|
1314
|
+
function useThrottleCallback(callback, delay, options = {}) {
|
|
1315
|
+
const { leading = true, trailing = true } = options;
|
|
1316
|
+
const lastExecuted = useRef(0);
|
|
1317
|
+
const timeoutRef = useRef(null);
|
|
1318
|
+
const lastArgs = useRef(null);
|
|
1319
|
+
const callbackRef = useRef(callback);
|
|
1320
|
+
useEffect(() => {
|
|
1321
|
+
callbackRef.current = callback;
|
|
1322
|
+
}, [callback]);
|
|
1323
|
+
useEffect(
|
|
1324
|
+
() => () => {
|
|
1325
|
+
if (timeoutRef.current) {
|
|
1326
|
+
clearTimeout(timeoutRef.current);
|
|
1327
|
+
}
|
|
1328
|
+
},
|
|
1329
|
+
[]
|
|
1330
|
+
);
|
|
1331
|
+
return useCallback(
|
|
1332
|
+
((...args) => {
|
|
1333
|
+
lastArgs.current = args;
|
|
1334
|
+
const now = Date.now();
|
|
1335
|
+
const elapsed = now - lastExecuted.current;
|
|
1336
|
+
const execute = () => {
|
|
1337
|
+
lastExecuted.current = Date.now();
|
|
1338
|
+
if (lastArgs.current) {
|
|
1339
|
+
callbackRef.current(...lastArgs.current);
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
if (timeoutRef.current) {
|
|
1343
|
+
clearTimeout(timeoutRef.current);
|
|
1344
|
+
timeoutRef.current = null;
|
|
1345
|
+
}
|
|
1346
|
+
if (elapsed >= delay) {
|
|
1347
|
+
if (leading) {
|
|
1348
|
+
execute();
|
|
1349
|
+
} else if (trailing) {
|
|
1350
|
+
timeoutRef.current = setTimeout(execute, delay);
|
|
1351
|
+
}
|
|
1352
|
+
} else if (trailing) {
|
|
1353
|
+
timeoutRef.current = setTimeout(execute, delay - elapsed);
|
|
1354
|
+
}
|
|
1355
|
+
}),
|
|
1356
|
+
[delay, leading, trailing]
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
function subscribe(callback) {
|
|
1360
|
+
window.addEventListener("online", callback);
|
|
1361
|
+
window.addEventListener("offline", callback);
|
|
1362
|
+
return () => {
|
|
1363
|
+
window.removeEventListener("online", callback);
|
|
1364
|
+
window.removeEventListener("offline", callback);
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
function getSnapshot() {
|
|
1368
|
+
return navigator.onLine;
|
|
1369
|
+
}
|
|
1370
|
+
function getServerSnapshot() {
|
|
1371
|
+
return true;
|
|
1372
|
+
}
|
|
1373
|
+
function useOnlineStatus() {
|
|
1374
|
+
const isOnline = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
1375
|
+
const [lastChanged, setLastChanged] = useState(null);
|
|
1376
|
+
const handleStatusChange = useCallback(() => {
|
|
1377
|
+
setLastChanged(Date.now());
|
|
1378
|
+
}, []);
|
|
1379
|
+
useEffect(() => {
|
|
1380
|
+
window.addEventListener("online", handleStatusChange);
|
|
1381
|
+
window.addEventListener("offline", handleStatusChange);
|
|
1382
|
+
return () => {
|
|
1383
|
+
window.removeEventListener("online", handleStatusChange);
|
|
1384
|
+
window.removeEventListener("offline", handleStatusChange);
|
|
1385
|
+
};
|
|
1386
|
+
}, [handleStatusChange]);
|
|
1387
|
+
return {
|
|
1388
|
+
isOnline,
|
|
1389
|
+
isOffline: !isOnline,
|
|
1390
|
+
lastChanged
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
function useIntersectionObserver(options = {}) {
|
|
1394
|
+
const { root = null, rootMargin = "0px", threshold = 0, triggerOnce = false, enabled = true } = options;
|
|
1395
|
+
const [entry, setEntry] = useState(null);
|
|
1396
|
+
const elementRef = useRef(null);
|
|
1397
|
+
const observerRef = useRef(null);
|
|
1398
|
+
const frozenRef = useRef(false);
|
|
1399
|
+
const disconnect = useCallback(() => {
|
|
1400
|
+
if (observerRef.current) {
|
|
1401
|
+
observerRef.current.disconnect();
|
|
1402
|
+
observerRef.current = null;
|
|
1403
|
+
}
|
|
1404
|
+
}, []);
|
|
1405
|
+
const ref = useCallback(
|
|
1406
|
+
(element) => {
|
|
1407
|
+
disconnect();
|
|
1408
|
+
elementRef.current = element;
|
|
1409
|
+
if (!enabled || frozenRef.current || !element) {
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
1413
|
+
setEntry({
|
|
1414
|
+
isIntersecting: true,
|
|
1415
|
+
intersectionRatio: 1,
|
|
1416
|
+
boundingClientRect: element.getBoundingClientRect(),
|
|
1417
|
+
intersectionRect: element.getBoundingClientRect(),
|
|
1418
|
+
rootBounds: null,
|
|
1419
|
+
target: element,
|
|
1420
|
+
time: Date.now()
|
|
1421
|
+
});
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
observerRef.current = new IntersectionObserver(
|
|
1425
|
+
([observerEntry]) => {
|
|
1426
|
+
setEntry(observerEntry);
|
|
1427
|
+
if (triggerOnce && observerEntry.isIntersecting) {
|
|
1428
|
+
frozenRef.current = true;
|
|
1429
|
+
disconnect();
|
|
1430
|
+
}
|
|
1431
|
+
},
|
|
1432
|
+
{ root, rootMargin, threshold }
|
|
1433
|
+
);
|
|
1434
|
+
observerRef.current.observe(element);
|
|
1435
|
+
},
|
|
1436
|
+
[enabled, root, rootMargin, threshold, triggerOnce, disconnect]
|
|
1437
|
+
);
|
|
1438
|
+
useEffect(
|
|
1439
|
+
() => () => {
|
|
1440
|
+
disconnect();
|
|
1441
|
+
},
|
|
1442
|
+
[disconnect]
|
|
1443
|
+
);
|
|
1444
|
+
return useMemo(
|
|
1445
|
+
() => ({
|
|
1446
|
+
ref,
|
|
1447
|
+
entry,
|
|
1448
|
+
isIntersecting: entry?.isIntersecting ?? false,
|
|
1449
|
+
disconnect
|
|
1450
|
+
}),
|
|
1451
|
+
[entry, disconnect]
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
function isInputElement(element) {
|
|
1455
|
+
if (!element || !(element instanceof HTMLElement)) {
|
|
1456
|
+
return false;
|
|
1457
|
+
}
|
|
1458
|
+
const tagName = element.tagName.toLowerCase();
|
|
1459
|
+
if (tagName === "input" || tagName === "textarea" || tagName === "select") {
|
|
1460
|
+
return true;
|
|
1461
|
+
}
|
|
1462
|
+
return element.isContentEditable;
|
|
1463
|
+
}
|
|
1464
|
+
function matchesShortcut(event, shortcut) {
|
|
1465
|
+
const eventKey = event.key.toLowerCase();
|
|
1466
|
+
const shortcutKey = shortcut.key.toLowerCase();
|
|
1467
|
+
if (eventKey !== shortcutKey) {
|
|
1468
|
+
return false;
|
|
1469
|
+
}
|
|
1470
|
+
const ctrlOrMeta = event.ctrlKey || event.metaKey;
|
|
1471
|
+
if (shortcut.ctrl && !ctrlOrMeta) return false;
|
|
1472
|
+
if (shortcut.meta && !event.metaKey) return false;
|
|
1473
|
+
if (shortcut.shift && !event.shiftKey) return false;
|
|
1474
|
+
if (shortcut.alt && !event.altKey) return false;
|
|
1475
|
+
if (!shortcut.ctrl && !shortcut.meta && ctrlOrMeta) return false;
|
|
1476
|
+
if (!shortcut.shift && event.shiftKey) return false;
|
|
1477
|
+
if (!shortcut.alt && event.altKey) return false;
|
|
1478
|
+
return true;
|
|
1479
|
+
}
|
|
1480
|
+
function useKeyboardShortcut(shortcut, callback, options = {}) {
|
|
1481
|
+
const {
|
|
1482
|
+
enabled = true,
|
|
1483
|
+
preventDefault = true,
|
|
1484
|
+
stopPropagation = false,
|
|
1485
|
+
target = typeof document !== "undefined" ? document : null,
|
|
1486
|
+
event = "keydown",
|
|
1487
|
+
ignoreInputs = false
|
|
1488
|
+
} = options;
|
|
1489
|
+
const callbackRef = useRef(callback);
|
|
1490
|
+
useEffect(() => {
|
|
1491
|
+
callbackRef.current = callback;
|
|
1492
|
+
}, [callback]);
|
|
1493
|
+
const handleKeyEvent = useCallback(
|
|
1494
|
+
(e) => {
|
|
1495
|
+
if (!enabled) return;
|
|
1496
|
+
const keyEvent = e;
|
|
1497
|
+
if (ignoreInputs && isInputElement(keyEvent.target)) {
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
if (matchesShortcut(keyEvent, shortcut)) {
|
|
1501
|
+
if (preventDefault) {
|
|
1502
|
+
keyEvent.preventDefault();
|
|
1503
|
+
}
|
|
1504
|
+
if (stopPropagation) {
|
|
1505
|
+
keyEvent.stopPropagation();
|
|
1506
|
+
}
|
|
1507
|
+
callbackRef.current(keyEvent);
|
|
1508
|
+
}
|
|
1509
|
+
},
|
|
1510
|
+
[enabled, shortcut, preventDefault, stopPropagation, ignoreInputs]
|
|
1511
|
+
);
|
|
1512
|
+
useEffect(() => {
|
|
1513
|
+
if (!target || !enabled) return void 0;
|
|
1514
|
+
target.addEventListener(event, handleKeyEvent);
|
|
1515
|
+
return () => {
|
|
1516
|
+
target.removeEventListener(event, handleKeyEvent);
|
|
1517
|
+
};
|
|
1518
|
+
}, [target, event, handleKeyEvent, enabled]);
|
|
1519
|
+
}
|
|
1520
|
+
function useEscapeKey(callback, options = {}) {
|
|
1521
|
+
useKeyboardShortcut({ key: "Escape" }, callback, {
|
|
1522
|
+
...options,
|
|
1523
|
+
ignoreInputs: false
|
|
1524
|
+
// Escape should work even in inputs
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
function useEnterKey(callback, options = {}) {
|
|
1528
|
+
useKeyboardShortcut({ key: "Enter" }, callback, options);
|
|
1529
|
+
}
|
|
1530
|
+
var FOCUSABLE_SELECTOR = [
|
|
1531
|
+
"a[href]",
|
|
1532
|
+
"button:not([disabled])",
|
|
1533
|
+
'input:not([disabled]):not([type="hidden"])',
|
|
1534
|
+
"select:not([disabled])",
|
|
1535
|
+
"textarea:not([disabled])",
|
|
1536
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
1537
|
+
'[contenteditable="true"]'
|
|
1538
|
+
].join(",");
|
|
1539
|
+
function useFocusTrap(options = {}) {
|
|
1540
|
+
const { enabled = true, autoFocus = true, restoreFocus = true, focusableSelector = FOCUSABLE_SELECTOR } = options;
|
|
1541
|
+
const containerRef = useRef(null);
|
|
1542
|
+
const previousActiveElement = useRef(null);
|
|
1543
|
+
const getFocusableElements = useCallback(() => {
|
|
1544
|
+
if (!containerRef.current) return [];
|
|
1545
|
+
return Array.from(containerRef.current.querySelectorAll(focusableSelector)).filter(
|
|
1546
|
+
(el) => el.offsetParent !== null
|
|
1547
|
+
);
|
|
1548
|
+
}, [focusableSelector]);
|
|
1549
|
+
const focusFirst = useCallback(() => {
|
|
1550
|
+
const elements = getFocusableElements();
|
|
1551
|
+
if (elements.length > 0) {
|
|
1552
|
+
elements[0].focus();
|
|
1553
|
+
}
|
|
1554
|
+
}, [getFocusableElements]);
|
|
1555
|
+
const focusLast = useCallback(() => {
|
|
1556
|
+
const elements = getFocusableElements();
|
|
1557
|
+
if (elements.length > 0) {
|
|
1558
|
+
elements[elements.length - 1].focus();
|
|
1559
|
+
}
|
|
1560
|
+
}, [getFocusableElements]);
|
|
1561
|
+
useEffect(() => {
|
|
1562
|
+
if (!enabled || !containerRef.current) return void 0;
|
|
1563
|
+
const handleKeyDown = (event) => {
|
|
1564
|
+
if (event.key !== "Tab") return;
|
|
1565
|
+
const elements = getFocusableElements();
|
|
1566
|
+
if (elements.length === 0) return;
|
|
1567
|
+
const firstElement = elements[0];
|
|
1568
|
+
const lastElement = elements[elements.length - 1];
|
|
1569
|
+
const activeElement = document.activeElement;
|
|
1570
|
+
if (event.shiftKey && activeElement === firstElement) {
|
|
1571
|
+
event.preventDefault();
|
|
1572
|
+
lastElement.focus();
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
if (!event.shiftKey && activeElement === lastElement) {
|
|
1576
|
+
event.preventDefault();
|
|
1577
|
+
firstElement.focus();
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
const container = containerRef.current;
|
|
1581
|
+
container.addEventListener("keydown", handleKeyDown);
|
|
1582
|
+
return () => {
|
|
1583
|
+
container.removeEventListener("keydown", handleKeyDown);
|
|
1584
|
+
};
|
|
1585
|
+
}, [enabled, getFocusableElements]);
|
|
1586
|
+
useEffect(() => {
|
|
1587
|
+
if (enabled) {
|
|
1588
|
+
if (restoreFocus) {
|
|
1589
|
+
previousActiveElement.current = document.activeElement;
|
|
1590
|
+
}
|
|
1591
|
+
if (autoFocus) {
|
|
1592
|
+
const timeoutId = setTimeout(focusFirst, 0);
|
|
1593
|
+
return () => clearTimeout(timeoutId);
|
|
1594
|
+
}
|
|
1595
|
+
} else if (restoreFocus && previousActiveElement.current) {
|
|
1596
|
+
previousActiveElement.current.focus();
|
|
1597
|
+
previousActiveElement.current = null;
|
|
1598
|
+
}
|
|
1599
|
+
return void 0;
|
|
1600
|
+
}, [enabled, autoFocus, restoreFocus, focusFirst]);
|
|
1601
|
+
const ref = useCallback((node) => {
|
|
1602
|
+
containerRef.current = node;
|
|
1603
|
+
}, []);
|
|
1604
|
+
return { ref, focusFirst, focusLast };
|
|
1605
|
+
}
|
|
1606
|
+
function useUpdateEffect(effect, deps) {
|
|
1607
|
+
const isFirstMount = useRef(true);
|
|
1608
|
+
useEffect(() => {
|
|
1609
|
+
if (isFirstMount.current) {
|
|
1610
|
+
isFirstMount.current = false;
|
|
1611
|
+
return void 0;
|
|
1612
|
+
}
|
|
1613
|
+
return effect();
|
|
1614
|
+
}, deps);
|
|
1615
|
+
}
|
|
1616
|
+
function useUpdateLayoutEffect(effect, deps) {
|
|
1617
|
+
const isFirstMount = useRef(true);
|
|
1618
|
+
useEffect(() => {
|
|
1619
|
+
if (isFirstMount.current) {
|
|
1620
|
+
isFirstMount.current = false;
|
|
1621
|
+
return void 0;
|
|
1622
|
+
}
|
|
1623
|
+
return effect();
|
|
1624
|
+
}, deps);
|
|
1625
|
+
}
|
|
1626
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
1627
|
+
function getStorageValue(key, defaultValue, deserializer) {
|
|
1628
|
+
if (typeof window === "undefined") {
|
|
1629
|
+
return defaultValue;
|
|
1630
|
+
}
|
|
1631
|
+
try {
|
|
1632
|
+
const item = window.sessionStorage.getItem(key);
|
|
1633
|
+
if (item === null) {
|
|
1634
|
+
return defaultValue;
|
|
1635
|
+
}
|
|
1636
|
+
return deserializer(item);
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
console.warn(`Error reading sessionStorage key "${key}":`, error);
|
|
1639
|
+
return defaultValue;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
function useSessionStorage(key, defaultValue, options = {}) {
|
|
1643
|
+
const { serializer = JSON.stringify, deserializer = JSON.parse } = options;
|
|
1644
|
+
const isClient = useIsClient();
|
|
1645
|
+
const [state, setStateInternal] = useState(() => {
|
|
1646
|
+
if (typeof window === "undefined") {
|
|
1647
|
+
return defaultValue;
|
|
1648
|
+
}
|
|
1649
|
+
return getStorageValue(key, defaultValue, deserializer);
|
|
1650
|
+
});
|
|
1651
|
+
const [hasValue, setHasValue] = useState(() => {
|
|
1652
|
+
if (typeof window === "undefined") {
|
|
1653
|
+
return false;
|
|
1654
|
+
}
|
|
1655
|
+
return window.sessionStorage.getItem(key) !== null;
|
|
1656
|
+
});
|
|
1657
|
+
const setState = useCallback(
|
|
1658
|
+
(value) => {
|
|
1659
|
+
try {
|
|
1660
|
+
const newValue = value instanceof Function ? value(state) : value;
|
|
1661
|
+
window.sessionStorage.setItem(key, serializer(newValue));
|
|
1662
|
+
setStateInternal(newValue);
|
|
1663
|
+
setHasValue(true);
|
|
1664
|
+
window.dispatchEvent(
|
|
1665
|
+
new StorageEvent("storage", {
|
|
1666
|
+
key,
|
|
1667
|
+
newValue: serializer(newValue),
|
|
1668
|
+
storageArea: window.sessionStorage
|
|
1669
|
+
})
|
|
1670
|
+
);
|
|
1671
|
+
} catch (error) {
|
|
1672
|
+
console.warn(`Error setting sessionStorage key "${key}":`, error);
|
|
1673
|
+
}
|
|
1674
|
+
},
|
|
1675
|
+
[key, serializer, state]
|
|
1676
|
+
);
|
|
1677
|
+
const remove = useCallback(() => {
|
|
1678
|
+
try {
|
|
1679
|
+
window.sessionStorage.removeItem(key);
|
|
1680
|
+
setStateInternal(defaultValue);
|
|
1681
|
+
setHasValue(false);
|
|
1682
|
+
window.dispatchEvent(
|
|
1683
|
+
new StorageEvent("storage", {
|
|
1684
|
+
key,
|
|
1685
|
+
newValue: null,
|
|
1686
|
+
storageArea: window.sessionStorage
|
|
1687
|
+
})
|
|
1688
|
+
);
|
|
1689
|
+
} catch (error) {
|
|
1690
|
+
console.warn(`Error removing sessionStorage key "${key}":`, error);
|
|
1691
|
+
}
|
|
1692
|
+
}, [key, defaultValue]);
|
|
1693
|
+
useEffect(() => {
|
|
1694
|
+
if (!isClient) return void 0;
|
|
1695
|
+
const value = getStorageValue(key, defaultValue, deserializer);
|
|
1696
|
+
setStateInternal(value);
|
|
1697
|
+
setHasValue(window.sessionStorage.getItem(key) !== null);
|
|
1698
|
+
return void 0;
|
|
1699
|
+
}, [key, isClient, defaultValue, deserializer]);
|
|
1700
|
+
useEffect(() => {
|
|
1701
|
+
if (!isClient) return void 0;
|
|
1702
|
+
const handleStorageChange = (event) => {
|
|
1703
|
+
if (event.key === key && event.storageArea === window.sessionStorage) {
|
|
1704
|
+
if (event.newValue === null) {
|
|
1705
|
+
setStateInternal(defaultValue);
|
|
1706
|
+
setHasValue(false);
|
|
1707
|
+
} else {
|
|
1708
|
+
try {
|
|
1709
|
+
setStateInternal(deserializer(event.newValue));
|
|
1710
|
+
setHasValue(true);
|
|
1711
|
+
} catch {
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
window.addEventListener("storage", handleStorageChange);
|
|
1717
|
+
return () => {
|
|
1718
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
1719
|
+
};
|
|
1720
|
+
}, [key, isClient, defaultValue, deserializer]);
|
|
1721
|
+
return {
|
|
1722
|
+
state,
|
|
1723
|
+
setState,
|
|
1724
|
+
remove,
|
|
1725
|
+
hasValue
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
function useArray(initialValue = []) {
|
|
1729
|
+
const [value, setValue] = useState(initialValue);
|
|
1730
|
+
const push = useCallback((item) => {
|
|
1731
|
+
setValue((arr) => [...arr, item]);
|
|
1732
|
+
}, []);
|
|
1733
|
+
const pushMany = useCallback((items) => {
|
|
1734
|
+
setValue((arr) => [...arr, ...items]);
|
|
1735
|
+
}, []);
|
|
1736
|
+
const remove = useCallback((itemOrPredicate) => {
|
|
1737
|
+
setValue((arr) => {
|
|
1738
|
+
const predicate = typeof itemOrPredicate === "function" ? itemOrPredicate : (item) => item === itemOrPredicate;
|
|
1739
|
+
const index = arr.findIndex(predicate);
|
|
1740
|
+
if (index === -1) return arr;
|
|
1741
|
+
return [...arr.slice(0, index), ...arr.slice(index + 1)];
|
|
1742
|
+
});
|
|
1743
|
+
}, []);
|
|
1744
|
+
const removeAt = useCallback((index) => {
|
|
1745
|
+
setValue((arr) => {
|
|
1746
|
+
if (index < 0 || index >= arr.length) return arr;
|
|
1747
|
+
return [...arr.slice(0, index), ...arr.slice(index + 1)];
|
|
1748
|
+
});
|
|
1749
|
+
}, []);
|
|
1750
|
+
const removeWhere = useCallback((predicate) => {
|
|
1751
|
+
setValue((arr) => arr.filter((item) => !predicate(item)));
|
|
1752
|
+
}, []);
|
|
1753
|
+
const updateAt = useCallback((index, item) => {
|
|
1754
|
+
setValue((arr) => {
|
|
1755
|
+
if (index < 0 || index >= arr.length) return arr;
|
|
1756
|
+
return [...arr.slice(0, index), item, ...arr.slice(index + 1)];
|
|
1757
|
+
});
|
|
1758
|
+
}, []);
|
|
1759
|
+
const updateAtPartial = useCallback((index, partial) => {
|
|
1760
|
+
setValue((arr) => {
|
|
1761
|
+
if (index < 0 || index >= arr.length) return arr;
|
|
1762
|
+
const item = arr[index];
|
|
1763
|
+
if (typeof item !== "object" || item === null) {
|
|
1764
|
+
console.warn("updateAtPartial only works with object items");
|
|
1765
|
+
return arr;
|
|
1766
|
+
}
|
|
1767
|
+
return [...arr.slice(0, index), { ...item, ...partial }, ...arr.slice(index + 1)];
|
|
1768
|
+
});
|
|
1769
|
+
}, []);
|
|
1770
|
+
const updateWhere = useCallback((predicate, update) => {
|
|
1771
|
+
setValue(
|
|
1772
|
+
(arr) => arr.map((item) => {
|
|
1773
|
+
if (!predicate(item)) return item;
|
|
1774
|
+
return typeof update === "function" ? update(item) : update;
|
|
1775
|
+
})
|
|
1776
|
+
);
|
|
1777
|
+
}, []);
|
|
1778
|
+
const insertAt = useCallback((index, item) => {
|
|
1779
|
+
setValue((arr) => {
|
|
1780
|
+
const normalizedIndex = Math.max(0, Math.min(index, arr.length));
|
|
1781
|
+
return [...arr.slice(0, normalizedIndex), item, ...arr.slice(normalizedIndex)];
|
|
1782
|
+
});
|
|
1783
|
+
}, []);
|
|
1784
|
+
const move = useCallback((fromIndex, toIndex) => {
|
|
1785
|
+
setValue((arr) => {
|
|
1786
|
+
if (fromIndex < 0 || fromIndex >= arr.length || toIndex < 0 || toIndex >= arr.length || fromIndex === toIndex) {
|
|
1787
|
+
return arr;
|
|
1788
|
+
}
|
|
1789
|
+
const newArr = [...arr];
|
|
1790
|
+
const [item] = newArr.splice(fromIndex, 1);
|
|
1791
|
+
newArr.splice(toIndex, 0, item);
|
|
1792
|
+
return newArr;
|
|
1793
|
+
});
|
|
1794
|
+
}, []);
|
|
1795
|
+
const swap = useCallback((indexA, indexB) => {
|
|
1796
|
+
setValue((arr) => {
|
|
1797
|
+
if (indexA < 0 || indexA >= arr.length || indexB < 0 || indexB >= arr.length || indexA === indexB) {
|
|
1798
|
+
return arr;
|
|
1799
|
+
}
|
|
1800
|
+
const newArr = [...arr];
|
|
1801
|
+
[newArr[indexA], newArr[indexB]] = [newArr[indexB], newArr[indexA]];
|
|
1802
|
+
return newArr;
|
|
1803
|
+
});
|
|
1804
|
+
}, []);
|
|
1805
|
+
const clear = useCallback(() => {
|
|
1806
|
+
setValue([]);
|
|
1807
|
+
}, []);
|
|
1808
|
+
const reset = useCallback(() => {
|
|
1809
|
+
setValue(initialValue);
|
|
1810
|
+
}, [initialValue]);
|
|
1811
|
+
const reverse = useCallback(() => {
|
|
1812
|
+
setValue((arr) => [...arr].reverse());
|
|
1813
|
+
}, []);
|
|
1814
|
+
const sort = useCallback((compareFn) => {
|
|
1815
|
+
setValue((arr) => [...arr].sort(compareFn));
|
|
1816
|
+
}, []);
|
|
1817
|
+
const filter = useCallback((predicate) => value.filter(predicate), [value]);
|
|
1818
|
+
const find = useCallback((predicate) => value.find(predicate), [value]);
|
|
1819
|
+
const findIndex = useCallback((predicate) => value.findIndex(predicate), [value]);
|
|
1820
|
+
const includes = useCallback((item) => value.includes(item), [value]);
|
|
1821
|
+
const computed = useMemo(
|
|
1822
|
+
() => ({
|
|
1823
|
+
length: value.length,
|
|
1824
|
+
isEmpty: value.length === 0,
|
|
1825
|
+
first: value[0],
|
|
1826
|
+
last: value[value.length - 1]
|
|
1827
|
+
}),
|
|
1828
|
+
[value]
|
|
1829
|
+
);
|
|
1830
|
+
return {
|
|
1831
|
+
value,
|
|
1832
|
+
setValue,
|
|
1833
|
+
push,
|
|
1834
|
+
pushMany,
|
|
1835
|
+
remove,
|
|
1836
|
+
removeAt,
|
|
1837
|
+
removeWhere,
|
|
1838
|
+
updateAt,
|
|
1839
|
+
updateAtPartial,
|
|
1840
|
+
updateWhere,
|
|
1841
|
+
insertAt,
|
|
1842
|
+
move,
|
|
1843
|
+
swap,
|
|
1844
|
+
clear,
|
|
1845
|
+
reset,
|
|
1846
|
+
reverse,
|
|
1847
|
+
sort,
|
|
1848
|
+
filter,
|
|
1849
|
+
find,
|
|
1850
|
+
findIndex,
|
|
1851
|
+
includes,
|
|
1852
|
+
...computed
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
var defaultRetryDelay = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4);
|
|
1856
|
+
function useMutation(mutationFn, options = {}) {
|
|
1857
|
+
const { onMutate, onSuccess, onError, onSettled, retry = 0, retryDelay = defaultRetryDelay } = options;
|
|
1858
|
+
const [state, setState] = useState({
|
|
1859
|
+
data: void 0,
|
|
1860
|
+
error: void 0,
|
|
1861
|
+
isLoading: false,
|
|
1862
|
+
isSuccess: false,
|
|
1863
|
+
isError: false,
|
|
1864
|
+
isIdle: true,
|
|
1865
|
+
status: "idle"
|
|
1866
|
+
});
|
|
1867
|
+
const mutationIdRef = useRef(0);
|
|
1868
|
+
const mutate = useCallback(
|
|
1869
|
+
(variables, callOptions) => {
|
|
1870
|
+
const currentId = ++mutationIdRef.current;
|
|
1871
|
+
const executeWithRetry = async (attempt) => {
|
|
1872
|
+
if (mutationIdRef.current !== currentId) return;
|
|
1873
|
+
setState({
|
|
1874
|
+
data: void 0,
|
|
1875
|
+
error: void 0,
|
|
1876
|
+
isLoading: true,
|
|
1877
|
+
isSuccess: false,
|
|
1878
|
+
isError: false,
|
|
1879
|
+
isIdle: false,
|
|
1880
|
+
status: "loading"
|
|
1881
|
+
});
|
|
1882
|
+
try {
|
|
1883
|
+
await onMutate?.(variables);
|
|
1884
|
+
const data = await mutationFn(variables);
|
|
1885
|
+
if (mutationIdRef.current !== currentId) return;
|
|
1886
|
+
setState({
|
|
1887
|
+
data,
|
|
1888
|
+
error: void 0,
|
|
1889
|
+
isLoading: false,
|
|
1890
|
+
isSuccess: true,
|
|
1891
|
+
isError: false,
|
|
1892
|
+
isIdle: false,
|
|
1893
|
+
status: "success"
|
|
1894
|
+
});
|
|
1895
|
+
await onSuccess?.(data, variables);
|
|
1896
|
+
await callOptions?.onSuccess?.(data);
|
|
1897
|
+
await onSettled?.(data, void 0, variables);
|
|
1898
|
+
await callOptions?.onSettled?.(data, void 0);
|
|
1899
|
+
} catch (err) {
|
|
1900
|
+
if (mutationIdRef.current !== currentId) return;
|
|
1901
|
+
const error = err;
|
|
1902
|
+
const maxRetries = typeof retry === "boolean" ? retry ? 3 : 0 : retry;
|
|
1903
|
+
if (attempt < maxRetries) {
|
|
1904
|
+
const delay = typeof retryDelay === "function" ? retryDelay(attempt) : retryDelay;
|
|
1905
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1906
|
+
await executeWithRetry(attempt + 1);
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
setState({
|
|
1910
|
+
data: void 0,
|
|
1911
|
+
error,
|
|
1912
|
+
isLoading: false,
|
|
1913
|
+
isSuccess: false,
|
|
1914
|
+
isError: true,
|
|
1915
|
+
isIdle: false,
|
|
1916
|
+
status: "error"
|
|
1917
|
+
});
|
|
1918
|
+
await onError?.(error, variables);
|
|
1919
|
+
await callOptions?.onError?.(error);
|
|
1920
|
+
await onSettled?.(void 0, error, variables);
|
|
1921
|
+
await callOptions?.onSettled?.(void 0, error);
|
|
1922
|
+
}
|
|
1923
|
+
};
|
|
1924
|
+
executeWithRetry(0);
|
|
1925
|
+
},
|
|
1926
|
+
[mutationFn, onMutate, onSuccess, onError, onSettled, retry, retryDelay]
|
|
1927
|
+
);
|
|
1928
|
+
const mutateAsync = useCallback(
|
|
1929
|
+
(variables) => new Promise((resolve, reject) => {
|
|
1930
|
+
mutate(variables, {
|
|
1931
|
+
onSuccess: resolve,
|
|
1932
|
+
onError: reject
|
|
1933
|
+
});
|
|
1934
|
+
}),
|
|
1935
|
+
[mutate]
|
|
1936
|
+
);
|
|
1937
|
+
const reset = useCallback(() => {
|
|
1938
|
+
mutationIdRef.current++;
|
|
1939
|
+
setState({
|
|
1940
|
+
data: void 0,
|
|
1941
|
+
error: void 0,
|
|
1942
|
+
isLoading: false,
|
|
1943
|
+
isSuccess: false,
|
|
1944
|
+
isError: false,
|
|
1945
|
+
isIdle: true,
|
|
1946
|
+
status: "idle"
|
|
1947
|
+
});
|
|
1948
|
+
}, []);
|
|
1949
|
+
return {
|
|
1950
|
+
...state,
|
|
1951
|
+
mutate,
|
|
1952
|
+
mutateAsync,
|
|
1953
|
+
reset
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
var defaultRetryDelay2 = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4);
|
|
1957
|
+
function useLazyQuery(queryFn, options = {}) {
|
|
1958
|
+
const {
|
|
1959
|
+
onSuccess,
|
|
1960
|
+
onError,
|
|
1961
|
+
onSettled,
|
|
1962
|
+
cacheTime = 0,
|
|
1963
|
+
staleTime = 0,
|
|
1964
|
+
retry = 0,
|
|
1965
|
+
retryDelay = defaultRetryDelay2,
|
|
1966
|
+
initialData
|
|
1967
|
+
} = options;
|
|
1968
|
+
const [state, setState] = useState({
|
|
1969
|
+
data: initialData,
|
|
1970
|
+
error: void 0,
|
|
1971
|
+
isLoading: false,
|
|
1972
|
+
isSuccess: !!initialData,
|
|
1973
|
+
isError: false,
|
|
1974
|
+
isCalled: !!initialData,
|
|
1975
|
+
isFetching: false,
|
|
1976
|
+
isRefetching: false
|
|
1977
|
+
});
|
|
1978
|
+
const queryIdRef = useRef(0);
|
|
1979
|
+
const lastVariablesRef = useRef(null);
|
|
1980
|
+
const cacheRef = useRef(/* @__PURE__ */ new Map());
|
|
1981
|
+
const getCacheKey = useCallback((variables) => JSON.stringify(variables), []);
|
|
1982
|
+
const isCacheValid = useCallback(
|
|
1983
|
+
(entry) => {
|
|
1984
|
+
if (!entry || cacheTime === 0) return false;
|
|
1985
|
+
const age = Date.now() - entry.timestamp;
|
|
1986
|
+
return age < cacheTime;
|
|
1987
|
+
},
|
|
1988
|
+
[cacheTime]
|
|
1989
|
+
);
|
|
1990
|
+
const isCacheFresh = useCallback(
|
|
1991
|
+
(entry) => {
|
|
1992
|
+
if (!entry || staleTime === 0) return false;
|
|
1993
|
+
const age = Date.now() - entry.timestamp;
|
|
1994
|
+
return age < staleTime;
|
|
1995
|
+
},
|
|
1996
|
+
[staleTime]
|
|
1997
|
+
);
|
|
1998
|
+
const query = useCallback(
|
|
1999
|
+
async (variables) => {
|
|
2000
|
+
const currentId = ++queryIdRef.current;
|
|
2001
|
+
lastVariablesRef.current = variables;
|
|
2002
|
+
const cacheKey = getCacheKey(variables);
|
|
2003
|
+
const cached = cacheRef.current.get(cacheKey);
|
|
2004
|
+
if (isCacheFresh(cached)) {
|
|
2005
|
+
setState((prev) => ({
|
|
2006
|
+
...prev,
|
|
2007
|
+
data: cached.data,
|
|
2008
|
+
isSuccess: true,
|
|
2009
|
+
isCalled: true
|
|
2010
|
+
}));
|
|
2011
|
+
return cached.data;
|
|
2012
|
+
}
|
|
2013
|
+
if (isCacheValid(cached)) {
|
|
2014
|
+
setState({
|
|
2015
|
+
data: cached.data,
|
|
2016
|
+
error: void 0,
|
|
2017
|
+
isLoading: false,
|
|
2018
|
+
isSuccess: true,
|
|
2019
|
+
isError: false,
|
|
2020
|
+
isCalled: true,
|
|
2021
|
+
isFetching: true,
|
|
2022
|
+
isRefetching: true
|
|
2023
|
+
});
|
|
2024
|
+
} else {
|
|
2025
|
+
setState({
|
|
2026
|
+
data: void 0,
|
|
2027
|
+
error: void 0,
|
|
2028
|
+
isLoading: true,
|
|
2029
|
+
isSuccess: false,
|
|
2030
|
+
isError: false,
|
|
2031
|
+
isCalled: true,
|
|
2032
|
+
isFetching: true,
|
|
2033
|
+
isRefetching: false
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
2036
|
+
const executeWithRetry = async (attempt) => {
|
|
2037
|
+
try {
|
|
2038
|
+
const data = await queryFn(variables);
|
|
2039
|
+
if (queryIdRef.current !== currentId) {
|
|
2040
|
+
return data;
|
|
2041
|
+
}
|
|
2042
|
+
cacheRef.current.set(cacheKey, {
|
|
2043
|
+
data,
|
|
2044
|
+
timestamp: Date.now(),
|
|
2045
|
+
variables
|
|
2046
|
+
});
|
|
2047
|
+
setState({
|
|
2048
|
+
data,
|
|
2049
|
+
error: void 0,
|
|
2050
|
+
isLoading: false,
|
|
2051
|
+
isSuccess: true,
|
|
2052
|
+
isError: false,
|
|
2053
|
+
isCalled: true,
|
|
2054
|
+
isFetching: false,
|
|
2055
|
+
isRefetching: false
|
|
2056
|
+
});
|
|
2057
|
+
await onSuccess?.(data, variables);
|
|
2058
|
+
await onSettled?.(data, void 0, variables);
|
|
2059
|
+
return data;
|
|
2060
|
+
} catch (err) {
|
|
2061
|
+
if (queryIdRef.current !== currentId) {
|
|
2062
|
+
throw err;
|
|
2063
|
+
}
|
|
2064
|
+
const error = err;
|
|
2065
|
+
const maxRetries = typeof retry === "boolean" ? retry ? 3 : 0 : retry;
|
|
2066
|
+
if (attempt < maxRetries) {
|
|
2067
|
+
const delay = typeof retryDelay === "function" ? retryDelay(attempt) : retryDelay;
|
|
2068
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2069
|
+
return executeWithRetry(attempt + 1);
|
|
2070
|
+
}
|
|
2071
|
+
setState({
|
|
2072
|
+
data: void 0,
|
|
2073
|
+
error,
|
|
2074
|
+
isLoading: false,
|
|
2075
|
+
isSuccess: false,
|
|
2076
|
+
isError: true,
|
|
2077
|
+
isCalled: true,
|
|
2078
|
+
isFetching: false,
|
|
2079
|
+
isRefetching: false
|
|
2080
|
+
});
|
|
2081
|
+
await onError?.(error, variables);
|
|
2082
|
+
await onSettled?.(void 0, error, variables);
|
|
2083
|
+
throw error;
|
|
2084
|
+
}
|
|
2085
|
+
};
|
|
2086
|
+
return executeWithRetry(0);
|
|
2087
|
+
},
|
|
2088
|
+
[queryFn, getCacheKey, isCacheFresh, isCacheValid, onSuccess, onError, onSettled, retry, retryDelay]
|
|
2089
|
+
);
|
|
2090
|
+
const refetch = useCallback(async () => {
|
|
2091
|
+
if (lastVariablesRef.current === null) {
|
|
2092
|
+
return void 0;
|
|
2093
|
+
}
|
|
2094
|
+
return query(lastVariablesRef.current);
|
|
2095
|
+
}, [query]);
|
|
2096
|
+
const reset = useCallback(() => {
|
|
2097
|
+
queryIdRef.current++;
|
|
2098
|
+
lastVariablesRef.current = null;
|
|
2099
|
+
cacheRef.current.clear();
|
|
2100
|
+
setState({
|
|
2101
|
+
data: initialData,
|
|
2102
|
+
error: void 0,
|
|
2103
|
+
isLoading: false,
|
|
2104
|
+
isSuccess: !!initialData,
|
|
2105
|
+
isError: false,
|
|
2106
|
+
isCalled: !!initialData,
|
|
2107
|
+
isFetching: false,
|
|
2108
|
+
isRefetching: false
|
|
2109
|
+
});
|
|
2110
|
+
}, [initialData]);
|
|
2111
|
+
const setData = useCallback((data) => {
|
|
2112
|
+
setState((prev) => {
|
|
2113
|
+
const newData = typeof data === "function" ? data(prev.data) : data;
|
|
2114
|
+
return {
|
|
2115
|
+
...prev,
|
|
2116
|
+
data: newData,
|
|
2117
|
+
isSuccess: true
|
|
2118
|
+
};
|
|
2119
|
+
});
|
|
2120
|
+
}, []);
|
|
2121
|
+
return {
|
|
2122
|
+
...state,
|
|
2123
|
+
query,
|
|
2124
|
+
refetch,
|
|
2125
|
+
reset,
|
|
2126
|
+
setData
|
|
2127
|
+
};
|
|
2128
|
+
}
|
|
2129
|
+
var defaultRetryDelay3 = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4);
|
|
2130
|
+
function useInfiniteScroll(fetchFn, options = {}) {
|
|
2131
|
+
const {
|
|
2132
|
+
onSuccess,
|
|
2133
|
+
onError,
|
|
2134
|
+
initialCursor,
|
|
2135
|
+
pageSize = 20,
|
|
2136
|
+
retry = 0,
|
|
2137
|
+
retryDelay = defaultRetryDelay3,
|
|
2138
|
+
observerOptions = { threshold: 0.1 }
|
|
2139
|
+
} = options;
|
|
2140
|
+
const [state, setState] = useState({
|
|
2141
|
+
items: [],
|
|
2142
|
+
pages: [],
|
|
2143
|
+
error: void 0,
|
|
2144
|
+
isLoading: false,
|
|
2145
|
+
isFetchingMore: false,
|
|
2146
|
+
hasNextPage: true,
|
|
2147
|
+
isEmpty: true,
|
|
2148
|
+
totalCount: void 0
|
|
2149
|
+
});
|
|
2150
|
+
const fetchIdRef = useRef(0);
|
|
2151
|
+
const cursorRef = useRef(initialCursor);
|
|
2152
|
+
const observerRef = useRef(null);
|
|
2153
|
+
const sentinelElementRef = useRef(null);
|
|
2154
|
+
const isMountedRef = useRef(true);
|
|
2155
|
+
useEffect(() => {
|
|
2156
|
+
isMountedRef.current = true;
|
|
2157
|
+
return () => {
|
|
2158
|
+
isMountedRef.current = false;
|
|
2159
|
+
if (observerRef.current) {
|
|
2160
|
+
observerRef.current.disconnect();
|
|
2161
|
+
}
|
|
2162
|
+
};
|
|
2163
|
+
}, []);
|
|
2164
|
+
const fetchPage = useCallback(
|
|
2165
|
+
async (cursor, isRefresh = false) => {
|
|
2166
|
+
const currentId = ++fetchIdRef.current;
|
|
2167
|
+
const executeWithRetry = async (attempt) => {
|
|
2168
|
+
if (!isMountedRef.current) return;
|
|
2169
|
+
setState((prev) => ({
|
|
2170
|
+
...prev,
|
|
2171
|
+
isLoading: isRefresh && prev.pages.length === 0,
|
|
2172
|
+
isFetchingMore: !isRefresh || prev.pages.length > 0,
|
|
2173
|
+
error: void 0
|
|
2174
|
+
}));
|
|
2175
|
+
try {
|
|
2176
|
+
const page = await fetchFn({ cursor, pageSize });
|
|
2177
|
+
if (fetchIdRef.current !== currentId || !isMountedRef.current) return;
|
|
2178
|
+
setState((prev) => {
|
|
2179
|
+
const newPages = isRefresh ? [page] : [...prev.pages, page];
|
|
2180
|
+
const newItems = newPages.flatMap((p) => p.items);
|
|
2181
|
+
return {
|
|
2182
|
+
items: newItems,
|
|
2183
|
+
pages: newPages,
|
|
2184
|
+
error: void 0,
|
|
2185
|
+
isLoading: false,
|
|
2186
|
+
isFetchingMore: false,
|
|
2187
|
+
hasNextPage: page.pageInfo.hasNextPage,
|
|
2188
|
+
isEmpty: newItems.length === 0,
|
|
2189
|
+
totalCount: page.pageInfo.totalCount
|
|
2190
|
+
};
|
|
2191
|
+
});
|
|
2192
|
+
cursorRef.current = page.pageInfo.endCursor;
|
|
2193
|
+
await onSuccess?.(page);
|
|
2194
|
+
} catch (err) {
|
|
2195
|
+
if (fetchIdRef.current !== currentId || !isMountedRef.current) return;
|
|
2196
|
+
const error = err;
|
|
2197
|
+
const maxRetries = typeof retry === "boolean" ? retry ? 3 : 0 : retry;
|
|
2198
|
+
if (attempt < maxRetries) {
|
|
2199
|
+
const delay = typeof retryDelay === "function" ? retryDelay(attempt) : retryDelay;
|
|
2200
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2201
|
+
await executeWithRetry(attempt + 1);
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
setState((prev) => ({
|
|
2205
|
+
...prev,
|
|
2206
|
+
error,
|
|
2207
|
+
isLoading: false,
|
|
2208
|
+
isFetchingMore: false
|
|
2209
|
+
}));
|
|
2210
|
+
await onError?.(error);
|
|
2211
|
+
}
|
|
2212
|
+
};
|
|
2213
|
+
return executeWithRetry(0);
|
|
2214
|
+
},
|
|
2215
|
+
[fetchFn, pageSize, onSuccess, onError, retry, retryDelay]
|
|
2216
|
+
);
|
|
2217
|
+
const fetchNextPage = useCallback(async () => {
|
|
2218
|
+
if (state.isFetchingMore || state.isLoading || !state.hasNextPage) return;
|
|
2219
|
+
await fetchPage(cursorRef.current);
|
|
2220
|
+
}, [fetchPage, state.isFetchingMore, state.isLoading, state.hasNextPage]);
|
|
2221
|
+
const refresh = useCallback(async () => {
|
|
2222
|
+
cursorRef.current = initialCursor;
|
|
2223
|
+
await fetchPage(initialCursor, true);
|
|
2224
|
+
}, [fetchPage, initialCursor]);
|
|
2225
|
+
const reset = useCallback(() => {
|
|
2226
|
+
fetchIdRef.current++;
|
|
2227
|
+
cursorRef.current = initialCursor;
|
|
2228
|
+
setState({
|
|
2229
|
+
items: [],
|
|
2230
|
+
pages: [],
|
|
2231
|
+
error: void 0,
|
|
2232
|
+
isLoading: false,
|
|
2233
|
+
isFetchingMore: false,
|
|
2234
|
+
hasNextPage: true,
|
|
2235
|
+
isEmpty: true,
|
|
2236
|
+
totalCount: void 0
|
|
2237
|
+
});
|
|
2238
|
+
}, [initialCursor]);
|
|
2239
|
+
const prependItems = useCallback((newItems) => {
|
|
2240
|
+
setState((prev) => ({
|
|
2241
|
+
...prev,
|
|
2242
|
+
items: [...newItems, ...prev.items],
|
|
2243
|
+
isEmpty: newItems.length === 0 && prev.items.length === 0
|
|
2244
|
+
}));
|
|
2245
|
+
}, []);
|
|
2246
|
+
const appendItems = useCallback((newItems) => {
|
|
2247
|
+
setState((prev) => ({
|
|
2248
|
+
...prev,
|
|
2249
|
+
items: [...prev.items, ...newItems],
|
|
2250
|
+
isEmpty: prev.items.length === 0 && newItems.length === 0
|
|
2251
|
+
}));
|
|
2252
|
+
}, []);
|
|
2253
|
+
const removeItem = useCallback((predicate) => {
|
|
2254
|
+
setState((prev) => {
|
|
2255
|
+
const newItems = prev.items.filter((item) => !predicate(item));
|
|
2256
|
+
return {
|
|
2257
|
+
...prev,
|
|
2258
|
+
items: newItems,
|
|
2259
|
+
isEmpty: newItems.length === 0
|
|
2260
|
+
};
|
|
2261
|
+
});
|
|
2262
|
+
}, []);
|
|
2263
|
+
const updateItem = useCallback(
|
|
2264
|
+
(predicate, update) => {
|
|
2265
|
+
setState((prev) => ({
|
|
2266
|
+
...prev,
|
|
2267
|
+
items: prev.items.map((item) => {
|
|
2268
|
+
if (!predicate(item)) return item;
|
|
2269
|
+
if (typeof update === "function") {
|
|
2270
|
+
return update(item);
|
|
2271
|
+
}
|
|
2272
|
+
return { ...item, ...update };
|
|
2273
|
+
})
|
|
2274
|
+
}));
|
|
2275
|
+
},
|
|
2276
|
+
[]
|
|
2277
|
+
);
|
|
2278
|
+
const sentinelRef = useCallback(
|
|
2279
|
+
(element) => {
|
|
2280
|
+
if (observerRef.current) {
|
|
2281
|
+
observerRef.current.disconnect();
|
|
2282
|
+
}
|
|
2283
|
+
sentinelElementRef.current = element;
|
|
2284
|
+
if (!element) return;
|
|
2285
|
+
observerRef.current = new IntersectionObserver((entries) => {
|
|
2286
|
+
if (entries[0]?.isIntersecting) {
|
|
2287
|
+
fetchNextPage();
|
|
2288
|
+
}
|
|
2289
|
+
}, observerOptions);
|
|
2290
|
+
observerRef.current.observe(element);
|
|
2291
|
+
},
|
|
2292
|
+
[fetchNextPage, observerOptions]
|
|
2293
|
+
);
|
|
2294
|
+
useEffect(() => {
|
|
2295
|
+
fetchPage(initialCursor, true);
|
|
2296
|
+
}, []);
|
|
2297
|
+
return {
|
|
2298
|
+
items: state.items,
|
|
2299
|
+
pages: state.pages,
|
|
2300
|
+
error: state.error,
|
|
2301
|
+
isLoading: state.isLoading,
|
|
2302
|
+
isFetchingMore: state.isFetchingMore,
|
|
2303
|
+
hasNextPage: state.hasNextPage,
|
|
2304
|
+
isEmpty: state.isEmpty,
|
|
2305
|
+
totalCount: state.totalCount,
|
|
2306
|
+
fetchNextPage,
|
|
2307
|
+
refresh,
|
|
2308
|
+
reset,
|
|
2309
|
+
sentinelRef,
|
|
2310
|
+
prependItems,
|
|
2311
|
+
appendItems,
|
|
2312
|
+
removeItem,
|
|
2313
|
+
updateItem
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
export { CustomPopover, cookiesAvailable, getCookie, removeCookie, setCookie, useArray, useAsync, useAsyncFn, useBackToTop, useBreakpoint, useBreakpointChecks, useClientRect, useConfigFromQuery, useCookies, useCountdownDate, useCountdownSeconds, useDoubleClick, useEnterKey, useEscapeKey, useFocusTrap, useImageDimensions, useInfiniteScroll, useIntersectionObserver, useIsomorphicLayoutEffect, useKeyboardShortcut, useLazyQuery, useMultipleImageDimensions, useMutation, useOnlineStatus, usePasswordVisibility, usePopover, usePopoverHover, useResponsiveQuery, useScrollLock, useScrollOffsetTop, useScrollPosition, useScrollToTop, useSessionStorage, useThrottle, useThrottleCallback, useUpdateEffect, useUpdateLayoutEffect, useWindowSize };
|
|
2318
|
+
//# sourceMappingURL=chunk-E3XANPTO.js.map
|
|
2319
|
+
//# sourceMappingURL=chunk-E3XANPTO.js.map
|