@ts-hooks-kit/core 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,2499 @@
1
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
2
+
3
+ //#region src/useAsync/useAsync.ts
4
+ /**
5
+ * Custom hook that manages the state of an async function.
6
+ * @template T - The type of the resolved value.
7
+ * @param {() => Promise<T>} asyncFn - The async function to execute.
8
+ * @param {unknown[]} [deps] - Dependencies that trigger re-execution when changed.
9
+ * @returns {UseAsyncState<T>} The state of the async operation.
10
+ * @public
11
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-async)
12
+ * @example
13
+ * ```tsx
14
+ * const { value, error, loading, retry } = useAsync(async () => {
15
+ * const response = await fetch('/api/data');
16
+ * return response.json();
17
+ * }, []);
18
+ * ```
19
+ */
20
+ function useAsync(asyncFn, deps = []) {
21
+ const [state, setState] = useState({
22
+ loading: true,
23
+ value: void 0,
24
+ error: void 0
25
+ });
26
+ const asyncFnRef = useRef(asyncFn);
27
+ const isMountedRef = useRef(true);
28
+ useEffect(() => {
29
+ asyncFnRef.current = asyncFn;
30
+ }, [asyncFn]);
31
+ const execute = useCallback(async () => {
32
+ setState((prev) => ({
33
+ ...prev,
34
+ loading: true,
35
+ error: void 0
36
+ }));
37
+ try {
38
+ const result = await asyncFnRef.current();
39
+ if (isMountedRef.current) setState({
40
+ loading: false,
41
+ value: result,
42
+ error: void 0
43
+ });
44
+ } catch (err) {
45
+ if (isMountedRef.current) setState({
46
+ loading: false,
47
+ value: void 0,
48
+ error: err instanceof Error ? err : new Error(String(err))
49
+ });
50
+ }
51
+ }, deps);
52
+ const retry = useCallback(() => {
53
+ execute();
54
+ }, [execute]);
55
+ useEffect(() => {
56
+ isMountedRef.current = true;
57
+ execute();
58
+ return () => {
59
+ isMountedRef.current = false;
60
+ };
61
+ }, [execute]);
62
+ return {
63
+ loading: state.loading,
64
+ value: state.value,
65
+ error: state.error,
66
+ retry
67
+ };
68
+ }
69
+
70
+ //#endregion
71
+ //#region src/useBoolean/useBoolean.ts
72
+ /**
73
+ * Custom hook that handles boolean state with useful utility functions.
74
+ * @param {boolean} [defaultValue] - The initial value for the boolean state (default is `false`).
75
+ * @returns {UseBooleanReturn} An object containing the boolean state value and utility functions to manipulate the state.
76
+ * @throws Will throw an error if `defaultValue` is an invalid boolean value.
77
+ * @public
78
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-boolean)
79
+ * @example
80
+ * ```tsx
81
+ * const { value, setTrue, setFalse, toggle } = useBoolean(true);
82
+ * ```
83
+ */
84
+ function useBoolean(defaultValue = false) {
85
+ if (typeof defaultValue !== "boolean") throw new Error("defaultValue must be `true` or `false`");
86
+ const [value, setValue] = useState(defaultValue);
87
+ const setTrue = useCallback(() => {
88
+ setValue(true);
89
+ }, []);
90
+ const setFalse = useCallback(() => {
91
+ setValue(false);
92
+ }, []);
93
+ const toggle = useCallback(() => {
94
+ setValue((x) => !x);
95
+ }, []);
96
+ return {
97
+ value,
98
+ setValue,
99
+ setTrue,
100
+ setFalse,
101
+ toggle
102
+ };
103
+ }
104
+
105
+ //#endregion
106
+ //#region src/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.ts
107
+ /**
108
+ * Custom hook that uses either `useLayoutEffect` or `useEffect` based on the environment (client-side or server-side).
109
+ * @param {Function} effect - The effect function to be executed.
110
+ * @param {Array<any>} [dependencies] - An array of dependencies for the effect (optional).
111
+ * @public
112
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-isomorphic-layout-effect)
113
+ * @example
114
+ * ```tsx
115
+ * useIsomorphicLayoutEffect(() => {
116
+ * // Code to be executed during the layout phase on the client side
117
+ * }, [dependency1, dependency2]);
118
+ * ```
119
+ */
120
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
121
+
122
+ //#endregion
123
+ //#region src/useEventListener/useEventListener.ts
124
+ /**
125
+ * Custom hook that attaches event listeners to DOM elements, the window, or media query lists.
126
+ * @template KW - The type of event for window events.
127
+ * @template KH - The type of event for HTML or SVG element events.
128
+ * @template KM - The type of event for media query list events.
129
+ * @template T - The type of the DOM element (default is `HTMLElement`).
130
+ * @param {KW | KH | KM} eventName - The name of the event to listen for.
131
+ * @param {(event: WindowEventMap[KW] | HTMLElementEventMap[KH] | SVGElementEventMap[KH] | MediaQueryListEventMap[KM] | Event) => void} handler - The event handler function.
132
+ * @param {RefObject<T>} [element] - The DOM element or media query list to attach the event listener to (optional).
133
+ * @param {boolean | AddEventListenerOptions} [options] - An options object that specifies characteristics about the event listener (optional).
134
+ * @public
135
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-event-listener)
136
+ * @example
137
+ * ```tsx
138
+ * // Example 1: Attach a window event listener
139
+ * useEventListener('resize', handleResize);
140
+ * ```
141
+ * @example
142
+ * ```tsx
143
+ * // Example 2: Attach a document event listener with options
144
+ * const elementRef = useRef(document);
145
+ * useEventListener('click', handleClick, elementRef, { capture: true });
146
+ * ```
147
+ * @example
148
+ * ```tsx
149
+ * // Example 3: Attach an element event listener
150
+ * const buttonRef = useRef<HTMLButtonElement>(null);
151
+ * useEventListener('click', handleButtonClick, buttonRef);
152
+ * ```
153
+ */
154
+ function useEventListener(eventName, handler, element, options) {
155
+ const savedHandler = useRef(handler);
156
+ useIsomorphicLayoutEffect(() => {
157
+ savedHandler.current = handler;
158
+ }, [handler]);
159
+ useEffect(() => {
160
+ const targetElement = element?.current ?? window;
161
+ if (!(targetElement && targetElement.addEventListener)) return;
162
+ const listener = (event) => {
163
+ savedHandler.current(event);
164
+ };
165
+ targetElement.addEventListener(eventName, listener, options);
166
+ return () => {
167
+ targetElement.removeEventListener(eventName, listener, options);
168
+ };
169
+ }, [
170
+ eventName,
171
+ element,
172
+ options
173
+ ]);
174
+ }
175
+
176
+ //#endregion
177
+ //#region src/useClickAnyWhere/useClickAnyWhere.ts
178
+ /**
179
+ * Custom hook that handles click events anywhere on the document.
180
+ * @param {Function} handler - The function to be called when a click event is detected anywhere on the document.
181
+ * @public
182
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-click-any-where)
183
+ * @example
184
+ * ```tsx
185
+ * const handleClick = (event) => {
186
+ * console.log('Document clicked!', event);
187
+ * };
188
+ *
189
+ * // Attach click event handler to document
190
+ * useClickAnywhere(handleClick);
191
+ * ```
192
+ */
193
+ function useClickAnyWhere(handler) {
194
+ useEventListener("click", (event) => {
195
+ handler(event);
196
+ });
197
+ }
198
+
199
+ //#endregion
200
+ //#region src/useCopyToClipboard/useCopyToClipboard.ts
201
+ /**
202
+ * Custom hook that copies text to the clipboard using the [`Clipboard API`](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API).
203
+ * @returns {[CopiedValue, CopyFn]} An tuple containing the copied text and a function to copy text to the clipboard.
204
+ * @public
205
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-copy-to-clipboard)
206
+ * @example
207
+ * ```tsx
208
+ * const [copiedText, copyToClipboard] = useCopyToClipboard();
209
+ * const textToCopy = 'Hello, world!';
210
+ *
211
+ * // Attempt to copy text to the clipboard
212
+ * copyToClipboard(textToCopy)
213
+ * .then(success => {
214
+ * if (success) {
215
+ * console.log(`Text "${textToCopy}" copied to clipboard successfully.`);
216
+ * } else {
217
+ * console.error('Failed to copy text to clipboard.');
218
+ * }
219
+ * });
220
+ * ```
221
+ */
222
+ function useCopyToClipboard() {
223
+ const [copiedText, setCopiedText] = useState(null);
224
+ const copy = useCallback(async (text) => {
225
+ if (!navigator?.clipboard) {
226
+ console.warn("Clipboard not supported");
227
+ return false;
228
+ }
229
+ try {
230
+ await navigator.clipboard.writeText(text);
231
+ setCopiedText(text);
232
+ return true;
233
+ } catch (error) {
234
+ console.warn("Copy failed", error);
235
+ setCopiedText(null);
236
+ return false;
237
+ }
238
+ }, []);
239
+ return [copiedText, copy];
240
+ }
241
+
242
+ //#endregion
243
+ //#region src/useCounter/useCounter.ts
244
+ /**
245
+ * Custom hook that manages a counter with increment, decrement, reset, and setCount functionalities.
246
+ * @param {number} [initialValue] - The initial value for the counter.
247
+ * @returns {UseCounterReturn} An object containing the current count and functions to interact with the counter.
248
+ * @public
249
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-counter)
250
+ * @example
251
+ * ```tsx
252
+ * const { count, increment, decrement, reset, setCount } = useCounter(5);
253
+ * ```
254
+ */
255
+ function useCounter(initialValue) {
256
+ const [count, setCount] = useState(initialValue ?? 0);
257
+ const increment = useCallback(() => {
258
+ setCount((x) => x + 1);
259
+ }, []);
260
+ const decrement = useCallback(() => {
261
+ setCount((x) => x - 1);
262
+ }, []);
263
+ const reset = useCallback(() => {
264
+ setCount(initialValue ?? 0);
265
+ }, [initialValue]);
266
+ return {
267
+ count,
268
+ increment,
269
+ decrement,
270
+ reset,
271
+ setCount
272
+ };
273
+ }
274
+
275
+ //#endregion
276
+ //#region src/useInterval/useInterval.ts
277
+ /**
278
+ * Custom hook that creates an interval that invokes a callback function at a specified delay using the [`setInterval API`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval).
279
+ * @param {() => void} callback - The function to be invoked at each interval.
280
+ * @param {number | null} delay - The time, in milliseconds, between each invocation of the callback. Use `null` to clear the interval.
281
+ * @public
282
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-interval)
283
+ * @example
284
+ * ```tsx
285
+ * const handleInterval = () => {
286
+ * // Code to be executed at each interval
287
+ * };
288
+ * useInterval(handleInterval, 1000);
289
+ * ```
290
+ */
291
+ function useInterval(callback, delay) {
292
+ const savedCallback = useRef(callback);
293
+ useIsomorphicLayoutEffect(() => {
294
+ savedCallback.current = callback;
295
+ }, [callback]);
296
+ useEffect(() => {
297
+ if (delay === null) return;
298
+ const id = setInterval(() => {
299
+ savedCallback.current();
300
+ }, delay);
301
+ return () => {
302
+ clearInterval(id);
303
+ };
304
+ }, [delay]);
305
+ }
306
+
307
+ //#endregion
308
+ //#region src/useCountdown/useCountdown.ts
309
+ /**
310
+ * Custom hook that manages countdown.
311
+ * @param {CountdownOptions} countdownOptions - The countdown's options.
312
+ * @returns {[number, CountdownControllers]} An array containing the countdown's count and its controllers.
313
+ * @public
314
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-countdown)
315
+ * @example
316
+ * ```tsx
317
+ * const [counter, { start, stop, reset }] = useCountdown({
318
+ * countStart: 10,
319
+ * intervalMs: 1000,
320
+ * isIncrement: false,
321
+ * });
322
+ * ```
323
+ */
324
+ function useCountdown({ countStart, countStop = 0, intervalMs = 1e3, isIncrement = false }) {
325
+ const { count, increment, decrement, reset: resetCounter } = useCounter(countStart);
326
+ const { value: isCountdownRunning, setTrue: startCountdown, setFalse: stopCountdown } = useBoolean(false);
327
+ const resetCountdown = useCallback(() => {
328
+ stopCountdown();
329
+ resetCounter();
330
+ }, [stopCountdown, resetCounter]);
331
+ const countdownCallback = useCallback(() => {
332
+ if (count === countStop) {
333
+ stopCountdown();
334
+ return;
335
+ }
336
+ if (isIncrement) increment();
337
+ else decrement();
338
+ }, [
339
+ count,
340
+ countStop,
341
+ decrement,
342
+ increment,
343
+ isIncrement,
344
+ stopCountdown
345
+ ]);
346
+ useInterval(countdownCallback, isCountdownRunning ? intervalMs : null);
347
+ return [count, {
348
+ startCountdown,
349
+ stopCountdown,
350
+ resetCountdown
351
+ }];
352
+ }
353
+
354
+ //#endregion
355
+ //#region src/useEventCallback/useEventCallback.ts
356
+ function useEventCallback(fn) {
357
+ const ref = useRef(() => {
358
+ throw new Error("Cannot call an event handler while rendering.");
359
+ });
360
+ useIsomorphicLayoutEffect(() => {
361
+ ref.current = fn;
362
+ }, [fn]);
363
+ return useCallback((...args) => ref.current?.(...args), [ref]);
364
+ }
365
+
366
+ //#endregion
367
+ //#region src/useLocalStorage/useLocalStorage.ts
368
+ const IS_SERVER$6 = typeof window === "undefined";
369
+ /**
370
+ * Custom hook that uses the [`localStorage API`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to persist state across page reloads.
371
+ * @template T - The type of the state to be stored in local storage.
372
+ * @param {string} key - The key under which the value will be stored in local storage.
373
+ * @param {T | (() => T)} initialValue - The initial value of the state or a function that returns the initial value.
374
+ * @param {UseLocalStorageOptions<T>} [options] - Options for customizing the behavior of serialization and deserialization (optional).
375
+ * @returns {[T, Dispatch<SetStateAction<T>>, () => void]} A tuple containing the stored value, a function to set the value and a function to remove the key from storage.
376
+ * @public
377
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-local-storage)
378
+ * @example
379
+ * ```tsx
380
+ * const [count, setCount, removeCount] = useLocalStorage('count', 0);
381
+ * // Access the `count` value, the `setCount` function to update it and `removeCount` function to remove the key from storage.
382
+ * ```
383
+ */
384
+ function useLocalStorage(key, initialValue, options = {}) {
385
+ const { initializeWithValue = true } = options;
386
+ const serializer = useCallback((value) => {
387
+ if (options.serializer) return options.serializer(value);
388
+ return JSON.stringify(value);
389
+ }, [options]);
390
+ const deserializer = useCallback((value) => {
391
+ if (options.deserializer) return options.deserializer(value);
392
+ if (value === "undefined") return void 0;
393
+ const defaultValue = initialValue instanceof Function ? initialValue() : initialValue;
394
+ let parsed;
395
+ try {
396
+ parsed = JSON.parse(value);
397
+ } catch (error) {
398
+ console.error("Error parsing JSON:", error);
399
+ return defaultValue;
400
+ }
401
+ return parsed;
402
+ }, [options, initialValue]);
403
+ const readValue = useCallback(() => {
404
+ const initialValueToUse = initialValue instanceof Function ? initialValue() : initialValue;
405
+ if (IS_SERVER$6) return initialValueToUse;
406
+ try {
407
+ const raw = window.localStorage.getItem(key);
408
+ return raw ? deserializer(raw) : initialValueToUse;
409
+ } catch (error) {
410
+ console.warn(`Error reading localStorage key “${key}”:`, error);
411
+ return initialValueToUse;
412
+ }
413
+ }, [
414
+ initialValue,
415
+ key,
416
+ deserializer
417
+ ]);
418
+ const [storedValue, setStoredValue] = useState(() => {
419
+ if (initializeWithValue) return readValue();
420
+ return initialValue instanceof Function ? initialValue() : initialValue;
421
+ });
422
+ const setValue = useEventCallback((value) => {
423
+ if (IS_SERVER$6) console.warn(`Tried setting localStorage key “${key}” even though environment is not a client`);
424
+ try {
425
+ const newValue = value instanceof Function ? value(readValue()) : value;
426
+ window.localStorage.setItem(key, serializer(newValue));
427
+ setStoredValue(newValue);
428
+ window.dispatchEvent(new StorageEvent("local-storage", { key }));
429
+ } catch (error) {
430
+ console.warn(`Error setting localStorage key “${key}”:`, error);
431
+ }
432
+ });
433
+ const removeValue = useEventCallback(() => {
434
+ if (IS_SERVER$6) console.warn(`Tried removing localStorage key “${key}” even though environment is not a client`);
435
+ const defaultValue = initialValue instanceof Function ? initialValue() : initialValue;
436
+ window.localStorage.removeItem(key);
437
+ setStoredValue(defaultValue);
438
+ window.dispatchEvent(new StorageEvent("local-storage", { key }));
439
+ });
440
+ useEffect(() => {
441
+ setStoredValue(readValue());
442
+ }, [key]);
443
+ const handleStorageChange = useCallback((event) => {
444
+ if (event.key && event.key !== key) return;
445
+ setStoredValue(readValue());
446
+ }, [key, readValue]);
447
+ useEventListener("storage", handleStorageChange);
448
+ useEventListener("local-storage", handleStorageChange);
449
+ return [
450
+ storedValue,
451
+ setValue,
452
+ removeValue
453
+ ];
454
+ }
455
+
456
+ //#endregion
457
+ //#region src/useMediaQuery/useMediaQuery.ts
458
+ const IS_SERVER$5 = typeof window === "undefined";
459
+ /**
460
+ * Custom hook that tracks the state of a media query using the [`Match Media API`](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia).
461
+ * @param {string} query - The media query to track.
462
+ * @param {?UseMediaQueryOptions} [options] - The options for customizing the behavior of the hook (optional).
463
+ * @returns {boolean} The current state of the media query (true if the query matches, false otherwise).
464
+ * @public
465
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-media-query)
466
+ * @example
467
+ * ```tsx
468
+ * const isSmallScreen = useMediaQuery('(max-width: 600px)');
469
+ * // Use `isSmallScreen` to conditionally apply styles or logic based on the screen size.
470
+ * ```
471
+ */
472
+ function useMediaQuery(query, { defaultValue = false, initializeWithValue = true } = {}) {
473
+ const getMatches = (query$1) => {
474
+ if (IS_SERVER$5) return defaultValue;
475
+ return window.matchMedia(query$1).matches;
476
+ };
477
+ const [matches, setMatches] = useState(() => {
478
+ if (initializeWithValue) return getMatches(query);
479
+ return defaultValue;
480
+ });
481
+ function handleChange() {
482
+ setMatches(getMatches(query));
483
+ }
484
+ useIsomorphicLayoutEffect(() => {
485
+ const matchMedia = window.matchMedia(query);
486
+ handleChange();
487
+ if (matchMedia.addListener) matchMedia.addListener(handleChange);
488
+ else matchMedia.addEventListener("change", handleChange);
489
+ return () => {
490
+ if (matchMedia.removeListener) matchMedia.removeListener(handleChange);
491
+ else matchMedia.removeEventListener("change", handleChange);
492
+ };
493
+ }, [query]);
494
+ return matches;
495
+ }
496
+
497
+ //#endregion
498
+ //#region src/useDarkMode/useDarkMode.ts
499
+ const COLOR_SCHEME_QUERY$1 = "(prefers-color-scheme: dark)";
500
+ const LOCAL_STORAGE_KEY$1 = "usehooks-ts-dark-mode";
501
+ /**
502
+ * Custom hook that returns the current state of the dark mode.
503
+ * @param {?DarkModeOptions} [options] - The initial value of the dark mode, default `false`.
504
+ * @returns {DarkModeReturn} An object containing the dark mode's state and its controllers.
505
+ * @public
506
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-dark-mode)
507
+ * @example
508
+ * ```tsx
509
+ * const { isDarkMode, toggle, enable, disable, set } = useDarkMode({ defaultValue: true });
510
+ * ```
511
+ */
512
+ function useDarkMode(options = {}) {
513
+ const { defaultValue, localStorageKey = LOCAL_STORAGE_KEY$1, initializeWithValue = true } = options;
514
+ const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY$1, {
515
+ initializeWithValue,
516
+ defaultValue
517
+ });
518
+ const [isDarkMode, setDarkMode] = useLocalStorage(localStorageKey, defaultValue ?? isDarkOS ?? false, { initializeWithValue });
519
+ useIsomorphicLayoutEffect(() => {
520
+ if (isDarkOS !== isDarkMode) setDarkMode(isDarkOS);
521
+ }, [isDarkOS]);
522
+ return {
523
+ isDarkMode,
524
+ toggle: () => {
525
+ setDarkMode((prev) => !prev);
526
+ },
527
+ enable: () => {
528
+ setDarkMode(true);
529
+ },
530
+ disable: () => {
531
+ setDarkMode(false);
532
+ },
533
+ set: (value) => {
534
+ setDarkMode(value);
535
+ }
536
+ };
537
+ }
538
+
539
+ //#endregion
540
+ //#region src/utils/debounce.ts
541
+ /**
542
+ * Creates a debounced version of a function.
543
+ * @param func The function to debounce
544
+ * @param delay The delay in milliseconds
545
+ * @param options Debounce options
546
+ * @returns Debounced function with cancel and flush methods
547
+ */
548
+ function debounce(func, delay, options = {}) {
549
+ const { leading = false, trailing = true, maxWait } = options;
550
+ let timeoutId = null;
551
+ let maxTimeoutId = null;
552
+ let lastArgs = null;
553
+ let lastResult;
554
+ let lastCallTime = null;
555
+ const clearTimeouts = () => {
556
+ if (timeoutId) {
557
+ clearTimeout(timeoutId);
558
+ timeoutId = null;
559
+ }
560
+ if (maxTimeoutId) {
561
+ clearTimeout(maxTimeoutId);
562
+ maxTimeoutId = null;
563
+ }
564
+ };
565
+ const invokeFunc = (args) => {
566
+ lastArgs = null;
567
+ lastCallTime = null;
568
+ lastResult = func(...args);
569
+ return lastResult;
570
+ };
571
+ const remainingWait = (time) => {
572
+ const timeSinceLastCall = time - (lastCallTime ?? 0);
573
+ return delay - timeSinceLastCall;
574
+ };
575
+ const shouldInvoke = (time) => {
576
+ const timeSinceLastCall = time - (lastCallTime ?? 0);
577
+ return lastCallTime === null || timeSinceLastCall >= delay || timeSinceLastCall < 0;
578
+ };
579
+ const trailingEdge = () => {
580
+ timeoutId = null;
581
+ if (trailing && lastArgs) return invokeFunc(lastArgs);
582
+ lastArgs = null;
583
+ lastCallTime = null;
584
+ return void 0;
585
+ };
586
+ const timerExpired = () => {
587
+ const time = Date.now();
588
+ if (shouldInvoke(time)) return trailingEdge();
589
+ const timeWaiting = remainingWait(time);
590
+ timeoutId = setTimeout(timerExpired, timeWaiting);
591
+ return void 0;
592
+ };
593
+ const debounced = (...args) => {
594
+ const time = Date.now();
595
+ const isInvoking = shouldInvoke(time);
596
+ lastArgs = args;
597
+ if (isInvoking) {
598
+ clearTimeouts();
599
+ if (lastCallTime === null) {
600
+ lastCallTime = time;
601
+ timeoutId = setTimeout(timerExpired, delay);
602
+ if (leading) return invokeFunc(args);
603
+ return void 0;
604
+ }
605
+ lastCallTime = time;
606
+ return lastResult;
607
+ }
608
+ if (timeoutId) clearTimeout(timeoutId);
609
+ if (maxWait && !maxTimeoutId && lastCallTime !== null) {
610
+ const timeSinceLastCall = time - lastCallTime;
611
+ const timeUntilMaxWait = maxWait - timeSinceLastCall;
612
+ maxTimeoutId = setTimeout(() => {
613
+ maxTimeoutId = null;
614
+ if (lastArgs) invokeFunc(lastArgs);
615
+ }, timeUntilMaxWait);
616
+ }
617
+ const timeWaiting = remainingWait(time);
618
+ timeoutId = setTimeout(timerExpired, timeWaiting);
619
+ return lastResult;
620
+ };
621
+ debounced.cancel = () => {
622
+ clearTimeouts();
623
+ lastArgs = null;
624
+ lastCallTime = null;
625
+ };
626
+ debounced.flush = () => {
627
+ if (timeoutId || maxTimeoutId) {
628
+ clearTimeouts();
629
+ if (lastArgs) return invokeFunc(lastArgs);
630
+ }
631
+ return lastResult;
632
+ };
633
+ return debounced;
634
+ }
635
+
636
+ //#endregion
637
+ //#region src/useUnmount/useUnmount.ts
638
+ /**
639
+ * Custom hook that runs a cleanup function when the component is unmounted.
640
+ * @param {() => void} func - The cleanup function to be executed on unmount.
641
+ * @public
642
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-unmount)
643
+ * @example
644
+ * ```tsx
645
+ * useUnmount(() => {
646
+ * // Cleanup logic here
647
+ * });
648
+ * ```
649
+ */
650
+ function useUnmount(func) {
651
+ const funcRef = useRef(func);
652
+ funcRef.current = func;
653
+ useEffect(() => () => {
654
+ funcRef.current();
655
+ }, []);
656
+ }
657
+
658
+ //#endregion
659
+ //#region src/useDebounceCallback/useDebounceCallback.ts
660
+ /**
661
+ * Custom hook that creates a debounced version of a callback function.
662
+ * @template T - Type of the original callback function.
663
+ * @param {T} func - The callback function to be debounced.
664
+ * @param {number} delay - The delay in milliseconds before the callback is invoked (default is `500` milliseconds).
665
+ * @param {DebounceOptions} [options] - Options to control the behavior of the debounced function.
666
+ * @returns {DebouncedState<T>} A debounced version of the original callback along with control functions.
667
+ * @public
668
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-debounce-callback)
669
+ * @example
670
+ * ```tsx
671
+ * const debouncedCallback = useDebounceCallback(
672
+ * (searchTerm) => {
673
+ * // Perform search after user stops typing for 500 milliseconds
674
+ * searchApi(searchTerm);
675
+ * },
676
+ * 500
677
+ * );
678
+ *
679
+ * // Later in the component
680
+ * debouncedCallback('react hooks'); // Will invoke the callback after 500 milliseconds of inactivity.
681
+ * ```
682
+ */
683
+ function useDebounceCallback(func, delay = 500, options) {
684
+ const debouncedFunc = useRef(null);
685
+ useUnmount(() => {
686
+ if (debouncedFunc.current) debouncedFunc.current.cancel();
687
+ });
688
+ const debounced = useMemo(() => {
689
+ const debouncedFuncInstance = debounce(func, delay, options);
690
+ const wrappedFunc = (...args) => {
691
+ return debouncedFuncInstance(...args);
692
+ };
693
+ wrappedFunc.cancel = () => {
694
+ debouncedFuncInstance.cancel();
695
+ };
696
+ wrappedFunc.isPending = () => {
697
+ return !!debouncedFunc.current;
698
+ };
699
+ wrappedFunc.flush = () => {
700
+ return debouncedFuncInstance.flush();
701
+ };
702
+ return wrappedFunc;
703
+ }, [
704
+ func,
705
+ delay,
706
+ options
707
+ ]);
708
+ useEffect(() => {
709
+ debouncedFunc.current = debounce(func, delay, options);
710
+ }, [
711
+ func,
712
+ delay,
713
+ options
714
+ ]);
715
+ return debounced;
716
+ }
717
+
718
+ //#endregion
719
+ //#region src/useDebounceValue/useDebounceValue.ts
720
+ /**
721
+ * Custom hook that returns a debounced version of the provided value, along with a function to update it.
722
+ * @template T - The type of the value.
723
+ * @param {T | (() => T)} initialValue - The value to be debounced.
724
+ * @param {number} delay - The delay in milliseconds before the value is updated (default is 500ms).
725
+ * @param {object} [options] - Optional configurations for the debouncing behavior.
726
+ * @returns {[T, DebouncedState<(value: T) => void>]} An array containing the debounced value and the function to update it.
727
+ * @public
728
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-debounce-value)
729
+ * @example
730
+ * ```tsx
731
+ * const [debouncedValue, updateDebouncedValue] = useDebounceValue(inputValue, 500, { leading: true });
732
+ * ```
733
+ */
734
+ function useDebounceValue(initialValue, delay, options) {
735
+ const eq = options?.equalityFn ?? ((left, right) => left === right);
736
+ const unwrappedInitialValue = initialValue instanceof Function ? initialValue() : initialValue;
737
+ const [debouncedValue, setDebouncedValue] = useState(unwrappedInitialValue);
738
+ const previousValueRef = useRef(unwrappedInitialValue);
739
+ const updateDebouncedValue = useDebounceCallback(setDebouncedValue, delay, options);
740
+ if (!eq(previousValueRef.current, unwrappedInitialValue)) {
741
+ updateDebouncedValue(unwrappedInitialValue);
742
+ previousValueRef.current = unwrappedInitialValue;
743
+ }
744
+ return [debouncedValue, updateDebouncedValue];
745
+ }
746
+
747
+ //#endregion
748
+ //#region src/useDisclosure/useDisclosure.ts
749
+ /**
750
+ * Custom hook for managing boolean disclosure state (modals, popovers, drawers, etc.).
751
+ * @param {boolean} [initialState=false] - The initial open state.
752
+ * @param {UseDisclosureOptions} [options] - Optional callbacks for open/close events.
753
+ * @returns {UseDisclosureReturn} A tuple containing the open state and control actions.
754
+ * @public
755
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-disclosure)
756
+ * @example
757
+ * ```tsx
758
+ * const [opened, { open, close, toggle }] = useDisclosure(false, {
759
+ * onOpen: () => console.log('Opened'),
760
+ * onClose: () => console.log('Closed'),
761
+ * });
762
+ *
763
+ * return (
764
+ * <>
765
+ * <button onClick={open}>Open Modal</button>
766
+ * <Modal opened={opened} onClose={close}>
767
+ * <button onClick={close}>Close</button>
768
+ * </Modal>
769
+ * </>
770
+ * );
771
+ * ```
772
+ */
773
+ function useDisclosure(initialState = false, options = {}) {
774
+ const { onOpen, onClose } = options;
775
+ const [opened, setOpened] = useState(initialState);
776
+ const open = useCallback(() => {
777
+ setOpened((isOpen) => {
778
+ if (!isOpen) {
779
+ onOpen?.();
780
+ return true;
781
+ }
782
+ return isOpen;
783
+ });
784
+ }, [onOpen]);
785
+ const close = useCallback(() => {
786
+ setOpened((isOpen) => {
787
+ if (isOpen) {
788
+ onClose?.();
789
+ return false;
790
+ }
791
+ return isOpen;
792
+ });
793
+ }, [onClose]);
794
+ const toggle = useCallback(() => {
795
+ setOpened((isOpen) => {
796
+ if (isOpen) onClose?.();
797
+ else onOpen?.();
798
+ return !isOpen;
799
+ });
800
+ }, [onClose, onOpen]);
801
+ return [opened, {
802
+ open,
803
+ close,
804
+ toggle
805
+ }];
806
+ }
807
+
808
+ //#endregion
809
+ //#region src/useDocumentTitle/useDocumentTitle.ts
810
+ /**
811
+ * Custom hook that sets the document title.
812
+ * @param {string} title - The title to set.
813
+ * @param {?UseDocumentTitleOptions} [options] - The options.
814
+ * @public
815
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-document-title)
816
+ * @example
817
+ * ```tsx
818
+ * useDocumentTitle('My new title');
819
+ * ```
820
+ */
821
+ function useDocumentTitle(title, options = {}) {
822
+ const { preserveTitleOnUnmount = true } = options;
823
+ const defaultTitle = useRef(null);
824
+ useIsomorphicLayoutEffect(() => {
825
+ defaultTitle.current = window.document.title;
826
+ }, []);
827
+ useIsomorphicLayoutEffect(() => {
828
+ window.document.title = title;
829
+ }, [title]);
830
+ useUnmount(() => {
831
+ if (!preserveTitleOnUnmount && defaultTitle.current) window.document.title = defaultTitle.current;
832
+ });
833
+ }
834
+
835
+ //#endregion
836
+ //#region src/useGeolocation/useGeolocation.ts
837
+ /**
838
+ * Custom hook that provides access to the browser's geolocation API.
839
+ * @param {UseGeolocationOptions} [options] - Options for geolocation and hook behavior.
840
+ * @returns {UseGeolocationState} The current geolocation state.
841
+ * @public
842
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-geolocation)
843
+ * @example
844
+ * ```tsx
845
+ * const { latitude, longitude, loading, error } = useGeolocation({
846
+ * enableHighAccuracy: true,
847
+ * timeout: 5000,
848
+ * });
849
+ *
850
+ * if (loading) return <div>Loading location...</div>;
851
+ * if (error) return <div>Error: {error.message}</div>;
852
+ * return <div>Location: {latitude}, {longitude}</div>;
853
+ * ```
854
+ */
855
+ function useGeolocation(options = {}) {
856
+ const { enableHighAccuracy = false, timeout = Infinity, maximumAge = 0, enabled = true } = options;
857
+ const [state, setState] = useState({
858
+ latitude: void 0,
859
+ longitude: void 0,
860
+ accuracy: void 0,
861
+ altitude: void 0,
862
+ altitudeAccuracy: void 0,
863
+ heading: void 0,
864
+ speed: void 0,
865
+ timestamp: void 0,
866
+ loading: true,
867
+ error: void 0
868
+ });
869
+ useEffect(() => {
870
+ if (!enabled) {
871
+ setState((prev) => ({
872
+ ...prev,
873
+ loading: false
874
+ }));
875
+ return;
876
+ }
877
+ if (!navigator.geolocation) {
878
+ setState((prev) => ({
879
+ ...prev,
880
+ loading: false,
881
+ error: new Error("Geolocation is not supported")
882
+ }));
883
+ return;
884
+ }
885
+ let isMounted = true;
886
+ const onSuccess = (position) => {
887
+ if (!isMounted) return;
888
+ setState({
889
+ latitude: position.coords.latitude,
890
+ longitude: position.coords.longitude,
891
+ accuracy: position.coords.accuracy,
892
+ altitude: position.coords.altitude,
893
+ altitudeAccuracy: position.coords.altitudeAccuracy,
894
+ heading: position.coords.heading,
895
+ speed: position.coords.speed,
896
+ timestamp: position.timestamp,
897
+ loading: false,
898
+ error: void 0
899
+ });
900
+ };
901
+ const onError = (error) => {
902
+ if (!isMounted) return;
903
+ setState((prev) => ({
904
+ ...prev,
905
+ loading: false,
906
+ error
907
+ }));
908
+ };
909
+ navigator.geolocation.getCurrentPosition(onSuccess, onError, {
910
+ enableHighAccuracy,
911
+ timeout,
912
+ maximumAge
913
+ });
914
+ return () => {
915
+ isMounted = false;
916
+ };
917
+ }, [
918
+ enableHighAccuracy,
919
+ enabled,
920
+ maximumAge,
921
+ timeout
922
+ ]);
923
+ return state;
924
+ }
925
+
926
+ //#endregion
927
+ //#region src/useHover/useHover.ts
928
+ /**
929
+ * Custom hook that tracks whether a DOM element is being hovered over.
930
+ * @template T - The type of the DOM element. Defaults to `HTMLElement`.
931
+ * @param {RefObject<T>} elementRef - The ref object for the DOM element to track.
932
+ * @returns {boolean} A boolean value indicating whether the element is being hovered over.
933
+ * @public
934
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-hover)
935
+ * @example
936
+ * ```tsx
937
+ * const buttonRef = useRef<HTMLButtonElement>(null);
938
+ * const isHovered = useHover(buttonRef);
939
+ * // Access the isHovered variable to determine if the button is being hovered over.
940
+ * ```
941
+ */
942
+ function useHover(elementRef) {
943
+ const [value, setValue] = useState(false);
944
+ const handleMouseEnter = () => {
945
+ setValue(true);
946
+ };
947
+ const handleMouseLeave = () => {
948
+ setValue(false);
949
+ };
950
+ useEventListener("mouseenter", handleMouseEnter, elementRef);
951
+ useEventListener("mouseleave", handleMouseLeave, elementRef);
952
+ return value;
953
+ }
954
+
955
+ //#endregion
956
+ //#region src/useIdle/useIdle.ts
957
+ /**
958
+ * Default events that indicate user activity.
959
+ */
960
+ const DEFAULT_EVENTS = [
961
+ "mousemove",
962
+ "mousedown",
963
+ "resize",
964
+ "keydown",
965
+ "touchstart",
966
+ "wheel"
967
+ ];
968
+ /**
969
+ * Custom hook that tracks whether the user is idle (no activity for a specified time).
970
+ * @param {number} timeout - The time in milliseconds of inactivity before considering the user idle.
971
+ * @param {UseIdleOptions} [options] - Options for customizing the hook behavior.
972
+ * @returns {IdleState} The current idle state and last activity timestamp.
973
+ * @public
974
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-idle)
975
+ * @example
976
+ * ```tsx
977
+ * const { idle, lastActive } = useIdle(5000);
978
+ *
979
+ * return (
980
+ * <div>
981
+ * <p>{idle ? 'User is idle' : 'User is active'}</p>
982
+ * <p>Last active: {new Date(lastActive).toLocaleString()}</p>
983
+ * </div>
984
+ * );
985
+ * ```
986
+ */
987
+ function useIdle(timeout, options = {}) {
988
+ const { events = DEFAULT_EVENTS, initialIdle = false } = options;
989
+ const [state, setState] = useState({
990
+ idle: initialIdle,
991
+ lastActive: Date.now()
992
+ });
993
+ const timerRef = useRef(null);
994
+ useEffect(() => {
995
+ const resetIdle = () => {
996
+ if (timerRef.current) clearTimeout(timerRef.current);
997
+ setState({
998
+ idle: false,
999
+ lastActive: Date.now()
1000
+ });
1001
+ timerRef.current = setTimeout(() => {
1002
+ setState((prev) => ({
1003
+ ...prev,
1004
+ idle: true
1005
+ }));
1006
+ }, timeout);
1007
+ };
1008
+ events.forEach((event) => {
1009
+ window.addEventListener(event, resetIdle);
1010
+ });
1011
+ timerRef.current = setTimeout(() => {
1012
+ setState((prev) => ({
1013
+ ...prev,
1014
+ idle: true
1015
+ }));
1016
+ }, timeout);
1017
+ return () => {
1018
+ events.forEach((event) => {
1019
+ window.removeEventListener(event, resetIdle);
1020
+ });
1021
+ if (timerRef.current) clearTimeout(timerRef.current);
1022
+ };
1023
+ }, [timeout, events]);
1024
+ return state;
1025
+ }
1026
+
1027
+ //#endregion
1028
+ //#region src/useIntersectionObserver/useIntersectionObserver.ts
1029
+ /**
1030
+ * Custom hook that tracks the intersection of a DOM element with its containing element or the viewport using the [`Intersection Observer API`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
1031
+ * @param {UseIntersectionObserverOptions} options - The options for the Intersection Observer.
1032
+ * @returns {IntersectionReturn} The ref callback, a boolean indicating if the element is intersecting, and the intersection observer entry.
1033
+ * @public
1034
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-intersection-observer)
1035
+ * @example
1036
+ * ```tsx
1037
+ * // Example 1
1038
+ * const [ref, isIntersecting, entry] = useIntersectionObserver({ threshold: 0.5 });
1039
+ * ```
1040
+ *
1041
+ * ```tsx
1042
+ * // Example 2
1043
+ * const { ref, isIntersecting, entry } = useIntersectionObserver({ threshold: 0.5 });
1044
+ * ```
1045
+ */
1046
+ function useIntersectionObserver({ threshold = 0, root = null, rootMargin = "0%", freezeOnceVisible = false, initialIsIntersecting = false, onChange } = {}) {
1047
+ const [ref, setRef] = useState(null);
1048
+ const [state, setState] = useState(() => ({
1049
+ isIntersecting: initialIsIntersecting,
1050
+ entry: void 0
1051
+ }));
1052
+ const callbackRef = useRef(void 0);
1053
+ callbackRef.current = onChange;
1054
+ const frozen = state.entry?.isIntersecting && freezeOnceVisible;
1055
+ useEffect(() => {
1056
+ if (!ref) return;
1057
+ if (!("IntersectionObserver" in window)) return;
1058
+ if (frozen) return;
1059
+ let unobserve;
1060
+ const observer = new IntersectionObserver((entries) => {
1061
+ const thresholds = Array.isArray(observer.thresholds) ? observer.thresholds : [observer.thresholds];
1062
+ entries.forEach((entry) => {
1063
+ const isIntersecting = entry.isIntersecting && thresholds.some((threshold$1) => entry.intersectionRatio >= threshold$1);
1064
+ setState({
1065
+ isIntersecting,
1066
+ entry
1067
+ });
1068
+ if (callbackRef.current) callbackRef.current(isIntersecting, entry);
1069
+ if (isIntersecting && freezeOnceVisible && unobserve) {
1070
+ unobserve();
1071
+ unobserve = void 0;
1072
+ }
1073
+ });
1074
+ }, {
1075
+ threshold,
1076
+ root,
1077
+ rootMargin
1078
+ });
1079
+ observer.observe(ref);
1080
+ return () => {
1081
+ observer.disconnect();
1082
+ };
1083
+ }, [
1084
+ ref,
1085
+ JSON.stringify(threshold),
1086
+ root,
1087
+ rootMargin,
1088
+ frozen,
1089
+ freezeOnceVisible
1090
+ ]);
1091
+ const prevRef = useRef(null);
1092
+ useEffect(() => {
1093
+ if (!ref && state.entry?.target && !freezeOnceVisible && !frozen && prevRef.current !== state.entry.target) {
1094
+ prevRef.current = state.entry.target;
1095
+ setState({
1096
+ isIntersecting: initialIsIntersecting,
1097
+ entry: void 0
1098
+ });
1099
+ }
1100
+ }, [
1101
+ ref,
1102
+ state.entry,
1103
+ freezeOnceVisible,
1104
+ frozen,
1105
+ initialIsIntersecting
1106
+ ]);
1107
+ const result = [
1108
+ setRef,
1109
+ !!state.isIntersecting,
1110
+ state.entry
1111
+ ];
1112
+ result.ref = result[0];
1113
+ result.isIntersecting = result[1];
1114
+ result.entry = result[2];
1115
+ return result;
1116
+ }
1117
+
1118
+ //#endregion
1119
+ //#region src/useIsClient/useIsClient.ts
1120
+ /**
1121
+ * Custom hook that determines if the code is running on the client side (in the browser).
1122
+ * @returns {boolean} A boolean value indicating whether the code is running on the client side.
1123
+ * @public
1124
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-is-client)
1125
+ * @example
1126
+ * ```tsx
1127
+ * const isClient = useIsClient();
1128
+ * // Use isClient to conditionally render or execute code specific to the client side.
1129
+ * ```
1130
+ */
1131
+ function useIsClient() {
1132
+ const [isClient, setClient] = useState(false);
1133
+ useEffect(() => {
1134
+ setClient(true);
1135
+ }, []);
1136
+ return isClient;
1137
+ }
1138
+
1139
+ //#endregion
1140
+ //#region src/useIsMounted/useIsMounted.ts
1141
+ /**
1142
+ * Custom hook that determines if the component is currently mounted.
1143
+ * @returns {() => boolean} A function that returns a boolean value indicating whether the component is mounted.
1144
+ * @public
1145
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-is-mounted)
1146
+ * @example
1147
+ * ```tsx
1148
+ * const isComponentMounted = useIsMounted();
1149
+ * // Use isComponentMounted() to check if the component is currently mounted before performing certain actions.
1150
+ * ```
1151
+ */
1152
+ function useIsMounted() {
1153
+ const isMounted = useRef(false);
1154
+ useEffect(() => {
1155
+ isMounted.current = true;
1156
+ return () => {
1157
+ isMounted.current = false;
1158
+ };
1159
+ }, []);
1160
+ return useCallback(() => isMounted.current, []);
1161
+ }
1162
+
1163
+ //#endregion
1164
+ //#region src/useList/useList.ts
1165
+ /**
1166
+ * Custom hook that manages a list state with setter actions.
1167
+ * @template T - The type of elements in the list.
1168
+ * @param {T[]} [initialValue] - The initial array of values for the list (optional).
1169
+ * @returns {UseListReturn<T>} A tuple containing the list state and actions to interact with the list.
1170
+ * @public
1171
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-list)
1172
+ * @example
1173
+ * ```tsx
1174
+ * const [list, listActions] = useList<number>([1, 2, 3]);
1175
+ * // Access the `list` array and use `listActions` to push, updateAt, removeAt, or reset values.
1176
+ * ```
1177
+ */
1178
+ function useList(initialValue = []) {
1179
+ const initialListRef = useRef(initialValue);
1180
+ const [list, setList] = useState(initialValue);
1181
+ const set = useCallback((newList) => {
1182
+ setList(newList);
1183
+ }, []);
1184
+ const push = useCallback((...values) => {
1185
+ setList((prev) => [...prev, ...values]);
1186
+ }, []);
1187
+ const updateAt = useCallback((index, value) => {
1188
+ setList((prev) => {
1189
+ if (index < 0 || index >= prev.length) return prev;
1190
+ const copy = [...prev];
1191
+ copy[index] = value;
1192
+ return copy;
1193
+ });
1194
+ }, []);
1195
+ const insertAt = useCallback((index, value) => {
1196
+ setList((prev) => {
1197
+ if (index < 0 || index > prev.length) return prev;
1198
+ const copy = [...prev];
1199
+ copy.splice(index, 0, value);
1200
+ return copy;
1201
+ });
1202
+ }, []);
1203
+ const removeAt = useCallback((index) => {
1204
+ setList((prev) => {
1205
+ if (index < 0 || index >= prev.length) return prev;
1206
+ const copy = [...prev];
1207
+ copy.splice(index, 1);
1208
+ return copy;
1209
+ });
1210
+ }, []);
1211
+ const clear = useCallback(() => {
1212
+ setList([]);
1213
+ }, []);
1214
+ const reset = useCallback(() => {
1215
+ setList([...initialListRef.current]);
1216
+ }, []);
1217
+ const actions = {
1218
+ set,
1219
+ push,
1220
+ updateAt,
1221
+ insertAt,
1222
+ removeAt,
1223
+ clear,
1224
+ reset
1225
+ };
1226
+ return [list, actions];
1227
+ }
1228
+
1229
+ //#endregion
1230
+ //#region src/useMap/useMap.ts
1231
+ /**
1232
+ * Custom hook that manages a key-value [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) state with setter actions.
1233
+ * @template K - The type of keys in the map.
1234
+ * @template V - The type of values in the map.
1235
+ * @param {MapOrEntries<K, V>} [initialState] - The initial state of the map as a Map or an array of key-value pairs (optional).
1236
+ * @returns {UseMapReturn<K, V>} A tuple containing the map state and actions to interact with the map.
1237
+ * @public
1238
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-map)
1239
+ * @example
1240
+ * ```tsx
1241
+ * const [map, mapActions] = useMap();
1242
+ * // Access the `map` state and use `mapActions` to set, remove, or reset entries.
1243
+ * ```
1244
+ */
1245
+ function useMap(initialState = new Map()) {
1246
+ const [map, setMap] = useState(new Map(initialState));
1247
+ const actions = {
1248
+ set: useCallback((key, value) => {
1249
+ setMap((prev) => {
1250
+ const copy = new Map(prev);
1251
+ copy.set(key, value);
1252
+ return copy;
1253
+ });
1254
+ }, []),
1255
+ setAll: useCallback((entries) => {
1256
+ setMap(() => new Map(entries));
1257
+ }, []),
1258
+ remove: useCallback((key) => {
1259
+ setMap((prev) => {
1260
+ const copy = new Map(prev);
1261
+ copy.delete(key);
1262
+ return copy;
1263
+ });
1264
+ }, []),
1265
+ reset: useCallback(() => {
1266
+ setMap(() => new Map());
1267
+ }, [])
1268
+ };
1269
+ return [map, actions];
1270
+ }
1271
+
1272
+ //#endregion
1273
+ //#region src/useMemoizedFn/useMemoizedFn.ts
1274
+ /**
1275
+ * Custom hook that returns a memoized version of a function that never changes reference.
1276
+ * The returned function will always call the latest version of the callback, but its
1277
+ * identity (reference) will remain stable across renders.
1278
+ *
1279
+ * This is useful when you need to pass a callback to a dependency array or child component
1280
+ * without causing unnecessary re-renders.
1281
+ *
1282
+ * @template T - The type of the function being memoized.
1283
+ * @param {T} fn - The function to memoize.
1284
+ * @returns {T} A memoized function with stable reference.
1285
+ * @public
1286
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-memoized-fn)
1287
+ * @example
1288
+ * ```tsx
1289
+ * const stableCallback = useMemoizedFn((value) => {
1290
+ * console.log(value);
1291
+ * });
1292
+ *
1293
+ * // stableCallback can be safely used in useEffect deps without causing infinite loops
1294
+ * useEffect(() => {
1295
+ * stableCallback(someValue);
1296
+ * }, [stableCallback]);
1297
+ * ```
1298
+ */
1299
+ function useMemoizedFn(fn) {
1300
+ const fnRef = useRef(fn);
1301
+ fnRef.current = fn;
1302
+ const memoizedFn = useCallback((...args) => {
1303
+ return fnRef.current(...args);
1304
+ }, []);
1305
+ return memoizedFn;
1306
+ }
1307
+
1308
+ //#endregion
1309
+ //#region src/useNetwork/useNetwork.ts
1310
+ /**
1311
+ * Custom hook that tracks the browser's network connection status.
1312
+ * @returns {NetworkState} The current network state including online status and connection info.
1313
+ * @public
1314
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-network)
1315
+ * @example
1316
+ * ```tsx
1317
+ * const { online, effectiveType, downlink } = useNetwork();
1318
+ *
1319
+ * return (
1320
+ * <div>
1321
+ * <p>Status: {online ? 'Online' : 'Offline'}</p>
1322
+ * <p>Connection: {effectiveType}</p>
1323
+ * <p>Speed: {downlink} Mbps</p>
1324
+ * </div>
1325
+ * );
1326
+ * ```
1327
+ */
1328
+ function useNetwork() {
1329
+ const [state, setState] = useState({
1330
+ online: typeof navigator !== "undefined" ? navigator.onLine : true,
1331
+ effectiveType: void 0,
1332
+ downlink: void 0,
1333
+ downlinkMax: void 0,
1334
+ type: void 0,
1335
+ rtt: void 0,
1336
+ saveData: void 0
1337
+ });
1338
+ useEffect(() => {
1339
+ const handleOnline = () => {
1340
+ setState((prev) => ({
1341
+ ...prev,
1342
+ online: true
1343
+ }));
1344
+ };
1345
+ const handleOffline = () => {
1346
+ setState((prev) => ({
1347
+ ...prev,
1348
+ online: false
1349
+ }));
1350
+ };
1351
+ const updateConnectionInfo = () => {
1352
+ const connection$1 = navigator.connection;
1353
+ if (connection$1) setState({
1354
+ online: navigator.onLine,
1355
+ effectiveType: connection$1.effectiveType,
1356
+ downlink: connection$1.downlink,
1357
+ downlinkMax: connection$1.downlinkMax,
1358
+ type: connection$1.type,
1359
+ rtt: connection$1.rtt,
1360
+ saveData: connection$1.saveData
1361
+ });
1362
+ };
1363
+ window.addEventListener("online", handleOnline);
1364
+ window.addEventListener("offline", handleOffline);
1365
+ const connection = navigator.connection;
1366
+ if (connection) {
1367
+ connection.addEventListener("change", updateConnectionInfo);
1368
+ updateConnectionInfo();
1369
+ }
1370
+ return () => {
1371
+ window.removeEventListener("online", handleOnline);
1372
+ window.removeEventListener("offline", handleOffline);
1373
+ if (connection) connection.removeEventListener("change", updateConnectionInfo);
1374
+ };
1375
+ }, []);
1376
+ return state;
1377
+ }
1378
+
1379
+ //#endregion
1380
+ //#region src/useOnClickOutside/useOnClickOutside.ts
1381
+ /**
1382
+ * Custom hook that handles clicks outside a specified element.
1383
+ * @template T - The type of the element's reference.
1384
+ * @param {RefObject<T | null> | RefObject<T | null>[]} ref - The React ref object(s) representing the element(s) to watch for outside clicks.
1385
+ * @param {(event: MouseEvent | TouchEvent | FocusEvent) => void} handler - The callback function to be executed when a click outside the element occurs.
1386
+ * @param {EventType} [eventType] - The mouse event type to listen for (optional, default is 'mousedown').
1387
+ * @param {?AddEventListenerOptions} [eventListenerOptions] - The options object to be passed to the `addEventListener` method (optional).
1388
+ * @returns {void}
1389
+ * @public
1390
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-on-click-outside)
1391
+ * @example
1392
+ * ```tsx
1393
+ * const containerRef = useRef(null);
1394
+ * useOnClickOutside([containerRef], () => {
1395
+ * // Handle clicks outside the container.
1396
+ * });
1397
+ * ```
1398
+ */
1399
+ function useOnClickOutside(ref, handler, eventType = "mousedown", eventListenerOptions = {}) {
1400
+ useEventListener(eventType, (event) => {
1401
+ const target = event.target;
1402
+ if (!target || !target.isConnected) return;
1403
+ const isOutside = Array.isArray(ref) ? ref.filter((r) => Boolean(r.current)).every((r) => r.current && !r.current.contains(target)) : ref.current && !ref.current.contains(target);
1404
+ if (isOutside) handler(event);
1405
+ }, void 0, eventListenerOptions);
1406
+ }
1407
+
1408
+ //#endregion
1409
+ //#region src/usePageLeave/usePageLeave.ts
1410
+ /**
1411
+ * Custom hook that invokes a callback when the user's mouse leaves the page.
1412
+ * @param {() => void} handler - The callback function to invoke when the mouse leaves the page.
1413
+ * @public
1414
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-page-leave)
1415
+ * @example
1416
+ * ```tsx
1417
+ * usePageLeave(() => {
1418
+ * console.log('User is leaving the page');
1419
+ * // Show "Don't leave!" modal or save draft
1420
+ * });
1421
+ * ```
1422
+ */
1423
+ function usePageLeave(handler) {
1424
+ useEffect(() => {
1425
+ if (!handler) return;
1426
+ const handleMouseLeave = () => {
1427
+ handler();
1428
+ };
1429
+ document.addEventListener("mouseleave", handleMouseLeave);
1430
+ return () => {
1431
+ document.removeEventListener("mouseleave", handleMouseLeave);
1432
+ };
1433
+ }, [handler]);
1434
+ }
1435
+
1436
+ //#endregion
1437
+ //#region src/usePagination/usePagination.ts
1438
+ const DOTS = "dots";
1439
+ /**
1440
+ * Custom hook for managing pagination logic.
1441
+ * Returns page navigation functions and a range array suitable for pagination UI.
1442
+ * @param {UsePaginationOptions} options - Pagination configuration options.
1443
+ * @returns {UsePaginationReturn} The pagination state and navigation functions.
1444
+ * @public
1445
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-pagination)
1446
+ * @example
1447
+ * ```tsx
1448
+ * const { activePage, setPage, next, prev, range } = usePagination({
1449
+ * total: 20,
1450
+ * siblings: 1,
1451
+ * boundaries: 1,
1452
+ * });
1453
+ *
1454
+ * // range = [1, 'dots', 4, 5, 6, 'dots', 20]
1455
+ * ```
1456
+ */
1457
+ function usePagination(options) {
1458
+ const { total, siblings = 1, boundaries = 1, initialPage = 1, page: controlledPage, onChange } = options;
1459
+ const [internalPage, setInternalPage] = useState(initialPage);
1460
+ const activePage = controlledPage ?? internalPage;
1461
+ const setPage = useCallback((page) => {
1462
+ const validPage = Math.max(1, Math.min(page, total));
1463
+ if (validPage !== activePage) {
1464
+ if (controlledPage === void 0) setInternalPage(validPage);
1465
+ onChange?.(validPage);
1466
+ }
1467
+ }, [
1468
+ activePage,
1469
+ controlledPage,
1470
+ onChange,
1471
+ total
1472
+ ]);
1473
+ const next = useCallback(() => {
1474
+ setPage(activePage + 1);
1475
+ }, [activePage, setPage]);
1476
+ const prev = useCallback(() => {
1477
+ setPage(activePage - 1);
1478
+ }, [activePage, setPage]);
1479
+ const isFirst = activePage === 1;
1480
+ const isLast = activePage === total;
1481
+ const range = useMemo(() => {
1482
+ const range$1 = [];
1483
+ const totalVisible = siblings * 2 + 3 + boundaries * 2;
1484
+ if (totalVisible >= total) for (let i = 1; i <= total; i++) range$1.push(i);
1485
+ else {
1486
+ const leftBoundaryEnd = boundaries;
1487
+ for (let i = 1; i <= leftBoundaryEnd; i++) range$1.push(i);
1488
+ const leftSibling = Math.max(activePage - siblings, boundaries + 1);
1489
+ const rightSibling = Math.min(activePage + siblings, total - boundaries);
1490
+ const showLeftDots = leftSibling > boundaries + 1;
1491
+ const showRightDots = rightSibling < total - boundaries;
1492
+ if (showLeftDots) range$1.push(DOTS);
1493
+ for (let i = leftSibling; i <= rightSibling; i++) range$1.push(i);
1494
+ if (showRightDots) range$1.push(DOTS);
1495
+ const rightBoundaryStart = total - boundaries + 1;
1496
+ for (let i = rightBoundaryStart; i <= total; i++) range$1.push(i);
1497
+ }
1498
+ return range$1;
1499
+ }, [
1500
+ activePage,
1501
+ boundaries,
1502
+ siblings,
1503
+ total
1504
+ ]);
1505
+ return {
1506
+ activePage,
1507
+ range,
1508
+ setPage,
1509
+ next,
1510
+ prev,
1511
+ first: isFirst,
1512
+ last: isLast
1513
+ };
1514
+ }
1515
+
1516
+ //#endregion
1517
+ //#region src/usePermission/usePermission.ts
1518
+ /**
1519
+ * Custom hook that tracks the state of a browser permission.
1520
+ * @param {PermissionName} permissionName - The name of the permission to track.
1521
+ * @returns {PermissionState} The current state of the permission.
1522
+ * @public
1523
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-permission)
1524
+ * @example
1525
+ * ```tsx
1526
+ * const { state, supported } = usePermission('camera');
1527
+ *
1528
+ * if (!supported) return <div>Permissions API not supported</div>;
1529
+ * if (state === 'denied') return <div>Camera access denied</div>;
1530
+ * return <Camera />;
1531
+ * ```
1532
+ */
1533
+ function usePermission(permissionName) {
1534
+ const [state, setState] = useState({
1535
+ state: "prompt",
1536
+ supported: true
1537
+ });
1538
+ const permissionStatusRef = useRef(null);
1539
+ useEffect(() => {
1540
+ if (!navigator.permissions) {
1541
+ setState({
1542
+ state: "unsupported",
1543
+ supported: false
1544
+ });
1545
+ return;
1546
+ }
1547
+ let isMounted = true;
1548
+ const queryPermission = async () => {
1549
+ try {
1550
+ const status = await navigator.permissions.query({ name: permissionName });
1551
+ if (!isMounted) return;
1552
+ permissionStatusRef.current = status;
1553
+ const updateState = () => {
1554
+ if (isMounted) setState({
1555
+ state: status.state,
1556
+ supported: true
1557
+ });
1558
+ };
1559
+ updateState();
1560
+ status.addEventListener("change", updateState);
1561
+ return () => {
1562
+ status.removeEventListener("change", updateState);
1563
+ };
1564
+ } catch {
1565
+ if (isMounted) setState({
1566
+ state: "unsupported",
1567
+ supported: false
1568
+ });
1569
+ }
1570
+ };
1571
+ queryPermission();
1572
+ return () => {
1573
+ isMounted = false;
1574
+ };
1575
+ }, [permissionName]);
1576
+ return state;
1577
+ }
1578
+
1579
+ //#endregion
1580
+ //#region src/usePrevious/usePrevious.ts
1581
+ function usePrevious(value, initialValue) {
1582
+ const ref = useRef(initialValue);
1583
+ useEffect(() => {
1584
+ ref.current = value;
1585
+ }, [value]);
1586
+ return ref.current;
1587
+ }
1588
+
1589
+ //#endregion
1590
+ //#region src/useQueue/useQueue.ts
1591
+ /**
1592
+ * Custom hook that manages a FIFO (First In, First Out) queue state with setter actions.
1593
+ * @template T - The type of elements in the queue.
1594
+ * @param {T[]} [initialValue] - The initial array of values for the queue (optional).
1595
+ * @returns {UseQueueReturn<T>} A tuple containing the queue state and actions to interact with the queue.
1596
+ * @public
1597
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-queue)
1598
+ * @example
1599
+ * ```tsx
1600
+ * const [queue, queueActions] = useQueue<number>([1, 2, 3]);
1601
+ * // Access the `queue` array and use `queueActions` to add, remove, or clear values.
1602
+ * ```
1603
+ */
1604
+ function useQueue(initialValue = []) {
1605
+ const queueRef = useRef(initialValue);
1606
+ const [, setTick] = useState(0);
1607
+ const update = useCallback(() => {
1608
+ setTick((t) => t + 1);
1609
+ }, []);
1610
+ const add = useCallback((value) => {
1611
+ queueRef.current = [...queueRef.current, value];
1612
+ update();
1613
+ }, [update]);
1614
+ const remove = useCallback(() => {
1615
+ if (queueRef.current.length === 0) return void 0;
1616
+ const [first, ...rest] = queueRef.current;
1617
+ queueRef.current = rest;
1618
+ update();
1619
+ return first;
1620
+ }, [update]);
1621
+ const clear = useCallback(() => {
1622
+ queueRef.current = [];
1623
+ update();
1624
+ }, [update]);
1625
+ const actions = useMemo(() => ({
1626
+ add,
1627
+ remove,
1628
+ clear,
1629
+ get first() {
1630
+ return queueRef.current[0];
1631
+ },
1632
+ get last() {
1633
+ return queueRef.current[queueRef.current.length - 1];
1634
+ },
1635
+ get size() {
1636
+ return queueRef.current.length;
1637
+ }
1638
+ }), [
1639
+ add,
1640
+ clear,
1641
+ remove
1642
+ ]);
1643
+ return [queueRef.current, actions];
1644
+ }
1645
+
1646
+ //#endregion
1647
+ //#region src/useReadLocalStorage/useReadLocalStorage.ts
1648
+ const IS_SERVER$4 = typeof window === "undefined";
1649
+ /**
1650
+ * Custom hook that reads a value from [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), closely related to [`useLocalStorage()`](https://usehooks-ts.com/react-hook/use-local-storage).
1651
+ * @template T - The type of the stored value.
1652
+ * @param {string} key - The key associated with the value in local storage.
1653
+ * @param {Options<T>} [options] - Additional options for reading the value (optional).
1654
+ * @returns {T | null | undefined} The stored value, or null if the key is not present or an error occurs.
1655
+ * @public
1656
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-read-local-storage)
1657
+ * @example
1658
+ * ```tsx
1659
+ * const storedData = useReadLocalStorage('myKey');
1660
+ * // Access the stored data from local storage.
1661
+ * ```
1662
+ */
1663
+ function useReadLocalStorage(key, options = {}) {
1664
+ let { initializeWithValue = true } = options;
1665
+ if (IS_SERVER$4) initializeWithValue = false;
1666
+ const deserializer = useCallback((value) => {
1667
+ if (options.deserializer) return options.deserializer(value);
1668
+ if (value === "undefined") return void 0;
1669
+ let parsed;
1670
+ try {
1671
+ parsed = JSON.parse(value);
1672
+ } catch (error) {
1673
+ console.error("Error parsing JSON:", error);
1674
+ return null;
1675
+ }
1676
+ return parsed;
1677
+ }, [options]);
1678
+ const readValue = useCallback(() => {
1679
+ if (IS_SERVER$4) return null;
1680
+ try {
1681
+ const raw = window.localStorage.getItem(key);
1682
+ return raw ? deserializer(raw) : null;
1683
+ } catch (error) {
1684
+ console.warn(`Error reading localStorage key “${key}”:`, error);
1685
+ return null;
1686
+ }
1687
+ }, [key, deserializer]);
1688
+ const [storedValue, setStoredValue] = useState(() => {
1689
+ if (initializeWithValue) return readValue();
1690
+ return void 0;
1691
+ });
1692
+ useEffect(() => {
1693
+ setStoredValue(readValue());
1694
+ }, [key]);
1695
+ const handleStorageChange = useCallback((event) => {
1696
+ if (event.key && event.key !== key) return;
1697
+ setStoredValue(readValue());
1698
+ }, [key, readValue]);
1699
+ useEventListener("storage", handleStorageChange);
1700
+ useEventListener("local-storage", handleStorageChange);
1701
+ return storedValue;
1702
+ }
1703
+
1704
+ //#endregion
1705
+ //#region src/useResizeObserver/useResizeObserver.ts
1706
+ const initialSize = {
1707
+ width: void 0,
1708
+ height: void 0
1709
+ };
1710
+ /**
1711
+ * Custom hook that observes the size of an element using the [`ResizeObserver API`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).
1712
+ * @template T - The type of the element to observe.
1713
+ * @param {UseResizeObserverOptions<T>} options - The options for the ResizeObserver.
1714
+ * @returns {Size} - The size of the observed element.
1715
+ * @public
1716
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-resize-observer)
1717
+ * @example
1718
+ * ```tsx
1719
+ * const myRef = useRef(null);
1720
+ * const { width = 0, height = 0 } = useResizeObserver({
1721
+ * ref: myRef,
1722
+ * box: 'content-box',
1723
+ * });
1724
+ *
1725
+ * <div ref={myRef}>Hello, world!</div>
1726
+ * ```
1727
+ */
1728
+ function useResizeObserver(options) {
1729
+ const { ref, box = "content-box" } = options;
1730
+ const [{ width, height }, setSize] = useState(initialSize);
1731
+ const isMounted = useIsMounted();
1732
+ const previousSize = useRef({ ...initialSize });
1733
+ const onResize = useRef(void 0);
1734
+ onResize.current = options.onResize;
1735
+ useEffect(() => {
1736
+ if (!ref.current) return;
1737
+ if (typeof window === "undefined" || !("ResizeObserver" in window)) return;
1738
+ const observer = new ResizeObserver(([entry]) => {
1739
+ if (!entry) return;
1740
+ const boxProp = box === "border-box" ? "borderBoxSize" : box === "device-pixel-content-box" ? "devicePixelContentBoxSize" : "contentBoxSize";
1741
+ const newWidth = extractSize(entry, boxProp, "inlineSize");
1742
+ const newHeight = extractSize(entry, boxProp, "blockSize");
1743
+ const hasChanged = previousSize.current.width !== newWidth || previousSize.current.height !== newHeight;
1744
+ if (hasChanged) {
1745
+ const newSize = {
1746
+ width: newWidth,
1747
+ height: newHeight
1748
+ };
1749
+ previousSize.current.width = newWidth;
1750
+ previousSize.current.height = newHeight;
1751
+ if (onResize.current) onResize.current(newSize);
1752
+ else if (isMounted()) setSize(newSize);
1753
+ }
1754
+ });
1755
+ observer.observe(ref.current, { box });
1756
+ return () => {
1757
+ observer.disconnect();
1758
+ };
1759
+ }, [
1760
+ box,
1761
+ ref,
1762
+ isMounted
1763
+ ]);
1764
+ return {
1765
+ width,
1766
+ height
1767
+ };
1768
+ }
1769
+ function extractSize(entry, box, sizeType) {
1770
+ if (!entry[box]) {
1771
+ if (box === "contentBoxSize") return entry.contentRect[sizeType === "inlineSize" ? "width" : "height"];
1772
+ return void 0;
1773
+ }
1774
+ return Array.isArray(entry[box]) ? entry[box][0][sizeType] : entry[box][sizeType];
1775
+ }
1776
+
1777
+ //#endregion
1778
+ //#region src/useScreen/useScreen.ts
1779
+ const IS_SERVER$3 = typeof window === "undefined";
1780
+ /**
1781
+ * Custom hook that tracks the [`screen`](https://developer.mozilla.org/en-US/docs/Web/API/Window/screen) dimensions and properties.
1782
+ * @param {?UseScreenOptions} [options] - The options for customizing the behavior of the hook (optional).
1783
+ * @returns {Screen | undefined} The current `Screen` object representing the screen dimensions and properties, or `undefined` if not available.
1784
+ * @public
1785
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-screen)
1786
+ * @example
1787
+ * ```tsx
1788
+ * const currentScreen = useScreen();
1789
+ * // Access properties of the current screen, such as width and height.
1790
+ * ```
1791
+ */
1792
+ function useScreen(options = {}) {
1793
+ let { initializeWithValue = true } = options;
1794
+ if (IS_SERVER$3) initializeWithValue = false;
1795
+ const readScreen = () => {
1796
+ if (IS_SERVER$3) return void 0;
1797
+ return window.screen;
1798
+ };
1799
+ const [screen, setScreen] = useState(() => {
1800
+ if (initializeWithValue) return readScreen();
1801
+ return void 0;
1802
+ });
1803
+ const debouncedSetScreen = useDebounceCallback(setScreen, options.debounceDelay);
1804
+ function handleSize() {
1805
+ const newScreen = readScreen();
1806
+ const setSize = options.debounceDelay ? debouncedSetScreen : setScreen;
1807
+ if (newScreen) {
1808
+ const { width, height, availHeight, availWidth, colorDepth, orientation, pixelDepth } = newScreen;
1809
+ setSize({
1810
+ width,
1811
+ height,
1812
+ availHeight,
1813
+ availWidth,
1814
+ colorDepth,
1815
+ orientation,
1816
+ pixelDepth
1817
+ });
1818
+ }
1819
+ }
1820
+ useEventListener("resize", handleSize);
1821
+ useIsomorphicLayoutEffect(() => {
1822
+ handleSize();
1823
+ }, []);
1824
+ return screen;
1825
+ }
1826
+
1827
+ //#endregion
1828
+ //#region src/useScript/useScript.ts
1829
+ const cachedScriptStatuses = new Map();
1830
+ /**
1831
+ * Gets the script element with the specified source URL.
1832
+ * @param {string} src - The source URL of the script to get.
1833
+ * @returns {{ node: HTMLScriptElement | null, status: UseScriptStatus | undefined }} The script element and its loading status.
1834
+ * @public
1835
+ * @example
1836
+ * ```tsx
1837
+ * const script = getScriptNode(src);
1838
+ * ```
1839
+ */
1840
+ function getScriptNode(src) {
1841
+ const node = document.querySelector(`script[src="${src}"]`);
1842
+ const status = node?.getAttribute("data-status");
1843
+ return {
1844
+ node,
1845
+ status
1846
+ };
1847
+ }
1848
+ /**
1849
+ * Custom hook that dynamically loads scripts and tracking their loading status.
1850
+ * @param {string | null} src - The source URL of the script to load. Set to `null` or omit to prevent loading (optional).
1851
+ * @param {UseScriptOptions} [options] - Additional options for controlling script loading (optional).
1852
+ * @returns {UseScriptStatus} The status of the script loading, which can be one of 'idle', 'loading', 'ready', or 'error'.
1853
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-script)
1854
+ * @example
1855
+ * const scriptStatus = useScript('https://example.com/script.js', { removeOnUnmount: true });
1856
+ * // Access the status of the script loading (e.g., 'loading', 'ready', 'error').
1857
+ */
1858
+ function useScript(src, options) {
1859
+ const [status, setStatus] = useState(() => {
1860
+ if (!src || options?.shouldPreventLoad) return "idle";
1861
+ if (typeof window === "undefined") return "loading";
1862
+ return cachedScriptStatuses.get(src) ?? "loading";
1863
+ });
1864
+ useEffect(() => {
1865
+ if (!src || options?.shouldPreventLoad) return;
1866
+ const cachedScriptStatus = cachedScriptStatuses.get(src);
1867
+ if (cachedScriptStatus === "ready" || cachedScriptStatus === "error") {
1868
+ setStatus(cachedScriptStatus);
1869
+ return;
1870
+ }
1871
+ const script = getScriptNode(src);
1872
+ let scriptNode = script.node;
1873
+ if (!scriptNode) {
1874
+ scriptNode = document.createElement("script");
1875
+ scriptNode.src = src;
1876
+ scriptNode.async = true;
1877
+ if (options?.id) scriptNode.id = options.id;
1878
+ scriptNode.setAttribute("data-status", "loading");
1879
+ document.body.appendChild(scriptNode);
1880
+ const setAttributeFromEvent = (event) => {
1881
+ const scriptStatus = event.type === "load" ? "ready" : "error";
1882
+ scriptNode?.setAttribute("data-status", scriptStatus);
1883
+ };
1884
+ scriptNode.addEventListener("load", setAttributeFromEvent);
1885
+ scriptNode.addEventListener("error", setAttributeFromEvent);
1886
+ } else setStatus(script.status ?? cachedScriptStatus ?? "loading");
1887
+ const setStateFromEvent = (event) => {
1888
+ const newStatus = event.type === "load" ? "ready" : "error";
1889
+ setStatus(newStatus);
1890
+ cachedScriptStatuses.set(src, newStatus);
1891
+ };
1892
+ scriptNode.addEventListener("load", setStateFromEvent);
1893
+ scriptNode.addEventListener("error", setStateFromEvent);
1894
+ return () => {
1895
+ if (scriptNode) {
1896
+ scriptNode.removeEventListener("load", setStateFromEvent);
1897
+ scriptNode.removeEventListener("error", setStateFromEvent);
1898
+ }
1899
+ if (scriptNode && options?.removeOnUnmount) {
1900
+ scriptNode.remove();
1901
+ cachedScriptStatuses.delete(src);
1902
+ }
1903
+ };
1904
+ }, [
1905
+ src,
1906
+ options?.shouldPreventLoad,
1907
+ options?.removeOnUnmount,
1908
+ options?.id
1909
+ ]);
1910
+ return status;
1911
+ }
1912
+
1913
+ //#endregion
1914
+ //#region src/useScrollLock/useScrollLock.ts
1915
+ const IS_SERVER$2 = typeof window === "undefined";
1916
+ /**
1917
+ * A custom hook that locks and unlocks scroll.
1918
+ * @param {UseScrollLockOptions} [options] - Options to configure the hook, by default it will lock the scroll automatically.
1919
+ * @returns {UseScrollLockReturn} - An object containing the lock and unlock functions.
1920
+ * @public
1921
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-scroll-lock)
1922
+ * @example
1923
+ * ```tsx
1924
+ * // Lock the scroll when the modal is mounted, and unlock it when it's unmounted
1925
+ * useScrollLock()
1926
+ * ```
1927
+ * @example
1928
+ * ```tsx
1929
+ * // Manually lock and unlock the scroll
1930
+ * const { lock, unlock } = useScrollLock({ autoLock: false })
1931
+ *
1932
+ * return (
1933
+ * <div>
1934
+ * <button onClick={lock}>Lock</button>
1935
+ * <button onClick={unlock}>Unlock</button>
1936
+ * </div>
1937
+ * )
1938
+ * ```
1939
+ */
1940
+ function useScrollLock(options = {}) {
1941
+ const { autoLock = true, lockTarget, widthReflow = true } = options;
1942
+ const [isLocked, setIsLocked] = useState(false);
1943
+ const target = useRef(null);
1944
+ const originalStyle = useRef(null);
1945
+ const lock = () => {
1946
+ if (target.current) {
1947
+ const { overflow, paddingRight } = target.current.style;
1948
+ originalStyle.current = {
1949
+ overflow,
1950
+ paddingRight
1951
+ };
1952
+ if (widthReflow) {
1953
+ const offsetWidth = target.current === document.body ? window.innerWidth : target.current.offsetWidth;
1954
+ const currentPaddingRight = parseInt(window.getComputedStyle(target.current).paddingRight, 10) || 0;
1955
+ const scrollbarWidth = offsetWidth - target.current.scrollWidth;
1956
+ target.current.style.paddingRight = `${scrollbarWidth + currentPaddingRight}px`;
1957
+ }
1958
+ target.current.style.overflow = "hidden";
1959
+ setIsLocked(true);
1960
+ }
1961
+ };
1962
+ const unlock = () => {
1963
+ if (target.current && originalStyle.current) {
1964
+ target.current.style.overflow = originalStyle.current.overflow;
1965
+ if (widthReflow) target.current.style.paddingRight = originalStyle.current.paddingRight;
1966
+ }
1967
+ setIsLocked(false);
1968
+ };
1969
+ useIsomorphicLayoutEffect(() => {
1970
+ if (IS_SERVER$2) return;
1971
+ if (lockTarget) target.current = typeof lockTarget === "string" ? document.querySelector(lockTarget) : lockTarget;
1972
+ if (!target.current) target.current = document.body;
1973
+ if (autoLock) lock();
1974
+ return () => {
1975
+ unlock();
1976
+ };
1977
+ }, [
1978
+ autoLock,
1979
+ lockTarget,
1980
+ widthReflow
1981
+ ]);
1982
+ return {
1983
+ isLocked,
1984
+ lock,
1985
+ unlock
1986
+ };
1987
+ }
1988
+
1989
+ //#endregion
1990
+ //#region src/useSessionStorage/useSessionStorage.ts
1991
+ const IS_SERVER$1 = typeof window === "undefined";
1992
+ /**
1993
+ * Custom hook that uses the [`sessionStorage API`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) to persist state across page reloads.
1994
+ * @template T - The type of the state to be stored in session storage.
1995
+ * @param {string} key - The key under which the value will be stored in session storage.
1996
+ * @param {T | (() => T)} initialValue - The initial value of the state or a function that returns the initial value.
1997
+ * @param {?UseSessionStorageOptions<T>} [options] - Options for customizing the behavior of serialization and deserialization (optional).
1998
+ * @returns {[T, Dispatch<SetStateAction<T>>, () => void]} A tuple containing the stored value, a function to set the value and a function to remove the key from storage.
1999
+ * @public
2000
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-session-storage)
2001
+ * @example
2002
+ * ```tsx
2003
+ * const [count, setCount, removeCount] = useSessionStorage('count', 0);
2004
+ * // Access the `count` value, the `setCount` function to update it and `removeCount` function to remove the key from storage.
2005
+ * ```
2006
+ */
2007
+ function useSessionStorage(key, initialValue, options = {}) {
2008
+ const { initializeWithValue = true } = options;
2009
+ const serializer = useCallback((value) => {
2010
+ if (options.serializer) return options.serializer(value);
2011
+ return JSON.stringify(value);
2012
+ }, [options]);
2013
+ const deserializer = useCallback((value) => {
2014
+ if (options.deserializer) return options.deserializer(value);
2015
+ if (value === "undefined") return void 0;
2016
+ const defaultValue = initialValue instanceof Function ? initialValue() : initialValue;
2017
+ let parsed;
2018
+ try {
2019
+ parsed = JSON.parse(value);
2020
+ } catch (error) {
2021
+ console.error("Error parsing JSON:", error);
2022
+ return defaultValue;
2023
+ }
2024
+ return parsed;
2025
+ }, [options, initialValue]);
2026
+ const readValue = useCallback(() => {
2027
+ const initialValueToUse = initialValue instanceof Function ? initialValue() : initialValue;
2028
+ if (IS_SERVER$1) return initialValueToUse;
2029
+ try {
2030
+ const raw = window.sessionStorage.getItem(key);
2031
+ return raw ? deserializer(raw) : initialValueToUse;
2032
+ } catch (error) {
2033
+ console.warn(`Error reading sessionStorage key “${key}”:`, error);
2034
+ return initialValueToUse;
2035
+ }
2036
+ }, [
2037
+ initialValue,
2038
+ key,
2039
+ deserializer
2040
+ ]);
2041
+ const [storedValue, setStoredValue] = useState(() => {
2042
+ if (initializeWithValue) return readValue();
2043
+ return initialValue instanceof Function ? initialValue() : initialValue;
2044
+ });
2045
+ const setValue = useEventCallback((value) => {
2046
+ if (IS_SERVER$1) console.warn(`Tried setting sessionStorage key “${key}” even though environment is not a client`);
2047
+ try {
2048
+ const newValue = value instanceof Function ? value(readValue()) : value;
2049
+ window.sessionStorage.setItem(key, serializer(newValue));
2050
+ setStoredValue(newValue);
2051
+ window.dispatchEvent(new StorageEvent("session-storage", { key }));
2052
+ } catch (error) {
2053
+ console.warn(`Error setting sessionStorage key “${key}”:`, error);
2054
+ }
2055
+ });
2056
+ const removeValue = useEventCallback(() => {
2057
+ if (IS_SERVER$1) console.warn(`Tried removing sessionStorage key “${key}” even though environment is not a client`);
2058
+ const defaultValue = initialValue instanceof Function ? initialValue() : initialValue;
2059
+ window.sessionStorage.removeItem(key);
2060
+ setStoredValue(defaultValue);
2061
+ window.dispatchEvent(new StorageEvent("session-storage", { key }));
2062
+ });
2063
+ useEffect(() => {
2064
+ setStoredValue(readValue());
2065
+ }, [key]);
2066
+ const handleStorageChange = useCallback((event) => {
2067
+ if (event.key && event.key !== key) return;
2068
+ setStoredValue(readValue());
2069
+ }, [key, readValue]);
2070
+ useEventListener("storage", handleStorageChange);
2071
+ useEventListener("session-storage", handleStorageChange);
2072
+ return [
2073
+ storedValue,
2074
+ setValue,
2075
+ removeValue
2076
+ ];
2077
+ }
2078
+
2079
+ //#endregion
2080
+ //#region src/useSet/useSet.ts
2081
+ /**
2082
+ * Custom hook that manages a [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) state with setter actions.
2083
+ * @template T - The type of elements in the set.
2084
+ * @param {Set<T> | T[]} [initialValue] - The initial value of the set as a Set or an array of values (optional).
2085
+ * @returns {UseSetReturn<T>} A tuple containing the set state and actions to interact with the set.
2086
+ * @public
2087
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-set)
2088
+ * @example
2089
+ * ```tsx
2090
+ * const [set, setActions] = useSet<string>(['hello']);
2091
+ * // Access the `set` state and use `setActions` to add, remove, toggle, or reset values.
2092
+ * ```
2093
+ */
2094
+ function useSet(initialValue = []) {
2095
+ const initialSet = initialValue instanceof Set ? initialValue : new Set(initialValue);
2096
+ const [set, setSet] = useState(initialSet);
2097
+ const actions = {
2098
+ add: useCallback((value) => {
2099
+ setSet((prev) => {
2100
+ const copy = new Set(prev);
2101
+ copy.add(value);
2102
+ return copy;
2103
+ });
2104
+ }, []),
2105
+ remove: useCallback((value) => {
2106
+ setSet((prev) => {
2107
+ const copy = new Set(prev);
2108
+ copy.delete(value);
2109
+ return copy;
2110
+ });
2111
+ }, []),
2112
+ toggle: useCallback((value) => {
2113
+ setSet((prev) => {
2114
+ const copy = new Set(prev);
2115
+ if (copy.has(value)) copy.delete(value);
2116
+ else copy.add(value);
2117
+ return copy;
2118
+ });
2119
+ }, []),
2120
+ has: useCallback((value) => set.has(value), [set]),
2121
+ clear: useCallback(() => {
2122
+ setSet(() => new Set());
2123
+ }, []),
2124
+ reset: useCallback(() => {
2125
+ setSet(() => new Set(initialSet));
2126
+ }, [])
2127
+ };
2128
+ return [set, actions];
2129
+ }
2130
+
2131
+ //#endregion
2132
+ //#region src/useStateList/useStateList.ts
2133
+ /**
2134
+ * Custom hook for navigating through a list of states.
2135
+ * Useful for step-by-step flows, carousels, or multi-step forms.
2136
+ * @template T - The type of the state values.
2137
+ * @param {T[]} stateSet - The array of possible states.
2138
+ * @returns {UseStateListReturn<T>} The current state and navigation functions.
2139
+ * @public
2140
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-state-list)
2141
+ * @example
2142
+ * ```tsx
2143
+ * const states = ['step1', 'step2', 'step3'];
2144
+ * const { state, next, prev, isFirst, isLast } = useStateList(states);
2145
+ *
2146
+ * return (
2147
+ * <div>
2148
+ * <p>Current: {state}</p>
2149
+ * <button onClick={prev} disabled={isFirst}>Back</button>
2150
+ * <button onClick={next} disabled={isLast}>Next</button>
2151
+ * </div>
2152
+ * );
2153
+ * ```
2154
+ */
2155
+ function useStateList(stateSet) {
2156
+ if (stateSet.length === 0) throw new Error("useStateList requires at least one state value");
2157
+ const stateSetRef = useRef(stateSet);
2158
+ const [currentIndex, setCurrentIndex] = useState(0);
2159
+ const state = stateSetRef.current[currentIndex];
2160
+ const isFirst = currentIndex === 0;
2161
+ const isLast = currentIndex === stateSetRef.current.length - 1;
2162
+ const prev = useCallback(() => {
2163
+ setCurrentIndex((index) => index > 0 ? index - 1 : index);
2164
+ }, []);
2165
+ const next = useCallback(() => {
2166
+ setCurrentIndex((index) => index < stateSetRef.current.length - 1 ? index + 1 : index);
2167
+ }, []);
2168
+ const setStateAt = useCallback((index) => {
2169
+ if (index >= 0 && index < stateSetRef.current.length) setCurrentIndex(index);
2170
+ }, []);
2171
+ const setState = useCallback((newState) => {
2172
+ const index = stateSetRef.current.indexOf(newState);
2173
+ if (index !== -1) setCurrentIndex(index);
2174
+ }, []);
2175
+ return {
2176
+ state,
2177
+ currentIndex,
2178
+ isFirst,
2179
+ isLast,
2180
+ prev,
2181
+ next,
2182
+ setStateAt,
2183
+ setState
2184
+ };
2185
+ }
2186
+
2187
+ //#endregion
2188
+ //#region src/useStep/useStep.ts
2189
+ /**
2190
+ * Custom hook that manages and navigates between steps in a multi-step process.
2191
+ * @param {number} maxStep - The maximum step in the process.
2192
+ * @returns {[number, UseStepActions]} An tuple containing the current step and helper functions for navigating steps.
2193
+ * @public
2194
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-step)
2195
+ * @example
2196
+ * ```tsx
2197
+ * const [currentStep, { goToNextStep, goToPrevStep, reset, canGoToNextStep, canGoToPrevStep, setStep }] = useStep(3);
2198
+ * // Access and use the current step and provided helper functions.
2199
+ * ```
2200
+ */
2201
+ function useStep(maxStep) {
2202
+ const [currentStep, setCurrentStep] = useState(1);
2203
+ const canGoToNextStep = currentStep + 1 <= maxStep;
2204
+ const canGoToPrevStep = currentStep - 1 > 0;
2205
+ const setStep = useCallback((step) => {
2206
+ const newStep = step instanceof Function ? step(currentStep) : step;
2207
+ if (newStep >= 1 && newStep <= maxStep) {
2208
+ setCurrentStep(newStep);
2209
+ return;
2210
+ }
2211
+ throw new Error("Step not valid");
2212
+ }, [maxStep, currentStep]);
2213
+ const goToNextStep = useCallback(() => {
2214
+ if (canGoToNextStep) setCurrentStep((step) => step + 1);
2215
+ }, [canGoToNextStep]);
2216
+ const goToPrevStep = useCallback(() => {
2217
+ if (canGoToPrevStep) setCurrentStep((step) => step - 1);
2218
+ }, [canGoToPrevStep]);
2219
+ const reset = useCallback(() => {
2220
+ setCurrentStep(1);
2221
+ }, []);
2222
+ return [currentStep, {
2223
+ goToNextStep,
2224
+ goToPrevStep,
2225
+ canGoToNextStep,
2226
+ canGoToPrevStep,
2227
+ setStep,
2228
+ reset
2229
+ }];
2230
+ }
2231
+
2232
+ //#endregion
2233
+ //#region src/useTernaryDarkMode/useTernaryDarkMode.ts
2234
+ const COLOR_SCHEME_QUERY = "(prefers-color-scheme: dark)";
2235
+ const LOCAL_STORAGE_KEY = "usehooks-ts-ternary-dark-mode";
2236
+ /**
2237
+ * Custom hook that manages ternary (system, dark, light) dark mode with local storage support.
2238
+ * @param {?TernaryDarkModeOptions | string} [options] - Options or the local storage key for the hook.
2239
+ * @returns {TernaryDarkModeReturn} An object containing the dark mode state and helper functions.
2240
+ * @public
2241
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-ternary-dark-mode)
2242
+ * @example
2243
+ * ```tsx
2244
+ * const { isDarkMode, ternaryDarkMode, setTernaryDarkMode, toggleTernaryDarkMode } = useTernaryDarkMode({ defaultValue: 'dark' });
2245
+ * // Access and use the dark mode state and provided helper functions.
2246
+ * ```
2247
+ */
2248
+ function useTernaryDarkMode({ defaultValue = "system", localStorageKey = LOCAL_STORAGE_KEY, initializeWithValue = true } = {}) {
2249
+ const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY, { initializeWithValue });
2250
+ const [mode, setMode] = useLocalStorage(localStorageKey, defaultValue, { initializeWithValue });
2251
+ const isDarkMode = mode === "dark" || mode === "system" && isDarkOS;
2252
+ const toggleTernaryDarkMode = () => {
2253
+ const modes = [
2254
+ "light",
2255
+ "system",
2256
+ "dark"
2257
+ ];
2258
+ setMode((prevMode) => {
2259
+ const prevIndex = modes.indexOf(prevMode);
2260
+ const nextIndex = (prevIndex + 1) % modes.length;
2261
+ return modes[nextIndex];
2262
+ });
2263
+ };
2264
+ return {
2265
+ isDarkMode,
2266
+ ternaryDarkMode: mode,
2267
+ setTernaryDarkMode: setMode,
2268
+ toggleTernaryDarkMode
2269
+ };
2270
+ }
2271
+
2272
+ //#endregion
2273
+ //#region src/useThrottle/useThrottle.ts
2274
+ /**
2275
+ * Custom hook that throttles a function, ensuring it is only called at most once per wait period.
2276
+ * @template T - The type of the function being throttled.
2277
+ * @param {T} fn - The function to throttle.
2278
+ * @param {number} wait - The number of milliseconds to throttle invocations to.
2279
+ * @returns {T} The throttled function.
2280
+ * @public
2281
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-throttle-fn)
2282
+ * @example
2283
+ * ```tsx
2284
+ * const throttledSave = useThrottleFn(saveToDatabase, 1000);
2285
+ * // Calling throttledSave() multiple times rapidly will only execute saveToDatabase once per second
2286
+ * ```
2287
+ */
2288
+ function useThrottleFn(fn, wait) {
2289
+ const timeoutRef = useRef(null);
2290
+ const lastCalledRef = useRef(0);
2291
+ const throttledFn = useCallback((...args) => {
2292
+ const now = Date.now();
2293
+ const remaining = wait - (now - lastCalledRef.current);
2294
+ if (remaining <= 0 || remaining > wait) {
2295
+ if (timeoutRef.current) {
2296
+ clearTimeout(timeoutRef.current);
2297
+ timeoutRef.current = null;
2298
+ }
2299
+ lastCalledRef.current = now;
2300
+ return fn(...args);
2301
+ }
2302
+ }, [fn, wait]);
2303
+ return throttledFn;
2304
+ }
2305
+ /**
2306
+ * Custom hook that throttles a value, ensuring updates occur at most once per wait period.
2307
+ * @template T - The type of the value being throttled.
2308
+ * @param {T} value - The value to throttle.
2309
+ * @param {number} wait - The number of milliseconds to throttle updates to.
2310
+ * @returns {T} The throttled value.
2311
+ * @public
2312
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-throttle)
2313
+ * @example
2314
+ * ```tsx
2315
+ * const throttledSearchTerm = useThrottle(searchTerm, 300);
2316
+ * // throttledSearchTerm will only update 300ms after the last change to searchTerm
2317
+ * ```
2318
+ */
2319
+ function useThrottle(value, wait) {
2320
+ const [throttledValue, setThrottledValue] = useState(value);
2321
+ const lastUpdatedRef = useRef(0);
2322
+ const previousValueRef = useRef(value);
2323
+ const now = Date.now();
2324
+ const remaining = wait - (now - lastUpdatedRef.current);
2325
+ if (remaining <= 0 || remaining > wait || previousValueRef.current !== value) {
2326
+ if (previousValueRef.current !== value) {
2327
+ setThrottledValue(value);
2328
+ lastUpdatedRef.current = now;
2329
+ previousValueRef.current = value;
2330
+ }
2331
+ }
2332
+ return throttledValue;
2333
+ }
2334
+
2335
+ //#endregion
2336
+ //#region src/useTimeout/useTimeout.ts
2337
+ /**
2338
+ * Custom hook that handles timeouts in React components using the [`setTimeout API`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout).
2339
+ * @param {() => void} callback - The function to be executed when the timeout elapses.
2340
+ * @param {number | null} delay - The duration (in milliseconds) for the timeout. Set to `null` to clear the timeout.
2341
+ * @returns {void} This hook does not return anything.
2342
+ * @public
2343
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-timeout)
2344
+ * @example
2345
+ * ```tsx
2346
+ * // Usage of useTimeout hook
2347
+ * useTimeout(() => {
2348
+ * // Code to be executed after the specified delay
2349
+ * }, 1000); // Set a timeout of 1000 milliseconds (1 second)
2350
+ * ```
2351
+ */
2352
+ function useTimeout(callback, delay) {
2353
+ const savedCallback = useRef(callback);
2354
+ useIsomorphicLayoutEffect(() => {
2355
+ savedCallback.current = callback;
2356
+ }, [callback]);
2357
+ useEffect(() => {
2358
+ if (!delay && delay !== 0) return;
2359
+ const id = setTimeout(() => {
2360
+ savedCallback.current();
2361
+ }, delay);
2362
+ return () => {
2363
+ clearTimeout(id);
2364
+ };
2365
+ }, [delay]);
2366
+ }
2367
+
2368
+ //#endregion
2369
+ //#region src/useToggle/useToggle.ts
2370
+ /**
2371
+ * Custom hook that manages a boolean toggle state in React components.
2372
+ * @param {boolean} [defaultValue] - The initial value for the toggle state.
2373
+ * @returns {[boolean, () => void, Dispatch<SetStateAction<boolean>>]} A tuple containing the current state,
2374
+ * a function to toggle the state, and a function to set the state explicitly.
2375
+ * @public
2376
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-toggle)
2377
+ * @example
2378
+ * ```tsx
2379
+ * const [isToggled, toggle, setToggle] = useToggle(); // Initial value is false
2380
+ * // OR
2381
+ * const [isToggled, toggle, setToggle] = useToggle(true); // Initial value is true
2382
+ * // Use isToggled in your component, toggle to switch the state, setToggle to set the state explicitly.
2383
+ * ```
2384
+ */
2385
+ function useToggle(defaultValue) {
2386
+ const [value, setValue] = useState(!!defaultValue);
2387
+ const toggle = useCallback(() => {
2388
+ setValue((x) => !x);
2389
+ }, []);
2390
+ return [
2391
+ value,
2392
+ toggle,
2393
+ setValue
2394
+ ];
2395
+ }
2396
+
2397
+ //#endregion
2398
+ //#region src/useUpdate/useUpdate.ts
2399
+ /**
2400
+ * Custom hook that returns a function to force a component re-render.
2401
+ * @returns {() => void} A function that, when called, will force the component to re-render.
2402
+ * @public
2403
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-update)
2404
+ * @example
2405
+ * ```tsx
2406
+ * const update = useUpdate();
2407
+ *
2408
+ * return (
2409
+ * <div>
2410
+ * <p>Current time: {Date.now()}</p>
2411
+ * <button onClick={update}>Update</button>
2412
+ * </div>
2413
+ * );
2414
+ * ```
2415
+ */
2416
+ function useUpdate() {
2417
+ const [, setTick] = useState(0);
2418
+ const update = useCallback(() => {
2419
+ setTick((tick) => tick + 1);
2420
+ }, []);
2421
+ return update;
2422
+ }
2423
+
2424
+ //#endregion
2425
+ //#region src/useUpdateEffect/useUpdateEffect.ts
2426
+ /**
2427
+ * A hook that runs an effect only when dependencies change, skipping the initial mount.
2428
+ * This is useful for responding to prop or state changes without running on first render.
2429
+ *
2430
+ * @param {() => void | (() => void)} effect - The effect function to run.
2431
+ * @param {unknown[]} deps - The dependency array.
2432
+ *
2433
+ * @example
2434
+ * ```tsx
2435
+ * function Component({ value }) {
2436
+ * useUpdateEffect(() => {
2437
+ * console.log('Value changed:', value);
2438
+ * }, [value]);
2439
+ *
2440
+ * return <div>{value}</div>;
2441
+ * }
2442
+ * ```
2443
+ */
2444
+ function useUpdateEffect(effect, deps) {
2445
+ const isFirstMount = useRef(true);
2446
+ useEffect(() => {
2447
+ if (isFirstMount.current) {
2448
+ isFirstMount.current = false;
2449
+ return;
2450
+ }
2451
+ return effect();
2452
+ }, deps);
2453
+ }
2454
+
2455
+ //#endregion
2456
+ //#region src/useWindowSize/useWindowSize.ts
2457
+ const IS_SERVER = typeof window === "undefined";
2458
+ /**
2459
+ * Custom hook that tracks the size of the window.
2460
+ * @param {?UseWindowSizeOptions} [options] - The options for customizing the behavior of the hook (optional).
2461
+ * @returns {object} An object containing the width and height of the window.
2462
+ * @public
2463
+ * @see [Documentation](https://usehooks-ts.com/react-hook/use-window-size)
2464
+ * @example
2465
+ * ```tsx
2466
+ * const { width = 0, height = 0 } = useWindowSize();
2467
+ * console.log(`Window size: ${width} x ${height}`);
2468
+ * ```
2469
+ */
2470
+ function useWindowSize(options = {}) {
2471
+ let { initializeWithValue = true } = options;
2472
+ if (IS_SERVER) initializeWithValue = false;
2473
+ const [windowSize, setWindowSize] = useState(() => {
2474
+ if (initializeWithValue) return {
2475
+ width: window.innerWidth,
2476
+ height: window.innerHeight
2477
+ };
2478
+ return {
2479
+ width: void 0,
2480
+ height: void 0
2481
+ };
2482
+ });
2483
+ const debouncedSetWindowSize = useDebounceCallback(setWindowSize, options.debounceDelay);
2484
+ function handleSize() {
2485
+ const setSize = options.debounceDelay ? debouncedSetWindowSize : setWindowSize;
2486
+ setSize({
2487
+ width: window.innerWidth,
2488
+ height: window.innerHeight
2489
+ });
2490
+ }
2491
+ useEventListener("resize", handleSize);
2492
+ useIsomorphicLayoutEffect(() => {
2493
+ handleSize();
2494
+ }, []);
2495
+ return windowSize;
2496
+ }
2497
+
2498
+ //#endregion
2499
+ export { DOTS, useAsync, useBoolean, useClickAnyWhere, useCopyToClipboard, useCountdown, useCounter, useDarkMode, useDebounceCallback, useDebounceValue, useDisclosure, useDocumentTitle, useEventCallback, useEventListener, useGeolocation, useHover, useIdle, useIntersectionObserver, useInterval, useIsClient, useIsMounted, useIsomorphicLayoutEffect, useList, useLocalStorage, useMap, useMediaQuery, useMemoizedFn, useNetwork, useOnClickOutside, usePageLeave, usePagination, usePermission, usePrevious, useQueue, useReadLocalStorage, useResizeObserver, useScreen, useScript, useScrollLock, useSessionStorage, useSet, useStateList, useStep, useTernaryDarkMode, useThrottle, useThrottleFn, useTimeout, useToggle, useUnmount, useUpdate, useUpdateEffect, useWindowSize };