@alessiofrittoli/react-hooks 3.8.0 → 4.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/README.md CHANGED
@@ -58,6 +58,7 @@
58
58
  - [`useIsPortrait`](#useisportrait)
59
59
  - [`useIsTouchDevice`](#useistouchdevice)
60
60
  - [`useMediaQuery`](#usemediaquery)
61
+ - [`usePreventContextMenu`](#usepreventcontextmenu)
61
62
  - [`useDocumentVisibility`](#usedocumentvisibility)
62
63
  - [`useWakeLock`](#usewakelock)
63
64
  - [DOM API](#dom-api)
@@ -131,9 +132,20 @@ export default config;
131
132
 
132
133
  #### Updates in the latest release 🎉
133
134
 
134
- - Added `useDocumentVisibility`. See [API Reference](#usedocumentvisibility) for more info.
135
- - Added `useWakeLock`. See [API Reference](#usewakelock) for more info.
136
- - Added `useDeferCallback`. See [API Reference](#usedefercallback) for more info.
135
+ - Added `usePreventContextMenu` hook. See [API Reference](#usepreventcontextmenu) for more info.
136
+
137
+ ---
138
+
139
+ Old updates
140
+
141
+ - Improved `useConnection` hook. It now returns
142
+ [`NetworkInformation`](https://github.com/alessiofrittoli/web-utils?tab=readme-ov-file#network-information) when available.
143
+ See [API Reference](#useconnection) for more info.
144
+ - Improved `useEventListener` hook types. It now supports `EventTarget` as listener targets.
145
+ See [API Reference](#useeventlistener) for more info.
146
+ - Added `useDocumentVisibility` hook. See [API Reference](#usedocumentvisibility) for more info.
147
+ - Added `useWakeLock` hook. See [API Reference](#usewakelock) for more info.
148
+ - Added `useDeferCallback` hook. See [API Reference](#usedefercallback) for more info.
137
149
 
138
150
  ---
139
151
 
@@ -318,13 +330,11 @@ Get states about Internet Connection.
318
330
 
319
331
  <summary style="cursor:pointer">Returns</summary>
320
332
 
321
- Type: `UseConnectionReturnType`
333
+ Type: `Connection`
322
334
 
323
- An object with the following properties:
335
+ An object defining network status and `NetworkInformation`.
324
336
 
325
- - `connection`: `online | offline` - Indicates the connections status.
326
- - `isOnline`: `boolean` - Indicates whether the current device is online.
327
- - `isOffline`: `boolean` - Indicates whether the current device is offline.
337
+ - See [`Connection`](https://github.com/alessiofrittoli/web-utils?tab=readme-ov-file#connection-interface) interface from [`@alessiofrittoli/web-utils`](https://www.npmjs.com/package/@alessiofrittoli/web-utils)
328
338
 
329
339
  </details>
330
340
 
@@ -496,6 +506,8 @@ Attach a new Event listener to the `Window`, `Document`, `MediaQueryList` or an
496
506
  | Parameter | Type | Description |
497
507
  | ------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
498
508
  | `type` | `K\|K[]` | The `Window` event name or an array of event names. |
509
+ | | | ⚠️ Please, make sure to memoize the event names array with `useMemo` |
510
+ | | | or declare that array outside your Component/hook in order to avoid infinite loops when a React state changes. |
499
511
  | `options` | `WindowListenerOptions<K>` | An object defining init options. |
500
512
  | `options.listener` | `WindowEventListener<K>` | The Window Event listener. |
501
513
  | `options.onLoad` | `() => void` | A custom callback executed before event listener get attached. |
@@ -513,6 +525,8 @@ Attach a new Event listener to the `Window`, `Document`, `MediaQueryList` or an
513
525
  | Parameter | Type | Description |
514
526
  | ------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
515
527
  | `type` | `K\|K[]` | The `Document` event name or an array of event names. |
528
+ | | | ⚠️ Please, make sure to memoize the event names array with `useMemo` |
529
+ | | | or declare that array outside your Component/hook in order to avoid infinite loops when a React state changes. |
516
530
  | `options` | `DocumentListenerOptions<K>` | An object defining init options. |
517
531
  | `options.target` | `Document\|null\|React.RefObject<Document\|null>` | The `Document` reference or a React RefObject of the `Document`. |
518
532
  | `options.listener` | `DocumentEventListener<K>` | The Document Event listener. |
@@ -531,6 +545,8 @@ Attach a new Event listener to the `Window`, `Document`, `MediaQueryList` or an
531
545
  | Parameter | Type | Description |
532
546
  | ------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
533
547
  | `type` | `K\|K[]` | The `HTMLElement` event name or an array of event names. |
548
+ | | | ⚠️ Please, make sure to memoize the event names array with `useMemo` |
549
+ | | | or declare that array outside your Component/hook in order to avoid infinite loops when a React state changes. |
534
550
  | `options` | `ElementListenerOptions<K>` | An object defining init options. |
535
551
  | `options.target` | `T\|React.RefObject<T\| null>` | The React RefObject of the target where the listener get attached to. |
536
552
  | `options.listener` | `ElementEventListener<K>` | The HTMLElement Event listener. |
@@ -564,15 +580,17 @@ Attach a new Event listener to the `Window`, `Document`, `MediaQueryList` or an
564
580
 
565
581
  <summary style="cursor:pointer">Custom events</summary>
566
582
 
567
- | Parameter | Type | Description |
568
- | ------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
569
- | `type` | `K\|K[]` | The custom event name or an array of event names. |
570
- | `options` | `CustomEventListenerOptions<T, K>` | An object defining init options. |
571
- | `options.target` | `Document\|HTMLElement\|null\|React.RefObject<Document\|HTMLElement\|null>` | (Optional) The target where the listener get attached to. If not set, the listener will get attached to the `Window` object. |
572
- | `options.listener` | `( event: T[ K ] ) => void` | The Event listener. |
573
- | `options.onLoad` | `() => void` | A custom callback executed before event listener get attached. |
574
- | `options.onCleanUp` | `() => void` | A custom callback executed after event listener get removed. |
575
- | `options.options` | `ListenerOptions` | Specifies characteristics about the event listener. See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options). |
583
+ | Parameter | Type | Description |
584
+ | ------------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
585
+ | `type` | `K\|K[]` | The custom event name or an array of event names. |
586
+ | | | ⚠️ Please, make sure to memoize the event names array with `useMemo` |
587
+ | | | or declare that array outside your Component/hook in order to avoid infinite loops when a React state changes. |
588
+ | `options` | `CustomEventListenerOptions<T, K>` | An object defining init options. |
589
+ | `options.target` | `Document\|EventTarget\|HTMLElement\|null\|React.RefObject<Document\|HTMLElement\|null>` | (Optional) The target where the listener get attached to. If not set, the listener will get attached to the `Window` object. |
590
+ | `options.listener` | `( event: T[ K ] ) => void` | The Event listener. |
591
+ | `options.onLoad` | `() => void` | A custom callback executed before event listener get attached. |
592
+ | `options.onCleanUp` | `() => void` | A custom callback executed after event listener get removed. |
593
+ | `options.options` | `ListenerOptions` | Specifies characteristics about the event listener. See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options). |
576
594
 
577
595
  </details>
578
596
 
@@ -725,6 +743,30 @@ export const MyComponent: React.FC = () => {
725
743
 
726
744
  ---
727
745
 
746
+ ###### Attach listeners to multiple events
747
+
748
+ ```tsx
749
+ import { useCallback, useState } from "react";
750
+ import { useEventListener } from "@alessiofrittoli/react-hooks";
751
+
752
+ /**
753
+ * We define events outside the Component to avoid array recreation when a state update is triggered.
754
+ *
755
+ * This prevents infinite loops for `useEventListener` life-cycle
756
+ */
757
+ const events: (keyof WindowEventMap)[] = ["resize", "scroll"];
758
+
759
+ export const MyComponent: React.FC = () => {
760
+ const [isInteracting, setIsIntercting] = useState(false);
761
+
762
+ useEventListener(events, {
763
+ listener: useCallback(() => {
764
+ setIsIntercting(true);
765
+ }, []),
766
+ });
767
+ };
768
+ ```
769
+
728
770
  </details>
729
771
 
730
772
  ---
@@ -859,6 +901,40 @@ useMediaQuery("(prefers-color-scheme: dark)", {
859
901
 
860
902
  ---
861
903
 
904
+ ##### `usePreventContextMenu`
905
+
906
+ Prevents the context menu from appearing on a specified target element.
907
+
908
+ <details>
909
+
910
+ <summary style="cursor:pointer">Parameters</summary>
911
+
912
+ | Parameter | Type | Default | Description |
913
+ | --------- | ----------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------- |
914
+ | `target` | `EventListenerTarget\|React.RefObject<EventListenerTarget>` | `window` | The target element or a `React.RefObject` where the context menu should be prevented. |
915
+ | | | | If not provided, the listener will be attached to the top `window`. |
916
+
917
+ </details>
918
+
919
+ ---
920
+
921
+ <details>
922
+
923
+ <summary style="cursor:pointer">Examples</summary>
924
+
925
+ ```tsx
926
+ // Prevent context menu on the entire top window.
927
+ usePreventContextMenu();
928
+
929
+ // Prevent context menu on a specific target.
930
+ const ref = useRef<HTMLDivElement>(null);
931
+ usePreventContextMenu(ref);
932
+ ```
933
+
934
+ </details>
935
+
936
+ ---
937
+
862
938
  ##### `useDocumentVisibility`
863
939
 
864
940
  Track the visibility state of the document (i.e., whether the page is visible or hidden).
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { DeferredTask } from '@alessiofrittoli/web-utils';
1
+ import { Connection, DeferredTask } from '@alessiofrittoli/web-utils';
2
2
  import * as _alessiofrittoli_math_utils from '@alessiofrittoli/math-utils';
3
3
  import { PaginateOptions } from '@alessiofrittoli/math-utils';
4
4
 
@@ -29,31 +29,12 @@ declare const useLocalStorage: <T = string>(key: string, initial?: T) => [Value<
29
29
  */
30
30
  declare const useSessionStorage: <T = string>(key: string, initial?: T) => [Value<T>, SetValue<Value<T>>];
31
31
 
32
- type Connection = 'online' | 'offline';
33
- declare const getState: (online: boolean) => "online" | "offline";
34
- interface UseConnectionReturnType {
35
- /**
36
- * Indicates the connections status.
37
- *
38
- */
39
- connection: Connection;
40
- /**
41
- * Indicates whether the current device is online.
42
- *
43
- */
44
- isOnline: boolean;
45
- /**
46
- * Indicates whether the current device is offline.
47
- *
48
- */
49
- isOffline: boolean;
50
- }
51
32
  /**
52
33
  * Get states about Internet Connection.
53
34
  *
54
- * @returns An object defining Internet Connection status. See {@link UseConnectionReturnType} for more info.
35
+ * @returns An object defining network status and `NetworkInformation`. See {@link Connection} for more info.
55
36
  */
56
- declare const useConnection: () => UseConnectionReturnType;
37
+ declare const useConnection: () => Connection;
57
38
 
58
39
  interface UseDarkModeOutput {
59
40
  /**
@@ -168,6 +149,7 @@ declare function useDocumentVisibility(otpions: StateDisabledUseDocumentVisibili
168
149
  */
169
150
  declare function useDocumentVisibility(otpions?: UseDocumentVisibilityOptions): boolean;
170
151
 
152
+ type EventListenerTarget = (Window | Document | HTMLElement | EventTarget | null | undefined);
171
153
  /**
172
154
  * Specifies characteristics about the event listener.
173
155
  *
@@ -276,6 +258,11 @@ interface CommonListenerOptions {
276
258
  options?: ListenerOptions;
277
259
  }
278
260
  interface WindowListenerOptions<K extends keyof WindowEventMap> extends CommonListenerOptions {
261
+ /**
262
+ * Optionally defines a `Window` context.
263
+ *
264
+ */
265
+ target?: Window;
279
266
  /**
280
267
  * The Window Event listener.
281
268
  *
@@ -288,7 +275,7 @@ interface DocumentListenerOptions<K extends keyof DocumentEventMap> extends Comm
288
275
  * The `Document` reference or a React RefObject of the `Document`.
289
276
  *
290
277
  */
291
- target: Document | null | React.RefObject<Document | null>;
278
+ target?: Document | null | React.RefObject<Document | null | undefined>;
292
279
  /**
293
280
  * The Document Event listener.
294
281
  *
@@ -296,14 +283,22 @@ interface DocumentListenerOptions<K extends keyof DocumentEventMap> extends Comm
296
283
  */
297
284
  listener: DocumentEventListener<K>;
298
285
  }
299
- interface ElementListenerOptions<T extends HTMLElement, K extends keyof HTMLElementEventMap> extends CommonListenerOptions {
286
+ interface EventTargetListenerOptions<T extends EventTarget, K extends keyof HTMLElementEventMap> extends CommonListenerOptions {
300
287
  /**
301
288
  * The React RefObject of the target where the listener get attached to.
302
289
  *
303
290
  */
304
- target: T | React.RefObject<T | null>;
291
+ target?: T | React.RefObject<T | null | undefined>;
292
+ /**
293
+ * The `EventTarget` Event listener.
294
+ *
295
+ * @param event The event object that implements the [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) interface.
296
+ */
297
+ listener: ElementEventListener<K>;
298
+ }
299
+ interface ElementListenerOptions<T extends HTMLElement, K extends keyof HTMLElementEventMap> extends Omit<EventTargetListenerOptions<T, K>, 'listener'> {
305
300
  /**
306
- * The HTMLElement Event listener.
301
+ * The `HTMLElement` Event listener.
307
302
  *
308
303
  * @param event The event object that implements the [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) interface.
309
304
  */
@@ -322,13 +317,13 @@ interface MediaQueryListenerOptions extends CommonListenerOptions {
322
317
  */
323
318
  listener: MediaQueryChangeListener;
324
319
  }
325
- interface CustomEventListenerOptions<T extends Record<string, Event>, K extends keyof T = keyof T> extends CommonListenerOptions {
320
+ interface CustomEventListenerOptions<T extends Record<string, U>, K extends keyof T = keyof T, U extends Event = Event> extends CommonListenerOptions {
326
321
  /**
327
322
  * The target where the listener get attached to.
328
323
  *
329
324
  * If not set, the listener will get attached to the `Window` object.
330
325
  */
331
- target?: Document | HTMLElement | null | React.RefObject<Document | HTMLElement | null>;
326
+ target?: EventListenerTarget | React.RefObject<EventListenerTarget>;
332
327
  /**
333
328
  * The Event listener.
334
329
  *
@@ -403,6 +398,28 @@ declare function useEventListener<K extends keyof WindowEventMap>(type: K | K[],
403
398
  * ```
404
399
  */
405
400
  declare function useEventListener<K extends keyof DocumentEventMap>(type: K | K[], options: DocumentListenerOptions<K>): void;
401
+ /**
402
+ * Attach a new Event listener to an `EventTarget` object.
403
+ *
404
+ * @param type The event name or an array of event names.
405
+ * @param options An object defining init options. See {@link EventTargetListenerOptions} for more info.
406
+ *
407
+ * @example
408
+ *
409
+ * #### Add a new `EventTarget` event listener
410
+ *
411
+ * ```ts
412
+ * // A React.RefObject<EventTarget | null> can be used too.
413
+ *
414
+ * useEventListener( 'click', {
415
+ * target: new EventTarget(),
416
+ * listener: useCallback( () => {
417
+ * console.log( 'click event on `EventTarget` dispatched.' )
418
+ * }, [] )
419
+ * } )
420
+ * ```
421
+ */
422
+ declare function useEventListener<T extends EventTarget, K extends keyof HTMLElementEventMap>(type: K | K[], options: EventTargetListenerOptions<T, K>): void;
406
423
  /**
407
424
  * Attach a new Event listener to a `HTMLElement` object.
408
425
  *
@@ -552,7 +569,7 @@ declare function useEventListener(type: 'change', options: MediaQueryListenerOpt
552
569
  * }, [] )
553
570
  *```
554
571
  */
555
- declare function useEventListener<T extends Record<string, Event>, K extends keyof T = keyof T>(type: K | K[], options: CustomEventListenerOptions<T, K>): void;
572
+ declare function useEventListener<T extends Record<string, U>, K extends keyof T = keyof T, U extends Event = Event>(type: K | K[], options: CustomEventListenerOptions<T, K>): void;
556
573
 
557
574
  /**
558
575
  * Check if device is portrait oriented.
@@ -1522,4 +1539,4 @@ declare function useTimeout<T extends readonly unknown[]>(callback: TimerHandler
1522
1539
  */
1523
1540
  declare const useLightTimeout: <T extends readonly unknown[]>(callback: TimerHandler<T>, options?: BasicTimerOptions<T>) => void;
1524
1541
 
1525
- export { type AddEventListenerOptions, type BasicTimerOptions, type ChangeHandler, type CommonListenerOptions, type Connection, type CustomEventListenerOptions, type DocumentEventListener, type DocumentListenerOptions, type ElementEventListener, type ElementListenerOptions, type GroupSelectHandler, type InputState, type InputType, type IntersectionState, type IsSelectedHandler, type ListenerOptions, type MarginType, type MarginValue, type MediaQueryChangeListener, type MediaQueryEventListener, type MediaQueryListenerOptions, type OnChangeHandler, type OnIntersectHandler, type OnIntersectStateHandler, type OnWakeLockRequestError, type ParseValueHandler, type ResetSelectionHandler, type SelectAllHandler, type SelectHandler, type SetSelectionHandler, type SetValue, type StartTimer, type StateDisabledUseDocumentVisibilityOptions, type StateTimerOptions, type StateTimerReturnType, type StopTimer, type TimerHandler, type TimerId, type TimerOptions, type TimerReturnType, type UseConnectionReturnType, type UseDarkModeOptions, type UseDarkModeOutput, type UseDocumentVisibilityOptions, type UseInViewOptions, type UseInViewReturnType, type UseInputOptions, type UseInputOutput, type UseIntervalWhenVisibleReturnType, type UseIntervalWhenVisibleStateReturnType, type UseMediaQueryOptions, type UseMediaQueryStateOptions, type UseSelectionReturnType, type UseWakeLock, type UseWakeLockBase, type UseWakeLockOptions, type ValidateValueHandler, type Value, type VisibilityChangeHandler, type WindowEventListener, type WindowListenerOptions, getState, useConnection, useDarkMode, useDebounce, useDeferCallback, useDocumentVisibility, useEffectOnce, useEventListener, useFocusTrap, useInView, useInput, useInterval, useIntervalWhenVisible, useIsClient, useIsFirstRender, useIsPortrait, useIsTouchDevice, useLightInterval, useLightTimeout, useLocalStorage, useMediaQuery, usePagination, useScrollBlock, useSelection, useSessionStorage, useStorage, useTimeout, useUpdateEffect, useWakeLock };
1542
+ export { type AddEventListenerOptions, type BasicTimerOptions, type ChangeHandler, type CommonListenerOptions, type CustomEventListenerOptions, type DocumentEventListener, type DocumentListenerOptions, type ElementEventListener, type ElementListenerOptions, type EventListenerTarget, type EventTargetListenerOptions, type GroupSelectHandler, type InputState, type InputType, type IntersectionState, type IsSelectedHandler, type ListenerOptions, type MarginType, type MarginValue, type MediaQueryChangeListener, type MediaQueryEventListener, type MediaQueryListenerOptions, type OnChangeHandler, type OnIntersectHandler, type OnIntersectStateHandler, type OnWakeLockRequestError, type ParseValueHandler, type ResetSelectionHandler, type SelectAllHandler, type SelectHandler, type SetSelectionHandler, type SetValue, type StartTimer, type StateDisabledUseDocumentVisibilityOptions, type StateTimerOptions, type StateTimerReturnType, type StopTimer, type TimerHandler, type TimerId, type TimerOptions, type TimerReturnType, type UseDarkModeOptions, type UseDarkModeOutput, type UseDocumentVisibilityOptions, type UseInViewOptions, type UseInViewReturnType, type UseInputOptions, type UseInputOutput, type UseIntervalWhenVisibleReturnType, type UseIntervalWhenVisibleStateReturnType, type UseMediaQueryOptions, type UseMediaQueryStateOptions, type UseSelectionReturnType, type UseWakeLock, type UseWakeLockBase, type UseWakeLockOptions, type ValidateValueHandler, type Value, type VisibilityChangeHandler, type WindowEventListener, type WindowListenerOptions, useConnection, useDarkMode, useDebounce, useDeferCallback, useDocumentVisibility, useEffectOnce, useEventListener, useFocusTrap, useInView, useInput, useInterval, useIntervalWhenVisible, useIsClient, useIsFirstRender, useIsPortrait, useIsTouchDevice, useLightInterval, useLightTimeout, useLocalStorage, useMediaQuery, usePagination, useScrollBlock, useSelection, useSessionStorage, useStorage, useTimeout, useUpdateEffect, useWakeLock };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { DeferredTask } from '@alessiofrittoli/web-utils';
1
+ import { Connection, DeferredTask } from '@alessiofrittoli/web-utils';
2
2
  import * as _alessiofrittoli_math_utils from '@alessiofrittoli/math-utils';
3
3
  import { PaginateOptions } from '@alessiofrittoli/math-utils';
4
4
 
@@ -29,31 +29,12 @@ declare const useLocalStorage: <T = string>(key: string, initial?: T) => [Value<
29
29
  */
30
30
  declare const useSessionStorage: <T = string>(key: string, initial?: T) => [Value<T>, SetValue<Value<T>>];
31
31
 
32
- type Connection = 'online' | 'offline';
33
- declare const getState: (online: boolean) => "online" | "offline";
34
- interface UseConnectionReturnType {
35
- /**
36
- * Indicates the connections status.
37
- *
38
- */
39
- connection: Connection;
40
- /**
41
- * Indicates whether the current device is online.
42
- *
43
- */
44
- isOnline: boolean;
45
- /**
46
- * Indicates whether the current device is offline.
47
- *
48
- */
49
- isOffline: boolean;
50
- }
51
32
  /**
52
33
  * Get states about Internet Connection.
53
34
  *
54
- * @returns An object defining Internet Connection status. See {@link UseConnectionReturnType} for more info.
35
+ * @returns An object defining network status and `NetworkInformation`. See {@link Connection} for more info.
55
36
  */
56
- declare const useConnection: () => UseConnectionReturnType;
37
+ declare const useConnection: () => Connection;
57
38
 
58
39
  interface UseDarkModeOutput {
59
40
  /**
@@ -168,6 +149,7 @@ declare function useDocumentVisibility(otpions: StateDisabledUseDocumentVisibili
168
149
  */
169
150
  declare function useDocumentVisibility(otpions?: UseDocumentVisibilityOptions): boolean;
170
151
 
152
+ type EventListenerTarget = (Window | Document | HTMLElement | EventTarget | null | undefined);
171
153
  /**
172
154
  * Specifies characteristics about the event listener.
173
155
  *
@@ -276,6 +258,11 @@ interface CommonListenerOptions {
276
258
  options?: ListenerOptions;
277
259
  }
278
260
  interface WindowListenerOptions<K extends keyof WindowEventMap> extends CommonListenerOptions {
261
+ /**
262
+ * Optionally defines a `Window` context.
263
+ *
264
+ */
265
+ target?: Window;
279
266
  /**
280
267
  * The Window Event listener.
281
268
  *
@@ -288,7 +275,7 @@ interface DocumentListenerOptions<K extends keyof DocumentEventMap> extends Comm
288
275
  * The `Document` reference or a React RefObject of the `Document`.
289
276
  *
290
277
  */
291
- target: Document | null | React.RefObject<Document | null>;
278
+ target?: Document | null | React.RefObject<Document | null | undefined>;
292
279
  /**
293
280
  * The Document Event listener.
294
281
  *
@@ -296,14 +283,22 @@ interface DocumentListenerOptions<K extends keyof DocumentEventMap> extends Comm
296
283
  */
297
284
  listener: DocumentEventListener<K>;
298
285
  }
299
- interface ElementListenerOptions<T extends HTMLElement, K extends keyof HTMLElementEventMap> extends CommonListenerOptions {
286
+ interface EventTargetListenerOptions<T extends EventTarget, K extends keyof HTMLElementEventMap> extends CommonListenerOptions {
300
287
  /**
301
288
  * The React RefObject of the target where the listener get attached to.
302
289
  *
303
290
  */
304
- target: T | React.RefObject<T | null>;
291
+ target?: T | React.RefObject<T | null | undefined>;
292
+ /**
293
+ * The `EventTarget` Event listener.
294
+ *
295
+ * @param event The event object that implements the [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) interface.
296
+ */
297
+ listener: ElementEventListener<K>;
298
+ }
299
+ interface ElementListenerOptions<T extends HTMLElement, K extends keyof HTMLElementEventMap> extends Omit<EventTargetListenerOptions<T, K>, 'listener'> {
305
300
  /**
306
- * The HTMLElement Event listener.
301
+ * The `HTMLElement` Event listener.
307
302
  *
308
303
  * @param event The event object that implements the [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) interface.
309
304
  */
@@ -322,13 +317,13 @@ interface MediaQueryListenerOptions extends CommonListenerOptions {
322
317
  */
323
318
  listener: MediaQueryChangeListener;
324
319
  }
325
- interface CustomEventListenerOptions<T extends Record<string, Event>, K extends keyof T = keyof T> extends CommonListenerOptions {
320
+ interface CustomEventListenerOptions<T extends Record<string, U>, K extends keyof T = keyof T, U extends Event = Event> extends CommonListenerOptions {
326
321
  /**
327
322
  * The target where the listener get attached to.
328
323
  *
329
324
  * If not set, the listener will get attached to the `Window` object.
330
325
  */
331
- target?: Document | HTMLElement | null | React.RefObject<Document | HTMLElement | null>;
326
+ target?: EventListenerTarget | React.RefObject<EventListenerTarget>;
332
327
  /**
333
328
  * The Event listener.
334
329
  *
@@ -403,6 +398,28 @@ declare function useEventListener<K extends keyof WindowEventMap>(type: K | K[],
403
398
  * ```
404
399
  */
405
400
  declare function useEventListener<K extends keyof DocumentEventMap>(type: K | K[], options: DocumentListenerOptions<K>): void;
401
+ /**
402
+ * Attach a new Event listener to an `EventTarget` object.
403
+ *
404
+ * @param type The event name or an array of event names.
405
+ * @param options An object defining init options. See {@link EventTargetListenerOptions} for more info.
406
+ *
407
+ * @example
408
+ *
409
+ * #### Add a new `EventTarget` event listener
410
+ *
411
+ * ```ts
412
+ * // A React.RefObject<EventTarget | null> can be used too.
413
+ *
414
+ * useEventListener( 'click', {
415
+ * target: new EventTarget(),
416
+ * listener: useCallback( () => {
417
+ * console.log( 'click event on `EventTarget` dispatched.' )
418
+ * }, [] )
419
+ * } )
420
+ * ```
421
+ */
422
+ declare function useEventListener<T extends EventTarget, K extends keyof HTMLElementEventMap>(type: K | K[], options: EventTargetListenerOptions<T, K>): void;
406
423
  /**
407
424
  * Attach a new Event listener to a `HTMLElement` object.
408
425
  *
@@ -552,7 +569,7 @@ declare function useEventListener(type: 'change', options: MediaQueryListenerOpt
552
569
  * }, [] )
553
570
  *```
554
571
  */
555
- declare function useEventListener<T extends Record<string, Event>, K extends keyof T = keyof T>(type: K | K[], options: CustomEventListenerOptions<T, K>): void;
572
+ declare function useEventListener<T extends Record<string, U>, K extends keyof T = keyof T, U extends Event = Event>(type: K | K[], options: CustomEventListenerOptions<T, K>): void;
556
573
 
557
574
  /**
558
575
  * Check if device is portrait oriented.
@@ -1522,4 +1539,4 @@ declare function useTimeout<T extends readonly unknown[]>(callback: TimerHandler
1522
1539
  */
1523
1540
  declare const useLightTimeout: <T extends readonly unknown[]>(callback: TimerHandler<T>, options?: BasicTimerOptions<T>) => void;
1524
1541
 
1525
- export { type AddEventListenerOptions, type BasicTimerOptions, type ChangeHandler, type CommonListenerOptions, type Connection, type CustomEventListenerOptions, type DocumentEventListener, type DocumentListenerOptions, type ElementEventListener, type ElementListenerOptions, type GroupSelectHandler, type InputState, type InputType, type IntersectionState, type IsSelectedHandler, type ListenerOptions, type MarginType, type MarginValue, type MediaQueryChangeListener, type MediaQueryEventListener, type MediaQueryListenerOptions, type OnChangeHandler, type OnIntersectHandler, type OnIntersectStateHandler, type OnWakeLockRequestError, type ParseValueHandler, type ResetSelectionHandler, type SelectAllHandler, type SelectHandler, type SetSelectionHandler, type SetValue, type StartTimer, type StateDisabledUseDocumentVisibilityOptions, type StateTimerOptions, type StateTimerReturnType, type StopTimer, type TimerHandler, type TimerId, type TimerOptions, type TimerReturnType, type UseConnectionReturnType, type UseDarkModeOptions, type UseDarkModeOutput, type UseDocumentVisibilityOptions, type UseInViewOptions, type UseInViewReturnType, type UseInputOptions, type UseInputOutput, type UseIntervalWhenVisibleReturnType, type UseIntervalWhenVisibleStateReturnType, type UseMediaQueryOptions, type UseMediaQueryStateOptions, type UseSelectionReturnType, type UseWakeLock, type UseWakeLockBase, type UseWakeLockOptions, type ValidateValueHandler, type Value, type VisibilityChangeHandler, type WindowEventListener, type WindowListenerOptions, getState, useConnection, useDarkMode, useDebounce, useDeferCallback, useDocumentVisibility, useEffectOnce, useEventListener, useFocusTrap, useInView, useInput, useInterval, useIntervalWhenVisible, useIsClient, useIsFirstRender, useIsPortrait, useIsTouchDevice, useLightInterval, useLightTimeout, useLocalStorage, useMediaQuery, usePagination, useScrollBlock, useSelection, useSessionStorage, useStorage, useTimeout, useUpdateEffect, useWakeLock };
1542
+ export { type AddEventListenerOptions, type BasicTimerOptions, type ChangeHandler, type CommonListenerOptions, type CustomEventListenerOptions, type DocumentEventListener, type DocumentListenerOptions, type ElementEventListener, type ElementListenerOptions, type EventListenerTarget, type EventTargetListenerOptions, type GroupSelectHandler, type InputState, type InputType, type IntersectionState, type IsSelectedHandler, type ListenerOptions, type MarginType, type MarginValue, type MediaQueryChangeListener, type MediaQueryEventListener, type MediaQueryListenerOptions, type OnChangeHandler, type OnIntersectHandler, type OnIntersectStateHandler, type OnWakeLockRequestError, type ParseValueHandler, type ResetSelectionHandler, type SelectAllHandler, type SelectHandler, type SetSelectionHandler, type SetValue, type StartTimer, type StateDisabledUseDocumentVisibilityOptions, type StateTimerOptions, type StateTimerReturnType, type StopTimer, type TimerHandler, type TimerId, type TimerOptions, type TimerReturnType, type UseDarkModeOptions, type UseDarkModeOutput, type UseDocumentVisibilityOptions, type UseInViewOptions, type UseInViewReturnType, type UseInputOptions, type UseInputOutput, type UseIntervalWhenVisibleReturnType, type UseIntervalWhenVisibleStateReturnType, type UseMediaQueryOptions, type UseMediaQueryStateOptions, type UseSelectionReturnType, type UseWakeLock, type UseWakeLockBase, type UseWakeLockOptions, type ValidateValueHandler, type Value, type VisibilityChangeHandler, type WindowEventListener, type WindowListenerOptions, useConnection, useDarkMode, useDebounce, useDeferCallback, useDocumentVisibility, useEffectOnce, useEventListener, useFocusTrap, useInView, useInput, useInterval, useIntervalWhenVisible, useIsClient, useIsFirstRender, useIsPortrait, useIsTouchDevice, useLightInterval, useLightTimeout, useLocalStorage, useMediaQuery, usePagination, useScrollBlock, useSelection, useSessionStorage, useStorage, useTimeout, useUpdateEffect, useWakeLock };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _react = require('react');var _webutils = require('@alessiofrittoli/web-utils');var I=(e,n,r="local")=>{let t=_react.useCallback.call(void 0, ()=>_nullishCoalesce((r==="local"?_webutils.LocalStorage:_webutils.SessionStorage).get(e), () => (n)),[r,e,n]),[i,a]=_react.useState.call(void 0, n),s=_react.useCallback.call(void 0, o=>{a(c=>{let p=o instanceof Function?o(c):o;return(typeof window<"u"&&r==="local"?_webutils.LocalStorage:_webutils.SessionStorage).set(e,p),p})},[r,e]);return _react.useEffect.call(void 0, ()=>{a(t())},[t]),[i,s]};var $=(e,n)=>I(e,n,"local");var it=(e,n)=>I(e,n,"session");var j=e=>e?"online":"offline",pt= exports.useConnection =()=>{let[e,n]=_react.useState.call(void 0, j(!0)),r=e==="online",t=e==="offline",i=_react.useCallback.call(void 0, ()=>n(j(navigator.onLine)),[]);return B(["online","offline"],{listener:i,onLoad:i}),{connection:e,isOnline:r,isOffline:t}};var V=()=>{let e=_react.useRef.call(void 0, !0);return e.current?(e.current=!1,!0):e.current};var L=(e,n)=>{let r=V();_react.useEffect.call(void 0, ()=>{if(!r)return e()},n)};var H=e=>typeof e=="string"?_webutils.isEmpty.call(void 0, e):!e,w={value:"",isTouched:!1,isValid:!0},N=(e,n)=>{switch(n.type){case"TOUCHED":return{...e,isTouched:!0};case"CHANGE":return{...e,value:n.value};case"BLUR":return{...e,value:e.value,isTouched:!H(_nullishCoalesce(e.value, () => ("")))};case"RESET":return w}};var ht=(e={})=>{let{inputRef:n}=e,{initialValue:r}=e,{touchTimeout:t=600}=e,{validate:i,parse:a}=e,{onChange:s}=e,[o,c]=_react.useReducer.call(void 0, N,{...w,value:r}),p=_react.useMemo.call(void 0, ()=>a?a(o.value):o.value,[o.value,a]),u=_react.useMemo.call(void 0, ()=>typeof i=="function"?i(p):!0,[p,i]),{isTouched:l}=o,d=H(p),f=!u&&l||!!r&&!u;L(()=>{let T=setTimeout(()=>{H(p)||c({type:"TOUCHED"})},t);return()=>clearTimeout(T)},[p,t]);let y=_react.useCallback.call(void 0, T=>{let{target:x}=T,{type:v}=x,k=v==="checkbox"?x.checked:x.value;c({type:"CHANGE",value:k}),_optionalChain([s, 'optionalCall', _2 => _2(a?a(k):k)])},[s,a]),S=_react.useCallback.call(void 0, ()=>{c({type:"BLUR"})},[]),b=_react.useCallback.call(void 0, ()=>{c({type:"TOUCHED"})},[]),E=_react.useCallback.call(void 0, T=>{c({type:"CHANGE",value:T})},[]),O=_react.useCallback.call(void 0, ()=>{_optionalChain([n, 'optionalAccess', _3 => _3.current, 'optionalAccess', _4 => _4.focus, 'call', _5 => _5()])},[n]),D=_react.useCallback.call(void 0, ()=>{c({type:"RESET"})},[]);return{value:p,isTouched:l,isValid:u,isEmpty:d,hasError:f,changeHandler:y,blurHandler:S,setValue:E,submit:b,focus:O,reset:D}};var Ct=(e,n)=>_react.useCallback.call(void 0, _webutils.deferCallback.call(void 0, e),n);var R=e=>{let n=V();_react.useEffect.call(void 0, ()=>{if(n)return e()},[])};var _=()=>{let[e,n]=_react.useState.call(void 0, !1);return _react.useEffect.call(void 0, ()=>n(!0),[]),e};var _mathutils = require('@alessiofrittoli/math-utils');var jt=(e={})=>_react.useMemo.call(void 0, ()=>_mathutils.paginate.call(void 0, e),[e]);var _t=(e,n=[])=>{let[r,t]=_react.useState.call(void 0, n),i=r.length>0,a=u=>r.includes(u),s=_react.useCallback.call(void 0, u=>t(l=>{let d=new Set(l);return d.has(u)?d.delete(u):d.add(u),Array.from(d.values())}),[]),o=_react.useCallback.call(void 0, u=>{t(l=>{if(l.length===0)return[u];let d=[...e],f=d.indexOf(l.at(0)),y=d.indexOf(u);if(f>y){let S=[...d].reverse(),b=S.indexOf(l.at(0)),E=S.indexOf(u);return S.slice(b,E+1)}return d.slice(f,y+1)})},[e]),c=_react.useCallback.call(void 0, ()=>{t(e)},[e]),p=_react.useCallback.call(void 0, u=>t(u?n:[]),[n]);return{selection:r,hasSelection:i,isSelected:a,setSelection:t,select:s,groupSelect:o,selectAll:c,resetSelection:p}};function M(e,n={}){let{updateState:r=!0,onChange:t}=n,[i,a]=_react.useState.call(void 0, _webutils.getMediaMatches.call(void 0, e)),s=_react.useCallback.call(void 0, ()=>{let o=_webutils.getMediaMatches.call(void 0, e);r&&a(o),_optionalChain([t, 'optionalCall', _6 => _6(o)])},[e,r,t]);if(_react.useEffect.call(void 0, ()=>{let o=window.matchMedia(e),{matches:c}=o;return r&&a(c),_optionalChain([t, 'optionalCall', _7 => _7(c)]),o.addEventListener("change",s),()=>{o.removeEventListener("change",s)}},[e,r,t,s]),!!r)return i}var sn=(e={})=>{let n=_(),r=M("(prefers-color-scheme: dark)"),{initial:t=r,docClassNames:i=[]}=e,[a,s]=$("dark-mode",t),o=_nullishCoalesce(a, () => (r)),[c,p]=i,u=_react.useRef.call(void 0, {light:"",dark:""});return L(()=>{s(r)},[r,s]),_react.useEffect.call(void 0, ()=>{c&&document.documentElement.classList.toggle(c,o),p&&document.documentElement.classList.toggle(p,!o)},[o,c,p]),_react.useEffect.call(void 0, ()=>{document.head.querySelectorAll('meta[name="theme-color"]').forEach(l=>{let d=l.getAttribute("media"),f=l.getAttribute("content");if(f){if(!d||d==="(prefers-color-scheme: light)"){u.current.light=f;return}u.current.dark=f}})},[]),L(()=>{let l=u.current.dark,d=u.current.light;a&&!l||!a&&!d||document.head.querySelectorAll('meta[name="theme-color"]').forEach(f=>{f.setAttribute("content",a?l:d)})},[a]),{isDarkMode:n?o:!1,isDarkOS:n?r:!1,toggleDarkMode:_react.useCallback.call(void 0, ()=>s(l=>!l),[s]),enableDarkMode:_react.useCallback.call(void 0, ()=>s(!0),[s]),disableDarkMode:_react.useCallback.call(void 0, ()=>s(!1),[s])}};function X(e={}){let{updateState:n=!0,onVisibilityChange:r}=e,[t,i]=_react.useState.call(void 0, !1),a=_react.useCallback.call(void 0, ()=>{let s=!document.hidden;n&&i(s),_optionalChain([r, 'optionalCall', _8 => _8(s)])},[n,r]);if(R(()=>{n&&i(!document.hidden)}),_react.useEffect.call(void 0, ()=>(document.addEventListener("visibilitychange",a),()=>document.removeEventListener("visibilitychange",a)),[a]),n)return t}function B(e,n){let{target:r,query:t,options:i,listener:a,onLoad:s,onCleanUp:o}=n;_react.useEffect.call(void 0, ()=>{let c=Array.isArray(e)?e:[e],p=_nullishCoalesce((t?window.matchMedia(t):r&&"current"in r?r.current:r), () => (window));if(p.addEventListener)return _optionalChain([s, 'optionalCall', _9 => _9()]),c.map(u=>{p.addEventListener(u,a,i)}),()=>{c.map(u=>{p.removeEventListener(u,a,i)}),_optionalChain([o, 'optionalCall', _10 => _10()])}},[e,r,t,i,a,s,o])}var En=()=>M(_webutils.portraitMediaQuery);var On=()=>M("(pointer: coarse)");var In=(e={})=>{let{onMount:n=!0,onError:r}=e,[t,i]=_react.useState.call(void 0, null),a=_react.useRef.call(void 0, n),s=t&&!t.released||!1,o=_react.useCallback.call(void 0, async l=>!t||t.released?navigator.wakeLock.request("screen").then(d=>{i(d),_optionalChain([l, 'optionalCall', _11 => _11()])}).catch(r||console.error):t,[t,r]),c=_react.useCallback.call(void 0, async()=>_optionalChain([t, 'optionalAccess', _12 => _12.release, 'call', _13 => _13()]),[t]),p=_react.useCallback.call(void 0, async()=>{o(()=>{a.current=!0})},[o]),u=_react.useCallback.call(void 0, async()=>{a.current=!1,c()},[c]);return X({updateState:!1,onVisibilityChange:_react.useCallback.call(void 0, l=>{if(l&&!s&&a.current)return o();if(!l)return c()},[s,o,c])}),R(()=>{s||!n||o()}),_react.useEffect.call(void 0, ()=>{if(!t)return;let l=()=>{t.released&&i(null)};return t.addEventListener("release",l),()=>{t.removeEventListener("release",l),!t.released&&t.release()}},[t]),{wakeLock:t,enabled:s,requestWakeLock:p,releaseWakeLock:u}};var Ue=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),Cn= exports.useFocusTrap =e=>{let n=_react.useMemo.call(void 0, ()=>_optionalChain([e, 'optionalAccess', _14 => _14.current]),[e]),[r,t]=_react.useState.call(void 0, !1),i=_react.useRef.call(void 0, null),a=_react.useCallback.call(void 0, o=>{i.current=document.activeElement;let c=o||n||!1;if(c)return t(c)},[n]),s=_react.useCallback.call(void 0, ()=>{_optionalChain([i, 'access', _15 => _15.current, 'optionalAccess', _16 => _16.focus, 'call', _17 => _17()]),t(!1)},[]);return _react.useEffect.call(void 0, ()=>{if(!r)return;let o=c=>{if(c.key!=="Tab")return;let p=Array.from(r.querySelectorAll(Ue)),u=p.at(0),l=p.at(-1);if(!c.shiftKey){document.activeElement===l&&(c.preventDefault(),_optionalChain([u, 'optionalAccess', _18 => _18.focus, 'call', _19 => _19()]));return}document.activeElement===u&&(c.preventDefault(),_optionalChain([l, 'optionalAccess', _20 => _20.focus, 'call', _21 => _21()]))};return document.addEventListener("keydown",o),()=>{document.removeEventListener("keydown",o)}},[r]),[a,s]};var Wn=(e,n={})=>{let{initial:r=!1,once:t,amount:i,margin:a,root:s,enable:o=!0}=n,{onEnter:c,onExit:p,onIntersect:u}=n,l=_react.useRef.call(void 0, !0),[d,f]=_react.useState.call(void 0, r),[y,S]=_react.useState.call(void 0, o),b=_react.useRef.call(void 0, null),E=_react.useRef.call(void 0, !1),O=_react.useMemo.call(void 0, ()=>{if(!y||typeof IntersectionObserver>"u")return;let D=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([T],x)=>{if(!T)return;let v=T.isIntersecting;try{if(E.current=!v&&!!b.current,v&&c&&await c({entry:T,observer:x}),E.current&&p&&await p({entry:T,observer:x}),u&&(v||!v&&b.current!=null)){let k={isEntering:v,isExiting:E.current};await u({entry:T,observer:x,...k})}if(b.current=v,!l.current)return;f(v)}catch(k){console.error(k)}v&&t&&x.disconnect()},{root:s||void 0,rootMargin:a,threshold:D})}catch(T){console.error(T)}},[s,a,i,t,y,c,p,u]);return _react.useEffect.call(void 0, ()=>{if(l.current=!0,!(!y||!e.current||!O))return O.observe(e.current),()=>{l.current=!1,O.disconnect()}},[e,O,y]),{inView:d,enabled:y,observer:O,isExiting:E.current,setInView:f,setEnabled:S}};var qn=e=>{let n=_react.useMemo.call(void 0, ()=>_optionalChain([e, 'optionalAccess', _22 => _22.current]),[e]),r=_react.useCallback.call(void 0, ()=>_webutils.blockScroll.call(void 0, n||void 0),[n]),t=_react.useCallback.call(void 0, ()=>_webutils.restoreScroll.call(void 0, n||void 0),[n]);return[r,t]};var te=(e,n={})=>{let{delay:r=1,args:t}=n;_react.useEffect.call(void 0, ()=>{let i=setTimeout(e,r,...t||[]);return()=>clearTimeout(i)},[r,t,e])};var zn=(e,n=500)=>{let[r,t]=_react.useState.call(void 0, e),i=_react.useMemo.call(void 0, ()=>[e],[e]);return te(t,{delay:n,args:i}),r};function ne(e,n={}){let{delay:r=1,args:t,autoplay:i=!0,runOnStart:a=!1,updateState:s=!1}=n,o=_react.useRef.call(void 0, void 0),[c,p]=_react.useState.call(void 0, i),u=_react.useCallback.call(void 0, ()=>o.current?(clearInterval(o.current),o.current=void 0,!0):!1,[]),l=_react.useCallback.call(void 0, ()=>{let f=u();return a&&(t?e(...t):e()),o.current=setInterval(e,r,...t||[]),!f&&s&&p(!0),o.current},[r,t,s,a,e,u]),d=_react.useCallback.call(void 0, ()=>{u()&&s&&p(!1)},[s,u]);return _react.useEffect.call(void 0, ()=>(i&&l(),()=>d()),[i,l,d]),s?{isActive:c,start:l,stop:d}:{start:l,stop:d}}var nr=(e,n={})=>{let{delay:r=1,args:t}=n;_react.useEffect.call(void 0, ()=>{let i=setInterval(e,r,...t||[]);return()=>clearInterval(i)},[r,t,e])};function ar(e,n={}){let{autoplay:r=!0}=n,t=ne(e,{autoplay:!1,...n}),{start:i,stop:a}=t,s=_react.useCallback.call(void 0, ()=>document.hidden?a():i(),[i,a]),o=_react.useCallback.call(void 0, ()=>{if(document.addEventListener("visibilitychange",s),!document.hidden)return i()},[i,s]),c=_react.useCallback.call(void 0, ()=>{a(),document.removeEventListener("visibilitychange",s)},[a,s]);return _react.useEffect.call(void 0, ()=>(r&&o(),()=>c()),[r,o,c]),{...t,start:o,stop:c}}function pr(e,n={}){let{delay:r=1,args:t,autoplay:i=!0,runOnStart:a=!1,updateState:s=!1}=n,o=_react.useRef.call(void 0, void 0),[c,p]=_react.useState.call(void 0, i),u=_react.useCallback.call(void 0, ()=>o.current?(clearTimeout(o.current),o.current=void 0,!0):!1,[]),l=_react.useCallback.call(void 0, ()=>{let f=u();return a&&(t?_optionalChain([e, 'optionalCall', _23 => _23(...t)]):_optionalChain([e, 'optionalCall', _24 => _24()])),o.current=setTimeout(()=>{if(o.current=void 0,s&&p(!1),t)return _optionalChain([e, 'optionalCall', _25 => _25(...t)]);_optionalChain([e, 'optionalCall', _26 => _26()])},r),!f&&s&&p(!0),o.current},[r,t,s,a,e,u]),d=_react.useCallback.call(void 0, ()=>{u()&&s&&p(!1)},[s,u]);return _react.useEffect.call(void 0, ()=>(i&&l(),()=>d()),[i,l,d]),s?{isActive:c,start:l,stop:d}:{start:l,stop:d}}exports.getState = j; exports.useConnection = pt; exports.useDarkMode = sn; exports.useDebounce = zn; exports.useDeferCallback = Ct; exports.useDocumentVisibility = X; exports.useEffectOnce = R; exports.useEventListener = B; exports.useFocusTrap = Cn; exports.useInView = Wn; exports.useInput = ht; exports.useInterval = ne; exports.useIntervalWhenVisible = ar; exports.useIsClient = _; exports.useIsFirstRender = V; exports.useIsPortrait = En; exports.useIsTouchDevice = On; exports.useLightInterval = nr; exports.useLightTimeout = te; exports.useLocalStorage = $; exports.useMediaQuery = M; exports.usePagination = jt; exports.useScrollBlock = qn; exports.useSelection = _t; exports.useSessionStorage = it; exports.useStorage = I; exports.useTimeout = pr; exports.useUpdateEffect = L; exports.useWakeLock = In;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _react = require('react');var _webutils = require('@alessiofrittoli/web-utils');var I=(e,n,r="local")=>{let t=_react.useCallback.call(void 0, ()=>_nullishCoalesce((r==="local"?_webutils.LocalStorage:_webutils.SessionStorage).get(e), () => (n)),[r,e,n]),[i,a]=_react.useState.call(void 0, n),s=_react.useCallback.call(void 0, o=>{a(c=>{let p=o instanceof Function?o(c):o;return(typeof window<"u"&&r==="local"?_webutils.LocalStorage:_webutils.SessionStorage).set(e,p),p})},[r,e]);return _react.useEffect.call(void 0, ()=>{a(t())},[t]),[i,s]};var j=(e,n)=>I(e,n,"local");var ct=(e,n)=>I(e,n,"session");var ae={onLine:!0},ce=["online","offline"],ft= exports.useConnection =()=>{let[e,n]=_react.useState.call(void 0, ae),r=_react.useCallback.call(void 0, ()=>{n(_webutils.getConnection.call(void 0, ))},[]);return w("change",{target:e.network,listener:r,onLoad:r}),w(ce,{listener:r}),e};var V=()=>{let e=_react.useRef.call(void 0, !0);return e.current?(e.current=!1,!0):e.current};var L=(e,n)=>{let r=V();_react.useEffect.call(void 0, ()=>{if(!r)return e()},n)};var H=e=>typeof e=="string"?_webutils.isEmpty.call(void 0, e):!e,U={value:"",isTouched:!1,isValid:!0},B=(e,n)=>{switch(n.type){case"TOUCHED":return{...e,isTouched:!0};case"CHANGE":return{...e,value:n.value};case"BLUR":return{...e,value:e.value,isTouched:!H(_nullishCoalesce(e.value, () => ("")))};case"RESET":return U}};var Ht=(e={})=>{let{inputRef:n}=e,{initialValue:r}=e,{touchTimeout:t=600}=e,{validate:i,parse:a}=e,{onChange:s}=e,[o,c]=_react.useReducer.call(void 0, B,{...U,value:r}),p=_react.useMemo.call(void 0, ()=>a?a(o.value):o.value,[o.value,a]),u=_react.useMemo.call(void 0, ()=>typeof i=="function"?i(p):!0,[p,i]),{isTouched:l}=o,d=H(p),f=!u&&l||!!r&&!u;L(()=>{let T=setTimeout(()=>{H(p)||c({type:"TOUCHED"})},t);return()=>clearTimeout(T)},[p,t]);let y=_react.useCallback.call(void 0, T=>{let{target:x}=T,{type:v}=x,k=v==="checkbox"?x.checked:x.value;c({type:"CHANGE",value:k}),_optionalChain([s, 'optionalCall', _2 => _2(a?a(k):k)])},[s,a]),S=_react.useCallback.call(void 0, ()=>{c({type:"BLUR"})},[]),g=_react.useCallback.call(void 0, ()=>{c({type:"TOUCHED"})},[]),E=_react.useCallback.call(void 0, T=>{c({type:"CHANGE",value:T})},[]),O=_react.useCallback.call(void 0, ()=>{_optionalChain([n, 'optionalAccess', _3 => _3.current, 'optionalAccess', _4 => _4.focus, 'call', _5 => _5()])},[n]),D=_react.useCallback.call(void 0, ()=>{c({type:"RESET"})},[]);return{value:p,isTouched:l,isValid:u,isEmpty:d,hasError:f,changeHandler:y,blurHandler:S,setValue:E,submit:g,focus:O,reset:D}};var Ut=(e,n)=>_react.useCallback.call(void 0, _webutils.deferCallback.call(void 0, e),n);var R=e=>{let n=V();_react.useEffect.call(void 0, ()=>{if(n)return e()},[])};var G=()=>{let[e,n]=_react.useState.call(void 0, !1);return _react.useEffect.call(void 0, ()=>n(!0),[]),e};var _mathutils = require('@alessiofrittoli/math-utils');var Gt=(e={})=>_react.useMemo.call(void 0, ()=>_mathutils.paginate.call(void 0, e),[e]);var Xt=(e,n=[])=>{let[r,t]=_react.useState.call(void 0, n),i=r.length>0,a=u=>r.includes(u),s=_react.useCallback.call(void 0, u=>t(l=>{let d=new Set(l);return d.has(u)?d.delete(u):d.add(u),Array.from(d.values())}),[]),o=_react.useCallback.call(void 0, u=>{t(l=>{if(l.length===0)return[u];let d=[...e],f=d.indexOf(l.at(0)),y=d.indexOf(u);if(f>y){let S=[...d].reverse(),g=S.indexOf(l.at(0)),E=S.indexOf(u);return S.slice(g,E+1)}return d.slice(f,y+1)})},[e]),c=_react.useCallback.call(void 0, ()=>{t(e)},[e]),p=_react.useCallback.call(void 0, u=>t(u?n:[]),[n]);return{selection:r,hasSelection:i,isSelected:a,setSelection:t,select:s,groupSelect:o,selectAll:c,resetSelection:p}};function M(e,n={}){let{updateState:r=!0,onChange:t}=n,[i,a]=_react.useState.call(void 0, _webutils.getMediaMatches.call(void 0, e)),s=_react.useCallback.call(void 0, ()=>{let o=_webutils.getMediaMatches.call(void 0, e);r&&a(o),_optionalChain([t, 'optionalCall', _6 => _6(o)])},[e,r,t]);if(_react.useEffect.call(void 0, ()=>{let o=window.matchMedia(e),{matches:c}=o;return r&&a(c),_optionalChain([t, 'optionalCall', _7 => _7(c)]),o.addEventListener("change",s),()=>{o.removeEventListener("change",s)}},[e,r,t,s]),!!r)return i}var un=(e={})=>{let n=G(),r=M("(prefers-color-scheme: dark)"),{initial:t=r,docClassNames:i=[]}=e,[a,s]=j("dark-mode",t),o=_nullishCoalesce(a, () => (r)),[c,p]=i,u=_react.useRef.call(void 0, {light:"",dark:""});return L(()=>{s(r)},[r,s]),_react.useEffect.call(void 0, ()=>{c&&document.documentElement.classList.toggle(c,o),p&&document.documentElement.classList.toggle(p,!o)},[o,c,p]),_react.useEffect.call(void 0, ()=>{document.head.querySelectorAll('meta[name="theme-color"]').forEach(l=>{let d=l.getAttribute("media"),f=l.getAttribute("content");if(f){if(!d||d==="(prefers-color-scheme: light)"){u.current.light=f;return}u.current.dark=f}})},[]),L(()=>{let l=u.current.dark,d=u.current.light;a&&!l||!a&&!d||document.head.querySelectorAll('meta[name="theme-color"]').forEach(f=>{f.setAttribute("content",a?l:d)})},[a]),{isDarkMode:n?o:!1,isDarkOS:n?r:!1,toggleDarkMode:_react.useCallback.call(void 0, ()=>s(l=>!l),[s]),enableDarkMode:_react.useCallback.call(void 0, ()=>s(!0),[s]),disableDarkMode:_react.useCallback.call(void 0, ()=>s(!1),[s])}};function J(e={}){let{updateState:n=!0,onVisibilityChange:r}=e,[t,i]=_react.useState.call(void 0, !1),a=_react.useCallback.call(void 0, ()=>{let s=!document.hidden;n&&i(s),_optionalChain([r, 'optionalCall', _8 => _8(s)])},[n,r]);if(R(()=>{n&&i(!document.hidden)}),_react.useEffect.call(void 0, ()=>(document.addEventListener("visibilitychange",a),()=>document.removeEventListener("visibilitychange",a)),[a]),n)return t}function w(e,n){let{target:r,query:t,options:i,listener:a,onLoad:s,onCleanUp:o}=n;_react.useEffect.call(void 0, ()=>{let c=Array.isArray(e)?e:[e],p=_nullishCoalesce((t?window.matchMedia(t):r&&"current"in r?r.current:r), () => (window));if(p.addEventListener)return _optionalChain([s, 'optionalCall', _9 => _9()]),c.map(u=>{p.addEventListener(u,a,i)}),()=>{c.map(u=>{p.removeEventListener(u,a,i)}),_optionalChain([o, 'optionalCall', _10 => _10()])}},[e,r,t,i,a,s,o])}var gn=()=>M(_webutils.portraitMediaQuery);var Mn=()=>M("(pointer: coarse)");var Rn=(e={})=>{let{onMount:n=!0,onError:r}=e,[t,i]=_react.useState.call(void 0, null),a=_react.useRef.call(void 0, n),s=t&&!t.released||!1,o=_react.useCallback.call(void 0, async l=>!t||t.released?navigator.wakeLock.request("screen").then(d=>{i(d),_optionalChain([l, 'optionalCall', _11 => _11()])}).catch(r||console.error):t,[t,r]),c=_react.useCallback.call(void 0, async()=>_optionalChain([t, 'optionalAccess', _12 => _12.release, 'call', _13 => _13()]),[t]),p=_react.useCallback.call(void 0, async()=>{o(()=>{a.current=!0})},[o]),u=_react.useCallback.call(void 0, async()=>{a.current=!1,c()},[c]);return J({updateState:!1,onVisibilityChange:_react.useCallback.call(void 0, l=>{if(l&&!s&&a.current)return o();if(!l)return c()},[s,o,c])}),R(()=>{s||!n||o()}),_react.useEffect.call(void 0, ()=>{if(!t)return;let l=()=>{t.released&&i(null)};return t.addEventListener("release",l),()=>{t.removeEventListener("release",l),!t.released&&t.release()}},[t]),{wakeLock:t,enabled:s,requestWakeLock:p,releaseWakeLock:u}};var Ke=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),Un= exports.useFocusTrap =e=>{let n=_react.useMemo.call(void 0, ()=>_optionalChain([e, 'optionalAccess', _14 => _14.current]),[e]),[r,t]=_react.useState.call(void 0, !1),i=_react.useRef.call(void 0, null),a=_react.useCallback.call(void 0, o=>{i.current=document.activeElement;let c=o||n||!1;if(c)return t(c)},[n]),s=_react.useCallback.call(void 0, ()=>{_optionalChain([i, 'access', _15 => _15.current, 'optionalAccess', _16 => _16.focus, 'call', _17 => _17()]),t(!1)},[]);return _react.useEffect.call(void 0, ()=>{if(!r)return;let o=c=>{if(c.key!=="Tab")return;let p=Array.from(r.querySelectorAll(Ke)),u=p.at(0),l=p.at(-1);if(!c.shiftKey){document.activeElement===l&&(c.preventDefault(),_optionalChain([u, 'optionalAccess', _18 => _18.focus, 'call', _19 => _19()]));return}document.activeElement===u&&(c.preventDefault(),_optionalChain([l, 'optionalAccess', _20 => _20.focus, 'call', _21 => _21()]))};return document.addEventListener("keydown",o),()=>{document.removeEventListener("keydown",o)}},[r]),[a,s]};var Qn=(e,n={})=>{let{initial:r=!1,once:t,amount:i,margin:a,root:s,enable:o=!0}=n,{onEnter:c,onExit:p,onIntersect:u}=n,l=_react.useRef.call(void 0, !0),[d,f]=_react.useState.call(void 0, r),[y,S]=_react.useState.call(void 0, o),g=_react.useRef.call(void 0, null),E=_react.useRef.call(void 0, !1),O=_react.useMemo.call(void 0, ()=>{if(!y||typeof IntersectionObserver>"u")return;let D=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([T],x)=>{if(!T)return;let v=T.isIntersecting;try{if(E.current=!v&&!!g.current,v&&c&&await c({entry:T,observer:x}),E.current&&p&&await p({entry:T,observer:x}),u&&(v||!v&&g.current!=null)){let k={isEntering:v,isExiting:E.current};await u({entry:T,observer:x,...k})}if(g.current=v,!l.current)return;f(v)}catch(k){console.error(k)}v&&t&&x.disconnect()},{root:s||void 0,rootMargin:a,threshold:D})}catch(T){console.error(T)}},[s,a,i,t,y,c,p,u]);return _react.useEffect.call(void 0, ()=>{if(l.current=!0,!(!y||!e.current||!O))return O.observe(e.current),()=>{l.current=!1,O.disconnect()}},[e,O,y]),{inView:d,enabled:y,observer:O,isExiting:E.current,setInView:f,setEnabled:S}};var jn=e=>{let n=_react.useMemo.call(void 0, ()=>_optionalChain([e, 'optionalAccess', _22 => _22.current]),[e]),r=_react.useCallback.call(void 0, ()=>_webutils.blockScroll.call(void 0, n||void 0),[n]),t=_react.useCallback.call(void 0, ()=>_webutils.restoreScroll.call(void 0, n||void 0),[n]);return[r,t]};var ee=(e,n={})=>{let{delay:r=1,args:t}=n;_react.useEffect.call(void 0, ()=>{let i=setTimeout(e,r,...t||[]);return()=>clearTimeout(i)},[r,t,e])};var Yn=(e,n=500)=>{let[r,t]=_react.useState.call(void 0, e),i=_react.useMemo.call(void 0, ()=>[e],[e]);return ee(t,{delay:n,args:i}),r};function te(e,n={}){let{delay:r=1,args:t,autoplay:i=!0,runOnStart:a=!1,updateState:s=!1}=n,o=_react.useRef.call(void 0, void 0),[c,p]=_react.useState.call(void 0, i),u=_react.useCallback.call(void 0, ()=>o.current?(clearInterval(o.current),o.current=void 0,!0):!1,[]),l=_react.useCallback.call(void 0, ()=>{let f=u();return a&&(t?e(...t):e()),o.current=setInterval(e,r,...t||[]),!f&&s&&p(!0),o.current},[r,t,s,a,e,u]),d=_react.useCallback.call(void 0, ()=>{u()&&s&&p(!1)},[s,u]);return _react.useEffect.call(void 0, ()=>(i&&l(),()=>d()),[i,l,d]),s?{isActive:c,start:l,stop:d}:{start:l,stop:d}}var sr=(e,n={})=>{let{delay:r=1,args:t}=n;_react.useEffect.call(void 0, ()=>{let i=setInterval(e,r,...t||[]);return()=>clearInterval(i)},[r,t,e])};function lr(e,n={}){let{autoplay:r=!0}=n,t=te(e,{autoplay:!1,...n}),{start:i,stop:a}=t,s=_react.useCallback.call(void 0, ()=>document.hidden?a():i(),[i,a]),o=_react.useCallback.call(void 0, ()=>{if(document.addEventListener("visibilitychange",s),!document.hidden)return i()},[i,s]),c=_react.useCallback.call(void 0, ()=>{a(),document.removeEventListener("visibilitychange",s)},[a,s]);return _react.useEffect.call(void 0, ()=>(r&&o(),()=>c()),[r,o,c]),{...t,start:o,stop:c}}function fr(e,n={}){let{delay:r=1,args:t,autoplay:i=!0,runOnStart:a=!1,updateState:s=!1}=n,o=_react.useRef.call(void 0, void 0),[c,p]=_react.useState.call(void 0, i),u=_react.useCallback.call(void 0, ()=>o.current?(clearTimeout(o.current),o.current=void 0,!0):!1,[]),l=_react.useCallback.call(void 0, ()=>{let f=u();return a&&(t?_optionalChain([e, 'optionalCall', _23 => _23(...t)]):_optionalChain([e, 'optionalCall', _24 => _24()])),o.current=setTimeout(()=>{if(o.current=void 0,s&&p(!1),t)return _optionalChain([e, 'optionalCall', _25 => _25(...t)]);_optionalChain([e, 'optionalCall', _26 => _26()])},r),!f&&s&&p(!0),o.current},[r,t,s,a,e,u]),d=_react.useCallback.call(void 0, ()=>{u()&&s&&p(!1)},[s,u]);return _react.useEffect.call(void 0, ()=>(i&&l(),()=>d()),[i,l,d]),s?{isActive:c,start:l,stop:d}:{start:l,stop:d}}exports.useConnection = ft; exports.useDarkMode = un; exports.useDebounce = Yn; exports.useDeferCallback = Ut; exports.useDocumentVisibility = J; exports.useEffectOnce = R; exports.useEventListener = w; exports.useFocusTrap = Un; exports.useInView = Qn; exports.useInput = Ht; exports.useInterval = te; exports.useIntervalWhenVisible = lr; exports.useIsClient = G; exports.useIsFirstRender = V; exports.useIsPortrait = gn; exports.useIsTouchDevice = Mn; exports.useLightInterval = sr; exports.useLightTimeout = ee; exports.useLocalStorage = j; exports.useMediaQuery = M; exports.usePagination = Gt; exports.useScrollBlock = jn; exports.useSelection = Xt; exports.useSessionStorage = ct; exports.useStorage = I; exports.useTimeout = fr; exports.useUpdateEffect = L; exports.useWakeLock = Rn;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{useCallback as q,useEffect as oe,useState as se}from"react";import{LocalStorage as P,SessionStorage as $}from"@alessiofrittoli/web-utils";var V=(e,n,r="local")=>{let t=q(()=>(r==="local"?P:$).get(e)??n,[r,e,n]),[i,a]=se(n),s=q(o=>{a(c=>{let p=o instanceof Function?o(c):o;return(typeof window<"u"&&r==="local"?P:$).set(e,p),p})},[r,e]);return oe(()=>{a(t())},[t]),[i,s]};var j=(e,n)=>V(e,n,"local");var at=(e,n)=>V(e,n,"session");import{useCallback as ie,useState as ae}from"react";var B=e=>e?"online":"offline",dt=()=>{let[e,n]=ae(B(!0)),r=e==="online",t=e==="offline",i=ie(()=>n(B(navigator.onLine)),[]);return N(["online","offline"],{listener:i,onLoad:i}),{connection:e,isOnline:r,isOffline:t}};import{useCallback as W,useEffect as X,useRef as ke}from"react";import{useCallback as M,useMemo as _,useReducer as pe}from"react";import{useEffect as ue}from"react";import{useRef as ce}from"react";var H=()=>{let e=ce(!0);return e.current?(e.current=!1,!0):e.current};var h=(e,n)=>{let r=H();ue(()=>{if(!r)return e()},n)};import{isEmpty as le}from"@alessiofrittoli/web-utils";var R=e=>typeof e=="string"?le(e):!e,U={value:"",isTouched:!1,isValid:!0},G=(e,n)=>{switch(n.type){case"TOUCHED":return{...e,isTouched:!0};case"CHANGE":return{...e,value:n.value};case"BLUR":return{...e,value:e.value,isTouched:!R(e.value??"")};case"RESET":return U}};var It=(e={})=>{let{inputRef:n}=e,{initialValue:r}=e,{touchTimeout:t=600}=e,{validate:i,parse:a}=e,{onChange:s}=e,[o,c]=pe(G,{...U,value:r}),p=_(()=>a?a(o.value):o.value,[o.value,a]),u=_(()=>typeof i=="function"?i(p):!0,[p,i]),{isTouched:l}=o,d=R(p),T=!u&&l||!!r&&!u;h(()=>{let y=setTimeout(()=>{R(p)||c({type:"TOUCHED"})},t);return()=>clearTimeout(y)},[p,t]);let v=M(y=>{let{target:S}=y,{type:E}=S,g=E==="checkbox"?S.checked:S.value;c({type:"CHANGE",value:g}),s?.(a?a(g):g)},[s,a]),b=M(()=>{c({type:"BLUR"})},[]),O=M(()=>{c({type:"TOUCHED"})},[]),x=M(y=>{c({type:"CHANGE",value:y})},[]),k=M(()=>{n?.current?.focus()},[n]),w=M(()=>{c({type:"RESET"})},[]);return{value:p,isTouched:l,isValid:u,isEmpty:d,hasError:T,changeHandler:v,blurHandler:b,setValue:x,submit:O,focus:k,reset:w}};import{useCallback as de}from"react";import{deferCallback as me}from"@alessiofrittoli/web-utils";var Dt=(e,n)=>de(me(e),n);import{useEffect as fe}from"react";var C=e=>{let n=H();fe(()=>{if(n)return e()},[])};import{useEffect as Te,useState as ye}from"react";var z=()=>{let[e,n]=ye(!1);return Te(()=>n(!0),[]),e};import{useMemo as ve}from"react";import{paginate as Ee}from"@alessiofrittoli/math-utils";var Bt=(e={})=>ve(()=>Ee(e),[e]);import{useCallback as D,useState as xe}from"react";var zt=(e,n=[])=>{let[r,t]=xe(n),i=r.length>0,a=u=>r.includes(u),s=D(u=>t(l=>{let d=new Set(l);return d.has(u)?d.delete(u):d.add(u),Array.from(d.values())}),[]),o=D(u=>{t(l=>{if(l.length===0)return[u];let d=[...e],T=d.indexOf(l.at(0)),v=d.indexOf(u);if(T>v){let b=[...d].reverse(),O=b.indexOf(l.at(0)),x=b.indexOf(u);return b.slice(O,x+1)}return d.slice(T,v+1)})},[e]),c=D(()=>{t(e)},[e]),p=D(u=>t(u?n:[]),[n]);return{selection:r,hasSelection:i,isSelected:a,setSelection:t,select:s,groupSelect:o,selectAll:c,resetSelection:p}};import{useCallback as Se,useEffect as be,useState as Oe}from"react";import{getMediaMatches as J}from"@alessiofrittoli/web-utils";function L(e,n={}){let{updateState:r=!0,onChange:t}=n,[i,a]=Oe(J(e)),s=Se(()=>{let o=J(e);r&&a(o),t?.(o)},[e,r,t]);if(be(()=>{let o=window.matchMedia(e),{matches:c}=o;return r&&a(c),t?.(c),o.addEventListener("change",s),()=>{o.removeEventListener("change",s)}},[e,r,t,s]),!!r)return i}var an=(e={})=>{let n=z(),r=L("(prefers-color-scheme: dark)"),{initial:t=r,docClassNames:i=[]}=e,[a,s]=j("dark-mode",t),o=a??r,[c,p]=i,u=ke({light:"",dark:""});return h(()=>{s(r)},[r,s]),X(()=>{c&&document.documentElement.classList.toggle(c,o),p&&document.documentElement.classList.toggle(p,!o)},[o,c,p]),X(()=>{document.head.querySelectorAll('meta[name="theme-color"]').forEach(l=>{let d=l.getAttribute("media"),T=l.getAttribute("content");if(T){if(!d||d==="(prefers-color-scheme: light)"){u.current.light=T;return}u.current.dark=T}})},[]),h(()=>{let l=u.current.dark,d=u.current.light;a&&!l||!a&&!d||document.head.querySelectorAll('meta[name="theme-color"]').forEach(T=>{T.setAttribute("content",a?l:d)})},[a]),{isDarkMode:n?o:!1,isDarkOS:n?r:!1,toggleDarkMode:W(()=>s(l=>!l),[s]),enableDarkMode:W(()=>s(!0),[s]),disableDarkMode:W(()=>s(!1),[s])}};import{useCallback as ge,useEffect as Me,useState as Le}from"react";function Y(e={}){let{updateState:n=!0,onVisibilityChange:r}=e,[t,i]=Le(!1),a=ge(()=>{let s=!document.hidden;n&&i(s),r?.(s)},[n,r]);if(C(()=>{n&&i(!document.hidden)}),Me(()=>(document.addEventListener("visibilitychange",a),()=>document.removeEventListener("visibilitychange",a)),[a]),n)return t}import{useEffect as he}from"react";function N(e,n){let{target:r,query:t,options:i,listener:a,onLoad:s,onCleanUp:o}=n;he(()=>{let c=Array.isArray(e)?e:[e],p=(t?window.matchMedia(t):r&&"current"in r?r.current:r)??window;if(p.addEventListener)return s?.(),c.map(u=>{p.addEventListener(u,a,i)}),()=>{c.map(u=>{p.removeEventListener(u,a,i)}),o?.()}},[e,r,t,i,a,s,o])}import{portraitMediaQuery as Ie}from"@alessiofrittoli/web-utils";var xn=()=>L(Ie);var kn=()=>L("(pointer: coarse)");import{useCallback as I,useEffect as Ve,useRef as He,useState as Re}from"react";var Vn=(e={})=>{let{onMount:n=!0,onError:r}=e,[t,i]=Re(null),a=He(n),s=t&&!t.released||!1,o=I(async l=>!t||t.released?navigator.wakeLock.request("screen").then(d=>{i(d),l?.()}).catch(r||console.error):t,[t,r]),c=I(async()=>t?.release(),[t]),p=I(async()=>{o(()=>{a.current=!0})},[o]),u=I(async()=>{a.current=!1,c()},[c]);return Y({updateState:!1,onVisibilityChange:I(l=>{if(l&&!s&&a.current)return o();if(!l)return c()},[s,o,c])}),C(()=>{s||!n||o()}),Ve(()=>{if(!t)return;let l=()=>{t.released&&i(null)};return t.addEventListener("release",l),()=>{t.removeEventListener("release",l),!t.released&&t.release()}},[t]),{wakeLock:t,enabled:s,requestWakeLock:p,releaseWakeLock:u}};import{useCallback as Z,useEffect as Ce,useMemo as De,useRef as we,useState as Ue}from"react";var We=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),Dn=e=>{let n=De(()=>e?.current,[e]),[r,t]=Ue(!1),i=we(null),a=Z(o=>{i.current=document.activeElement;let c=o||n||!1;if(c)return t(c)},[n]),s=Z(()=>{i.current?.focus(),t(!1)},[]);return Ce(()=>{if(!r)return;let o=c=>{if(c.key!=="Tab")return;let p=Array.from(r.querySelectorAll(We)),u=p.at(0),l=p.at(-1);if(!c.shiftKey){document.activeElement===l&&(c.preventDefault(),u?.focus());return}document.activeElement===u&&(c.preventDefault(),l?.focus())};return document.addEventListener("keydown",o),()=>{document.removeEventListener("keydown",o)}},[r]),[a,s]};import{useEffect as Ke,useMemo as Ae,useRef as K,useState as ee}from"react";var Kn=(e,n={})=>{let{initial:r=!1,once:t,amount:i,margin:a,root:s,enable:o=!0}=n,{onEnter:c,onExit:p,onIntersect:u}=n,l=K(!0),[d,T]=ee(r),[v,b]=ee(o),O=K(null),x=K(!1),k=Ae(()=>{if(!v||typeof IntersectionObserver>"u")return;let w=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([y],S)=>{if(!y)return;let E=y.isIntersecting;try{if(x.current=!E&&!!O.current,E&&c&&await c({entry:y,observer:S}),x.current&&p&&await p({entry:y,observer:S}),u&&(E||!E&&O.current!=null)){let g={isEntering:E,isExiting:x.current};await u({entry:y,observer:S,...g})}if(O.current=E,!l.current)return;T(E)}catch(g){console.error(g)}E&&t&&S.disconnect()},{root:s||void 0,rootMargin:a,threshold:w})}catch(y){console.error(y)}},[s,a,i,t,v,c,p,u]);return Ke(()=>{if(l.current=!0,!(!v||!e.current||!k))return k.observe(e.current),()=>{l.current=!1,k.disconnect()}},[e,k,v]),{inView:d,enabled:v,observer:k,isExiting:x.current,setInView:T,setEnabled:b}};import{useCallback as te,useMemo as Qe}from"react";import{blockScroll as Fe,restoreScroll as qe}from"@alessiofrittoli/web-utils";var Pn=e=>{let n=Qe(()=>e?.current,[e]),r=te(()=>Fe(n||void 0),[n]),t=te(()=>qe(n||void 0),[n]);return[r,t]};import{useMemo as $e,useState as je}from"react";import{useEffect as Pe}from"react";var ne=(e,n={})=>{let{delay:r=1,args:t}=n;Pe(()=>{let i=setTimeout(e,r,...t||[]);return()=>clearTimeout(i)},[r,t,e])};var Jn=(e,n=500)=>{let[r,t]=je(e),i=$e(()=>[e],[e]);return ne(t,{delay:n,args:i}),r};import{useCallback as A,useEffect as Be,useRef as Ne,useState as Ge}from"react";function re(e,n={}){let{delay:r=1,args:t,autoplay:i=!0,runOnStart:a=!1,updateState:s=!1}=n,o=Ne(void 0),[c,p]=Ge(i),u=A(()=>o.current?(clearInterval(o.current),o.current=void 0,!0):!1,[]),l=A(()=>{let T=u();return a&&(t?e(...t):e()),o.current=setInterval(e,r,...t||[]),!T&&s&&p(!0),o.current},[r,t,s,a,e,u]),d=A(()=>{u()&&s&&p(!1)},[s,u]);return Be(()=>(i&&l(),()=>d()),[i,l,d]),s?{isActive:c,start:l,stop:d}:{start:l,stop:d}}import{useEffect as _e}from"react";var rr=(e,n={})=>{let{delay:r=1,args:t}=n;_e(()=>{let i=setInterval(e,r,...t||[]);return()=>clearInterval(i)},[r,t,e])};import{useCallback as Q,useEffect as ze}from"react";function cr(e,n={}){let{autoplay:r=!0}=n,t=re(e,{autoplay:!1,...n}),{start:i,stop:a}=t,s=Q(()=>document.hidden?a():i(),[i,a]),o=Q(()=>{if(document.addEventListener("visibilitychange",s),!document.hidden)return i()},[i,s]),c=Q(()=>{a(),document.removeEventListener("visibilitychange",s)},[a,s]);return ze(()=>(r&&o(),()=>c()),[r,o,c]),{...t,start:o,stop:c}}import{useCallback as F,useEffect as Je,useRef as Xe,useState as Ye}from"react";function dr(e,n={}){let{delay:r=1,args:t,autoplay:i=!0,runOnStart:a=!1,updateState:s=!1}=n,o=Xe(void 0),[c,p]=Ye(i),u=F(()=>o.current?(clearTimeout(o.current),o.current=void 0,!0):!1,[]),l=F(()=>{let T=u();return a&&(t?e?.(...t):e?.()),o.current=setTimeout(()=>{if(o.current=void 0,s&&p(!1),t)return e?.(...t);e?.()},r),!T&&s&&p(!0),o.current},[r,t,s,a,e,u]),d=F(()=>{u()&&s&&p(!1)},[s,u]);return Je(()=>(i&&l(),()=>d()),[i,l,d]),s?{isActive:c,start:l,stop:d}:{start:l,stop:d}}export{B as getState,dt as useConnection,an as useDarkMode,Jn as useDebounce,Dt as useDeferCallback,Y as useDocumentVisibility,C as useEffectOnce,N as useEventListener,Dn as useFocusTrap,Kn as useInView,It as useInput,re as useInterval,cr as useIntervalWhenVisible,z as useIsClient,H as useIsFirstRender,xn as useIsPortrait,kn as useIsTouchDevice,rr as useLightInterval,ne as useLightTimeout,j as useLocalStorage,L as useMediaQuery,Bt as usePagination,Pn as useScrollBlock,zt as useSelection,at as useSessionStorage,V as useStorage,dr as useTimeout,h as useUpdateEffect,Vn as useWakeLock};
1
+ import{useCallback as P,useEffect as re,useState as oe}from"react";import{LocalStorage as $,SessionStorage as j}from"@alessiofrittoli/web-utils";var V=(e,n,r="local")=>{let t=P(()=>(r==="local"?$:j).get(e)??n,[r,e,n]),[i,a]=oe(n),s=P(o=>{a(c=>{let p=o instanceof Function?o(c):o;return(typeof window<"u"&&r==="local"?$:j).set(e,p),p})},[r,e]);return re(()=>{a(t())},[t]),[i,s]};var B=(e,n)=>V(e,n,"local");var ut=(e,n)=>V(e,n,"session");import{useCallback as se,useState as ie}from"react";import{getConnection as ae}from"@alessiofrittoli/web-utils";var ce={onLine:!0},ue=["online","offline"],Tt=()=>{let[e,n]=ie(ce),r=se(()=>{n(ae())},[]);return U("change",{target:e.network,listener:r,onLoad:r}),U(ue,{listener:r}),e};import{useCallback as K,useEffect as J,useRef as Me}from"react";import{useCallback as M,useMemo as G,useReducer as me}from"react";import{useEffect as pe}from"react";import{useRef as le}from"react";var H=()=>{let e=le(!0);return e.current?(e.current=!1,!0):e.current};var h=(e,n)=>{let r=H();pe(()=>{if(!r)return e()},n)};import{isEmpty as de}from"@alessiofrittoli/web-utils";var R=e=>typeof e=="string"?de(e):!e,W={value:"",isTouched:!1,isValid:!0},N=(e,n)=>{switch(n.type){case"TOUCHED":return{...e,isTouched:!0};case"CHANGE":return{...e,value:n.value};case"BLUR":return{...e,value:e.value,isTouched:!R(e.value??"")};case"RESET":return W}};var Rt=(e={})=>{let{inputRef:n}=e,{initialValue:r}=e,{touchTimeout:t=600}=e,{validate:i,parse:a}=e,{onChange:s}=e,[o,c]=me(N,{...W,value:r}),p=G(()=>a?a(o.value):o.value,[o.value,a]),u=G(()=>typeof i=="function"?i(p):!0,[p,i]),{isTouched:l}=o,d=R(p),T=!u&&l||!!r&&!u;h(()=>{let y=setTimeout(()=>{R(p)||c({type:"TOUCHED"})},t);return()=>clearTimeout(y)},[p,t]);let v=M(y=>{let{target:S}=y,{type:E}=S,b=E==="checkbox"?S.checked:S.value;c({type:"CHANGE",value:b}),s?.(a?a(b):b)},[s,a]),g=M(()=>{c({type:"BLUR"})},[]),O=M(()=>{c({type:"TOUCHED"})},[]),x=M(y=>{c({type:"CHANGE",value:y})},[]),k=M(()=>{n?.current?.focus()},[n]),w=M(()=>{c({type:"RESET"})},[]);return{value:p,isTouched:l,isValid:u,isEmpty:d,hasError:T,changeHandler:v,blurHandler:g,setValue:x,submit:O,focus:k,reset:w}};import{useCallback as fe}from"react";import{deferCallback as Te}from"@alessiofrittoli/web-utils";var Wt=(e,n)=>fe(Te(e),n);import{useEffect as ye}from"react";var C=e=>{let n=H();ye(()=>{if(n)return e()},[])};import{useEffect as ve,useState as Ee}from"react";var _=()=>{let[e,n]=Ee(!1);return ve(()=>n(!0),[]),e};import{useMemo as xe}from"react";import{paginate as Se}from"@alessiofrittoli/math-utils";var _t=(e={})=>xe(()=>Se(e),[e]);import{useCallback as D,useState as ge}from"react";var Yt=(e,n=[])=>{let[r,t]=ge(n),i=r.length>0,a=u=>r.includes(u),s=D(u=>t(l=>{let d=new Set(l);return d.has(u)?d.delete(u):d.add(u),Array.from(d.values())}),[]),o=D(u=>{t(l=>{if(l.length===0)return[u];let d=[...e],T=d.indexOf(l.at(0)),v=d.indexOf(u);if(T>v){let g=[...d].reverse(),O=g.indexOf(l.at(0)),x=g.indexOf(u);return g.slice(O,x+1)}return d.slice(T,v+1)})},[e]),c=D(()=>{t(e)},[e]),p=D(u=>t(u?n:[]),[n]);return{selection:r,hasSelection:i,isSelected:a,setSelection:t,select:s,groupSelect:o,selectAll:c,resetSelection:p}};import{useCallback as Oe,useEffect as ke,useState as be}from"react";import{getMediaMatches as z}from"@alessiofrittoli/web-utils";function L(e,n={}){let{updateState:r=!0,onChange:t}=n,[i,a]=be(z(e)),s=Oe(()=>{let o=z(e);r&&a(o),t?.(o)},[e,r,t]);if(ke(()=>{let o=window.matchMedia(e),{matches:c}=o;return r&&a(c),t?.(c),o.addEventListener("change",s),()=>{o.removeEventListener("change",s)}},[e,r,t,s]),!!r)return i}var ln=(e={})=>{let n=_(),r=L("(prefers-color-scheme: dark)"),{initial:t=r,docClassNames:i=[]}=e,[a,s]=B("dark-mode",t),o=a??r,[c,p]=i,u=Me({light:"",dark:""});return h(()=>{s(r)},[r,s]),J(()=>{c&&document.documentElement.classList.toggle(c,o),p&&document.documentElement.classList.toggle(p,!o)},[o,c,p]),J(()=>{document.head.querySelectorAll('meta[name="theme-color"]').forEach(l=>{let d=l.getAttribute("media"),T=l.getAttribute("content");if(T){if(!d||d==="(prefers-color-scheme: light)"){u.current.light=T;return}u.current.dark=T}})},[]),h(()=>{let l=u.current.dark,d=u.current.light;a&&!l||!a&&!d||document.head.querySelectorAll('meta[name="theme-color"]').forEach(T=>{T.setAttribute("content",a?l:d)})},[a]),{isDarkMode:n?o:!1,isDarkOS:n?r:!1,toggleDarkMode:K(()=>s(l=>!l),[s]),enableDarkMode:K(()=>s(!0),[s]),disableDarkMode:K(()=>s(!1),[s])}};import{useCallback as Le,useEffect as he,useState as Ie}from"react";function X(e={}){let{updateState:n=!0,onVisibilityChange:r}=e,[t,i]=Ie(!1),a=Le(()=>{let s=!document.hidden;n&&i(s),r?.(s)},[n,r]);if(C(()=>{n&&i(!document.hidden)}),he(()=>(document.addEventListener("visibilitychange",a),()=>document.removeEventListener("visibilitychange",a)),[a]),n)return t}import{useEffect as Ve}from"react";function U(e,n){let{target:r,query:t,options:i,listener:a,onLoad:s,onCleanUp:o}=n;Ve(()=>{let c=Array.isArray(e)?e:[e],p=(t?window.matchMedia(t):r&&"current"in r?r.current:r)??window;if(p.addEventListener)return s?.(),c.map(u=>{p.addEventListener(u,a,i)}),()=>{c.map(u=>{p.removeEventListener(u,a,i)}),o?.()}},[e,r,t,i,a,s,o])}import{portraitMediaQuery as He}from"@alessiofrittoli/web-utils";var On=()=>L(He);var Ln=()=>L("(pointer: coarse)");import{useCallback as I,useEffect as Re,useRef as Ce,useState as De}from"react";var Cn=(e={})=>{let{onMount:n=!0,onError:r}=e,[t,i]=De(null),a=Ce(n),s=t&&!t.released||!1,o=I(async l=>!t||t.released?navigator.wakeLock.request("screen").then(d=>{i(d),l?.()}).catch(r||console.error):t,[t,r]),c=I(async()=>t?.release(),[t]),p=I(async()=>{o(()=>{a.current=!0})},[o]),u=I(async()=>{a.current=!1,c()},[c]);return X({updateState:!1,onVisibilityChange:I(l=>{if(l&&!s&&a.current)return o();if(!l)return c()},[s,o,c])}),C(()=>{s||!n||o()}),Re(()=>{if(!t)return;let l=()=>{t.released&&i(null)};return t.addEventListener("release",l),()=>{t.removeEventListener("release",l),!t.released&&t.release()}},[t]),{wakeLock:t,enabled:s,requestWakeLock:p,releaseWakeLock:u}};import{useCallback as Y,useEffect as we,useMemo as Ue,useRef as We,useState as Ke}from"react";var Ae=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),Wn=e=>{let n=Ue(()=>e?.current,[e]),[r,t]=Ke(!1),i=We(null),a=Y(o=>{i.current=document.activeElement;let c=o||n||!1;if(c)return t(c)},[n]),s=Y(()=>{i.current?.focus(),t(!1)},[]);return we(()=>{if(!r)return;let o=c=>{if(c.key!=="Tab")return;let p=Array.from(r.querySelectorAll(Ae)),u=p.at(0),l=p.at(-1);if(!c.shiftKey){document.activeElement===l&&(c.preventDefault(),u?.focus());return}document.activeElement===u&&(c.preventDefault(),l?.focus())};return document.addEventListener("keydown",o),()=>{document.removeEventListener("keydown",o)}},[r]),[a,s]};import{useEffect as Qe,useMemo as Fe,useRef as A,useState as Z}from"react";var Fn=(e,n={})=>{let{initial:r=!1,once:t,amount:i,margin:a,root:s,enable:o=!0}=n,{onEnter:c,onExit:p,onIntersect:u}=n,l=A(!0),[d,T]=Z(r),[v,g]=Z(o),O=A(null),x=A(!1),k=Fe(()=>{if(!v||typeof IntersectionObserver>"u")return;let w=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([y],S)=>{if(!y)return;let E=y.isIntersecting;try{if(x.current=!E&&!!O.current,E&&c&&await c({entry:y,observer:S}),x.current&&p&&await p({entry:y,observer:S}),u&&(E||!E&&O.current!=null)){let b={isEntering:E,isExiting:x.current};await u({entry:y,observer:S,...b})}if(O.current=E,!l.current)return;T(E)}catch(b){console.error(b)}E&&t&&S.disconnect()},{root:s||void 0,rootMargin:a,threshold:w})}catch(y){console.error(y)}},[s,a,i,t,v,c,p,u]);return Qe(()=>{if(l.current=!0,!(!v||!e.current||!k))return k.observe(e.current),()=>{l.current=!1,k.disconnect()}},[e,k,v]),{inView:d,enabled:v,observer:k,isExiting:x.current,setInView:T,setEnabled:g}};import{useCallback as ee,useMemo as qe}from"react";import{blockScroll as Pe,restoreScroll as $e}from"@alessiofrittoli/web-utils";var Bn=e=>{let n=qe(()=>e?.current,[e]),r=ee(()=>Pe(n||void 0),[n]),t=ee(()=>$e(n||void 0),[n]);return[r,t]};import{useMemo as Be,useState as Ne}from"react";import{useEffect as je}from"react";var te=(e,n={})=>{let{delay:r=1,args:t}=n;je(()=>{let i=setTimeout(e,r,...t||[]);return()=>clearTimeout(i)},[r,t,e])};var Zn=(e,n=500)=>{let[r,t]=Ne(e),i=Be(()=>[e],[e]);return te(t,{delay:n,args:i}),r};import{useCallback as Q,useEffect as Ge,useRef as _e,useState as ze}from"react";function ne(e,n={}){let{delay:r=1,args:t,autoplay:i=!0,runOnStart:a=!1,updateState:s=!1}=n,o=_e(void 0),[c,p]=ze(i),u=Q(()=>o.current?(clearInterval(o.current),o.current=void 0,!0):!1,[]),l=Q(()=>{let T=u();return a&&(t?e(...t):e()),o.current=setInterval(e,r,...t||[]),!T&&s&&p(!0),o.current},[r,t,s,a,e,u]),d=Q(()=>{u()&&s&&p(!1)},[s,u]);return Ge(()=>(i&&l(),()=>d()),[i,l,d]),s?{isActive:c,start:l,stop:d}:{start:l,stop:d}}import{useEffect as Je}from"react";var ir=(e,n={})=>{let{delay:r=1,args:t}=n;Je(()=>{let i=setInterval(e,r,...t||[]);return()=>clearInterval(i)},[r,t,e])};import{useCallback as F,useEffect as Xe}from"react";function pr(e,n={}){let{autoplay:r=!0}=n,t=ne(e,{autoplay:!1,...n}),{start:i,stop:a}=t,s=F(()=>document.hidden?a():i(),[i,a]),o=F(()=>{if(document.addEventListener("visibilitychange",s),!document.hidden)return i()},[i,s]),c=F(()=>{a(),document.removeEventListener("visibilitychange",s)},[a,s]);return Xe(()=>(r&&o(),()=>c()),[r,o,c]),{...t,start:o,stop:c}}import{useCallback as q,useEffect as Ye,useRef as Ze,useState as et}from"react";function Tr(e,n={}){let{delay:r=1,args:t,autoplay:i=!0,runOnStart:a=!1,updateState:s=!1}=n,o=Ze(void 0),[c,p]=et(i),u=q(()=>o.current?(clearTimeout(o.current),o.current=void 0,!0):!1,[]),l=q(()=>{let T=u();return a&&(t?e?.(...t):e?.()),o.current=setTimeout(()=>{if(o.current=void 0,s&&p(!1),t)return e?.(...t);e?.()},r),!T&&s&&p(!0),o.current},[r,t,s,a,e,u]),d=q(()=>{u()&&s&&p(!1)},[s,u]);return Ye(()=>(i&&l(),()=>d()),[i,l,d]),s?{isActive:c,start:l,stop:d}:{start:l,stop:d}}export{Tt as useConnection,ln as useDarkMode,Zn as useDebounce,Wt as useDeferCallback,X as useDocumentVisibility,C as useEffectOnce,U as useEventListener,Wn as useFocusTrap,Fn as useInView,Rt as useInput,ne as useInterval,pr as useIntervalWhenVisible,_ as useIsClient,H as useIsFirstRender,On as useIsPortrait,Ln as useIsTouchDevice,ir as useLightInterval,te as useLightTimeout,B as useLocalStorage,L as useMediaQuery,_t as usePagination,Bn as useScrollBlock,Yt as useSelection,ut as useSessionStorage,V as useStorage,Tr as useTimeout,h as useUpdateEffect,Cn as useWakeLock};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alessiofrittoli/react-hooks",
3
- "version": "3.8.0",
3
+ "version": "4.1.0",
4
4
  "description": "TypeScript React utility Hooks",
5
5
  "author": {
6
6
  "name": "Alessio Frittoli",
@@ -114,7 +114,7 @@
114
114
  "eslint": "^10.0.2",
115
115
  "eslint-plugin-react": "^7.37.5",
116
116
  "eslint-plugin-react-hooks": "^7.0.1",
117
- "globals": "^17.3.0",
117
+ "globals": "^17.4.0",
118
118
  "http-server": "^14.1.1",
119
119
  "jest": "^30.2.0",
120
120
  "jest-environment-jsdom": "^30.2.0",
@@ -129,6 +129,6 @@
129
129
  "dependencies": {
130
130
  "@alessiofrittoli/math-utils": "^2.0.0",
131
131
  "@alessiofrittoli/type-utils": "^1.9.0",
132
- "@alessiofrittoli/web-utils": "^2.0.1"
132
+ "@alessiofrittoli/web-utils": "^2.1.0"
133
133
  }
134
134
  }