@alessiofrittoli/react-hooks 2.0.1 → 3.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 +80 -11
- package/dist/index.d.mts +129 -12
- package/dist/index.d.ts +129 -12
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -267,9 +267,13 @@ Get Document Media matches and listen for changes.
|
|
|
267
267
|
|
|
268
268
|
<summary style="cursor:pointer">Parameters</summary>
|
|
269
269
|
|
|
270
|
-
| Parameter | Type | Description |
|
|
271
|
-
|
|
272
|
-
| `query` | `string` | A string specifying the media query to parse into a `MediaQueryList`. |
|
|
270
|
+
| Parameter | Type | Default | Description |
|
|
271
|
+
|-----------|----------|---------|-------------|
|
|
272
|
+
| `query` | `string` | - | A string specifying the media query to parse into a `MediaQueryList`. |
|
|
273
|
+
| `options` | `UseMediaQueryOptions\|UseMediaQueryStateOptions` | - | An object defining custom options. |
|
|
274
|
+
| `options.updateState` | `boolean` | `true` | Indicates whether the hook will dispatch a React state update when the given `query` change event get dispatched. |
|
|
275
|
+
| `options.onChange` | `OnChangeHandler` | - | A custom callback that will be invoked on initial page load and when the given `query` change event get dispatched. |
|
|
276
|
+
| | | | This callback is required if `updateState` is set to `false`. |
|
|
273
277
|
|
|
274
278
|
</details>
|
|
275
279
|
|
|
@@ -279,10 +283,10 @@ Get Document Media matches and listen for changes.
|
|
|
279
283
|
|
|
280
284
|
<summary style="cursor:pointer">Returns</summary>
|
|
281
285
|
|
|
282
|
-
Type: `boolean`
|
|
286
|
+
Type: `boolean|void`
|
|
283
287
|
|
|
284
|
-
- `true` if the document currently matches the media query list.
|
|
285
|
-
- `
|
|
288
|
+
- `true` or `false` if the document currently matches the media query list or not.
|
|
289
|
+
- `void` if `updateState` is set to `false`.
|
|
286
290
|
|
|
287
291
|
</details>
|
|
288
292
|
|
|
@@ -300,6 +304,21 @@ import { useMediaQuery } from '@alessiofrittoli/react-hooks'
|
|
|
300
304
|
const isDarkOS = useMediaQuery( '(prefers-color-scheme: dark)' )
|
|
301
305
|
```
|
|
302
306
|
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
###### Listen changes with no state updates
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
import { useMediaQuery } from '@alessiofrittoli/react-hooks'
|
|
313
|
+
|
|
314
|
+
useMediaQuery( '(prefers-color-scheme: dark)', {
|
|
315
|
+
updateState: false,
|
|
316
|
+
onChange( matches ) {
|
|
317
|
+
console.log( 'is dark OS?', matches )
|
|
318
|
+
}
|
|
319
|
+
} )
|
|
320
|
+
```
|
|
321
|
+
|
|
303
322
|
</details>
|
|
304
323
|
|
|
305
324
|
---
|
|
@@ -682,9 +701,17 @@ Check if the given target Element is intersecting with an ancestor Element or wi
|
|
|
682
701
|
| | | - `number` |
|
|
683
702
|
| | | - `number[]` |
|
|
684
703
|
| `options.once` | `boolean` | (Optional) By setting this to `true` the observer will be disconnected after the target Element enters the viewport. |
|
|
685
|
-
| `options.initial` | `boolean` | (Optional) Initial value. Default: `false`. |
|
|
704
|
+
| `options.initial` | `boolean` | (Optional) Initial value. This value is used while server rendering then will be updated in the client based on target visibility. Default: `false`. |
|
|
686
705
|
| `options.enable` | `boolean` | (Optional) Defines the initial observation activity. Use the returned `setEnabled` to update this state. Default: `true`. |
|
|
687
|
-
| `options.
|
|
706
|
+
| `options.onIntersect` | `OnIntersectStateHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
|
|
707
|
+
| | | This callback is awaited before any state update. |
|
|
708
|
+
| | | If an error is thrown the React State update won't be fired. |
|
|
709
|
+
| | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
|
|
710
|
+
| `options.onEnter` | `OnIntersectHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
|
|
711
|
+
| | | This callback is awaited before any state update. |
|
|
712
|
+
| | | If an error is thrown the React State update won't be fired. |
|
|
713
|
+
| | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
|
|
714
|
+
| `options.onExit` | `OnIntersectHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
|
|
688
715
|
| | | This callback is awaited before any state update. |
|
|
689
716
|
| | | If an error is thrown the React State update won't be fired. |
|
|
690
717
|
| | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
|
|
@@ -839,21 +866,63 @@ const OnDemandObservation: React.FC = () => {
|
|
|
839
866
|
'use client'
|
|
840
867
|
|
|
841
868
|
import { useRef } from 'react'
|
|
842
|
-
import { useInView, type
|
|
869
|
+
import { useInView, type OnIntersectStateHandler } from '@alessiofrittoli/react-hooks'
|
|
843
870
|
|
|
844
871
|
|
|
845
872
|
const AsyncStartExample: React.FC = () => {
|
|
846
873
|
|
|
847
874
|
const targetRef = useRef<HTMLDivElement>( null )
|
|
848
|
-
const
|
|
875
|
+
const onIntersect = useCallback<OnIntersectStateHandler>( async ( { entry, isEntering } ) => {
|
|
849
876
|
|
|
877
|
+
if ( isEntering ) {
|
|
878
|
+
console.log( 'Delaying state update...' )
|
|
879
|
+
await new Promise( resolve => setTimeout( resolve, 1000 ) ) // Simulate delay
|
|
880
|
+
console.log( 'Async task completed. `inView` will now be updated.' )
|
|
881
|
+
return
|
|
882
|
+
}
|
|
883
|
+
|
|
850
884
|
console.log( 'Delaying state update...' )
|
|
851
885
|
await new Promise( resolve => setTimeout( resolve, 1000 ) ) // Simulate delay
|
|
852
886
|
console.log( 'Async task completed. `inView` will now be updated.' )
|
|
853
887
|
|
|
854
888
|
}, [] )
|
|
855
889
|
|
|
856
|
-
const { inView } = useInView( targetRef, {
|
|
890
|
+
const { inView } = useInView( targetRef, { onIntersect } )
|
|
891
|
+
|
|
892
|
+
return (
|
|
893
|
+
<div
|
|
894
|
+
ref={ targetRef }
|
|
895
|
+
style={ {
|
|
896
|
+
height : 200,
|
|
897
|
+
background : inView ? 'lime' : 'gray',
|
|
898
|
+
} }
|
|
899
|
+
/>
|
|
900
|
+
)
|
|
901
|
+
}
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
---
|
|
905
|
+
|
|
906
|
+
###### Execute custom callback when `onEnter` and `onExit`
|
|
907
|
+
|
|
908
|
+
```tsx
|
|
909
|
+
'use client'
|
|
910
|
+
|
|
911
|
+
import { useRef } from 'react'
|
|
912
|
+
import { useInView, type OnIntersectHandler } from '@alessiofrittoli/react-hooks'
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
const AsyncStartExample: React.FC = () => {
|
|
916
|
+
|
|
917
|
+
const targetRef = useRef<HTMLDivElement>( null )
|
|
918
|
+
const onEnter = useCallback<OnIntersectHandler>( async ( { entry } ) => {
|
|
919
|
+
console.log( 'In viewport - ', entry )
|
|
920
|
+
}, [] )
|
|
921
|
+
const onExit = useCallback<OnIntersectHandler>( async ( { entry } ) => {
|
|
922
|
+
console.log( 'Exited viewport - ', entry )
|
|
923
|
+
}, [] )
|
|
924
|
+
|
|
925
|
+
const { inView } = useInView( targetRef, { onEnter, onExit } )
|
|
857
926
|
|
|
858
927
|
return (
|
|
859
928
|
<div
|
package/dist/index.d.mts
CHANGED
|
@@ -481,15 +481,50 @@ declare function useEventListener<T extends Record<string, Event>, K extends key
|
|
|
481
481
|
*/
|
|
482
482
|
declare const useIsPortrait: () => boolean;
|
|
483
483
|
|
|
484
|
+
type OnChangeHandler = (matches: boolean) => void;
|
|
485
|
+
interface CommonOptions {
|
|
486
|
+
/**
|
|
487
|
+
* A custom callback that will be invoked on initial page load and when the given `query` change event get dispatched.
|
|
488
|
+
*
|
|
489
|
+
* @param matches Whether the document currently matches the media query list.
|
|
490
|
+
*/
|
|
491
|
+
onChange?: OnChangeHandler;
|
|
492
|
+
}
|
|
493
|
+
interface UseMediaQueryOptions extends CommonOptions {
|
|
494
|
+
/**
|
|
495
|
+
* Indicates whether the hook will dispatch a React state update when the given `query` change event get dispatched.
|
|
496
|
+
*
|
|
497
|
+
*/
|
|
498
|
+
updateState: false;
|
|
499
|
+
onChange: OnChangeHandler;
|
|
500
|
+
}
|
|
501
|
+
interface UseMediaQueryStateOptions extends CommonOptions {
|
|
502
|
+
/**
|
|
503
|
+
* Indicates whether the hook will dispatch a React state update when the given `query` change event get dispatched.
|
|
504
|
+
*
|
|
505
|
+
*/
|
|
506
|
+
updateState?: true;
|
|
507
|
+
}
|
|
484
508
|
/**
|
|
485
|
-
* Get Document Media matches and
|
|
509
|
+
* Get Document Media matches and dispatch a React state update on MediaQuery changes.
|
|
486
510
|
*
|
|
487
511
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia)
|
|
488
512
|
*
|
|
489
|
-
* @param query
|
|
513
|
+
* @param query A string specifying the media query to parse into a `MediaQueryList`.
|
|
514
|
+
* @param options An object defining custom options. See {@linkcode UseMediaQueryStateOptions} for more info.
|
|
515
|
+
*
|
|
490
516
|
* @returns A boolean value that returns `true` if the document currently matches the media query list, or `false` if not.
|
|
491
517
|
*/
|
|
492
|
-
declare
|
|
518
|
+
declare function useMediaQuery(query: string, options?: UseMediaQueryStateOptions): boolean;
|
|
519
|
+
/**
|
|
520
|
+
* Get Document Media matches and listen for changes.
|
|
521
|
+
*
|
|
522
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia)
|
|
523
|
+
*
|
|
524
|
+
* @param query A string specifying the media query to parse into a `MediaQueryList`.
|
|
525
|
+
* @param options An object defining custom options. See {@linkcode UseMediaQueryOptions} for more info.
|
|
526
|
+
*/
|
|
527
|
+
declare function useMediaQuery(query: string, options?: UseMediaQueryOptions): void;
|
|
493
528
|
|
|
494
529
|
type SetFocusTrap = (target?: HTMLElement) => void;
|
|
495
530
|
type RestoreFocusTrap = () => void;
|
|
@@ -507,6 +542,42 @@ declare const useFocusTrap: (target?: React.RefObject<HTMLElement | null>) => re
|
|
|
507
542
|
|
|
508
543
|
type MarginValue = `${number}${'px' | '%'}`;
|
|
509
544
|
type MarginType = (MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue} ${MarginValue}`);
|
|
545
|
+
type IntersectionState = ({
|
|
546
|
+
/**
|
|
547
|
+
* Indicates whether the {@link IntersectionObserverEntry} is entering the viewport.
|
|
548
|
+
*
|
|
549
|
+
*/
|
|
550
|
+
isEntering: true;
|
|
551
|
+
/**
|
|
552
|
+
* Indicates whether the {@link IntersectionObserverEntry} is exiting the viewport.
|
|
553
|
+
*
|
|
554
|
+
*/
|
|
555
|
+
isExiting: false;
|
|
556
|
+
} | {
|
|
557
|
+
/**
|
|
558
|
+
* Indicates whether the {@link IntersectionObserverEntry} is entering the viewport.
|
|
559
|
+
*
|
|
560
|
+
*/
|
|
561
|
+
isEntering: false;
|
|
562
|
+
/**
|
|
563
|
+
* Indicates whether the {@link IntersectionObserverEntry} is exiting the viewport.
|
|
564
|
+
*
|
|
565
|
+
*/
|
|
566
|
+
isExiting: true;
|
|
567
|
+
});
|
|
568
|
+
interface OnIntersectHandlerOptions {
|
|
569
|
+
/**
|
|
570
|
+
* The intersecting {@link IntersectionObserverEntry}.
|
|
571
|
+
*
|
|
572
|
+
*/
|
|
573
|
+
entry: IntersectionObserverEntry;
|
|
574
|
+
/**
|
|
575
|
+
* The current {@link IntersectionObserver} instance.
|
|
576
|
+
*
|
|
577
|
+
*/
|
|
578
|
+
observer: IntersectionObserver;
|
|
579
|
+
}
|
|
580
|
+
type OnIntersectStateHandlerOptions = OnIntersectHandlerOptions & IntersectionState;
|
|
510
581
|
/**
|
|
511
582
|
* A custom callback executed when target element's visibility has crossed one or more thresholds.
|
|
512
583
|
*
|
|
@@ -516,10 +587,21 @@ type MarginType = (MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValu
|
|
|
516
587
|
*
|
|
517
588
|
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
518
589
|
*
|
|
519
|
-
* @param
|
|
520
|
-
* @param observer The current {@link IntersectionObserver} instance.
|
|
590
|
+
* @param options An object defining the current intersection entry and observer.
|
|
521
591
|
*/
|
|
522
|
-
type
|
|
592
|
+
type OnIntersectHandler = (options: OnIntersectHandlerOptions) => void | Promise<void>;
|
|
593
|
+
/**
|
|
594
|
+
* A custom callback executed when target element's visibility has crossed one or more thresholds.
|
|
595
|
+
*
|
|
596
|
+
* This callback is awaited before any state update.
|
|
597
|
+
*
|
|
598
|
+
* If an error is thrown the React State update won't be fired.
|
|
599
|
+
*
|
|
600
|
+
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
601
|
+
*
|
|
602
|
+
* @param options An object defining the current intersection entry, observer and entry state.
|
|
603
|
+
*/
|
|
604
|
+
type OnIntersectStateHandler = (options: OnIntersectStateHandlerOptions) => void | Promise<void>;
|
|
523
605
|
interface UseInViewOptions {
|
|
524
606
|
/**
|
|
525
607
|
* Identifies the {@link Element} or {@link Document} whose bounds are treated as the bounding box of the viewport for the Element which is the observer's target.
|
|
@@ -554,9 +636,9 @@ interface UseInViewOptions {
|
|
|
554
636
|
*/
|
|
555
637
|
once?: boolean;
|
|
556
638
|
/**
|
|
557
|
-
* Initial value.
|
|
639
|
+
* Initial value. This value is used while server rendering then will be updated in the client based on target visibility.
|
|
558
640
|
*
|
|
559
|
-
* @default
|
|
641
|
+
* @default false
|
|
560
642
|
*/
|
|
561
643
|
initial?: boolean;
|
|
562
644
|
/**
|
|
@@ -574,10 +656,33 @@ interface UseInViewOptions {
|
|
|
574
656
|
*
|
|
575
657
|
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
576
658
|
*
|
|
577
|
-
* @param
|
|
578
|
-
|
|
659
|
+
* @param options An object defining the current intersection entry, observer and entry state.
|
|
660
|
+
*/
|
|
661
|
+
onIntersect?: OnIntersectStateHandler;
|
|
662
|
+
/**
|
|
663
|
+
* A custom callback executed when target element is entering the viewport.
|
|
664
|
+
*
|
|
665
|
+
* This callback is awaited before any state update.
|
|
666
|
+
*
|
|
667
|
+
* If an error is thrown the React State update won't be fired.
|
|
668
|
+
*
|
|
669
|
+
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
670
|
+
*
|
|
671
|
+
* @param options An object defining the current intersection entry and observer.
|
|
579
672
|
*/
|
|
580
|
-
|
|
673
|
+
onEnter?: OnIntersectHandler;
|
|
674
|
+
/**
|
|
675
|
+
* A custom callback executed when target element is exiting the viewport.
|
|
676
|
+
*
|
|
677
|
+
* This callback is awaited before any state update.
|
|
678
|
+
*
|
|
679
|
+
* If an error is thrown the React State update won't be fired.
|
|
680
|
+
*
|
|
681
|
+
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
682
|
+
*
|
|
683
|
+
* @param options An object defining the current intersection entry and observer.
|
|
684
|
+
*/
|
|
685
|
+
onExit?: OnIntersectHandler;
|
|
581
686
|
}
|
|
582
687
|
interface UseInViewReturnType {
|
|
583
688
|
/**
|
|
@@ -585,6 +690,11 @@ interface UseInViewReturnType {
|
|
|
585
690
|
*
|
|
586
691
|
*/
|
|
587
692
|
inView: boolean;
|
|
693
|
+
/**
|
|
694
|
+
* Indicates whether the target Element is exiting the viewport.
|
|
695
|
+
*
|
|
696
|
+
*/
|
|
697
|
+
isExiting: boolean;
|
|
588
698
|
/**
|
|
589
699
|
* Indicates whether the target Element is being observed or not.
|
|
590
700
|
*
|
|
@@ -626,6 +736,13 @@ declare const useInView: (target: React.RefObject<Element | null>, options?: Use
|
|
|
626
736
|
*/
|
|
627
737
|
declare const useScrollBlock: (target?: React.RefObject<HTMLElement | null>) => readonly [() => void, () => void];
|
|
628
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Modified version of `useEffect` that only run once on intial load.
|
|
741
|
+
*
|
|
742
|
+
* @param effect Imperative function that can return a cleanup function.
|
|
743
|
+
*/
|
|
744
|
+
declare const useEffectOnce: (effect: React.EffectCallback) => void;
|
|
745
|
+
|
|
629
746
|
/**
|
|
630
747
|
* Check if the React Hook or Component where this hook is executed is running in a browser environment.
|
|
631
748
|
*
|
|
@@ -937,4 +1054,4 @@ declare function useTimeout<T extends readonly unknown[]>(callback: TimerHandler
|
|
|
937
1054
|
*/
|
|
938
1055
|
declare const useLightTimeout: <T extends readonly unknown[]>(callback: TimerHandler<T>, options?: BasicTimerOptions<T>) => void;
|
|
939
1056
|
|
|
940
|
-
export { type AddEventListenerOptions, type BasicTimerOptions, type CommonListenerOptions, type CustomEventListenerOptions, type DocumentEventListener, type DocumentListenerOptions, type ElementEventListener, type ElementListenerOptions, type ListenerOptions, type MarginType, type MarginValue, type MediaQueryChangeListener, type MediaQueryEventListener, type MediaQueryListenerOptions, type
|
|
1057
|
+
export { type AddEventListenerOptions, type BasicTimerOptions, type CommonListenerOptions, type CustomEventListenerOptions, type DocumentEventListener, type DocumentListenerOptions, type ElementEventListener, type ElementListenerOptions, type IntersectionState, type ListenerOptions, type MarginType, type MarginValue, type MediaQueryChangeListener, type MediaQueryEventListener, type MediaQueryListenerOptions, type OnChangeHandler, type OnIntersectHandler, type OnIntersectStateHandler, type StartTimer, type StateTimerOptions, type StateTimerReturnType, type StopTimer, type TimerHandler, type TimerId, type TimerOptions, type TimerReturnType, type UseDarkModeOptions, type UseDarkModeOutput, type UseInViewOptions, type UseInViewReturnType, type UseIntervalWhenVisibleReturnType, type UseIntervalWhenVisibleStateReturnType, type UseMediaQueryOptions, type UseMediaQueryStateOptions, type WindowEventListener, type WindowListenerOptions, useDarkMode, useDebounce, useEffectOnce, useEventListener, useFocusTrap, useInView, useInterval, useIntervalWhenVisible, useIsClient, useIsFirstRender, useIsPortrait, useLightInterval, useLightTimeout, useLocalStorage, useMediaQuery, useScrollBlock, useSessionStorage, useStorage, useTimeout, useUpdateEffect };
|
package/dist/index.d.ts
CHANGED
|
@@ -481,15 +481,50 @@ declare function useEventListener<T extends Record<string, Event>, K extends key
|
|
|
481
481
|
*/
|
|
482
482
|
declare const useIsPortrait: () => boolean;
|
|
483
483
|
|
|
484
|
+
type OnChangeHandler = (matches: boolean) => void;
|
|
485
|
+
interface CommonOptions {
|
|
486
|
+
/**
|
|
487
|
+
* A custom callback that will be invoked on initial page load and when the given `query` change event get dispatched.
|
|
488
|
+
*
|
|
489
|
+
* @param matches Whether the document currently matches the media query list.
|
|
490
|
+
*/
|
|
491
|
+
onChange?: OnChangeHandler;
|
|
492
|
+
}
|
|
493
|
+
interface UseMediaQueryOptions extends CommonOptions {
|
|
494
|
+
/**
|
|
495
|
+
* Indicates whether the hook will dispatch a React state update when the given `query` change event get dispatched.
|
|
496
|
+
*
|
|
497
|
+
*/
|
|
498
|
+
updateState: false;
|
|
499
|
+
onChange: OnChangeHandler;
|
|
500
|
+
}
|
|
501
|
+
interface UseMediaQueryStateOptions extends CommonOptions {
|
|
502
|
+
/**
|
|
503
|
+
* Indicates whether the hook will dispatch a React state update when the given `query` change event get dispatched.
|
|
504
|
+
*
|
|
505
|
+
*/
|
|
506
|
+
updateState?: true;
|
|
507
|
+
}
|
|
484
508
|
/**
|
|
485
|
-
* Get Document Media matches and
|
|
509
|
+
* Get Document Media matches and dispatch a React state update on MediaQuery changes.
|
|
486
510
|
*
|
|
487
511
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia)
|
|
488
512
|
*
|
|
489
|
-
* @param query
|
|
513
|
+
* @param query A string specifying the media query to parse into a `MediaQueryList`.
|
|
514
|
+
* @param options An object defining custom options. See {@linkcode UseMediaQueryStateOptions} for more info.
|
|
515
|
+
*
|
|
490
516
|
* @returns A boolean value that returns `true` if the document currently matches the media query list, or `false` if not.
|
|
491
517
|
*/
|
|
492
|
-
declare
|
|
518
|
+
declare function useMediaQuery(query: string, options?: UseMediaQueryStateOptions): boolean;
|
|
519
|
+
/**
|
|
520
|
+
* Get Document Media matches and listen for changes.
|
|
521
|
+
*
|
|
522
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia)
|
|
523
|
+
*
|
|
524
|
+
* @param query A string specifying the media query to parse into a `MediaQueryList`.
|
|
525
|
+
* @param options An object defining custom options. See {@linkcode UseMediaQueryOptions} for more info.
|
|
526
|
+
*/
|
|
527
|
+
declare function useMediaQuery(query: string, options?: UseMediaQueryOptions): void;
|
|
493
528
|
|
|
494
529
|
type SetFocusTrap = (target?: HTMLElement) => void;
|
|
495
530
|
type RestoreFocusTrap = () => void;
|
|
@@ -507,6 +542,42 @@ declare const useFocusTrap: (target?: React.RefObject<HTMLElement | null>) => re
|
|
|
507
542
|
|
|
508
543
|
type MarginValue = `${number}${'px' | '%'}`;
|
|
509
544
|
type MarginType = (MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue} ${MarginValue}`);
|
|
545
|
+
type IntersectionState = ({
|
|
546
|
+
/**
|
|
547
|
+
* Indicates whether the {@link IntersectionObserverEntry} is entering the viewport.
|
|
548
|
+
*
|
|
549
|
+
*/
|
|
550
|
+
isEntering: true;
|
|
551
|
+
/**
|
|
552
|
+
* Indicates whether the {@link IntersectionObserverEntry} is exiting the viewport.
|
|
553
|
+
*
|
|
554
|
+
*/
|
|
555
|
+
isExiting: false;
|
|
556
|
+
} | {
|
|
557
|
+
/**
|
|
558
|
+
* Indicates whether the {@link IntersectionObserverEntry} is entering the viewport.
|
|
559
|
+
*
|
|
560
|
+
*/
|
|
561
|
+
isEntering: false;
|
|
562
|
+
/**
|
|
563
|
+
* Indicates whether the {@link IntersectionObserverEntry} is exiting the viewport.
|
|
564
|
+
*
|
|
565
|
+
*/
|
|
566
|
+
isExiting: true;
|
|
567
|
+
});
|
|
568
|
+
interface OnIntersectHandlerOptions {
|
|
569
|
+
/**
|
|
570
|
+
* The intersecting {@link IntersectionObserverEntry}.
|
|
571
|
+
*
|
|
572
|
+
*/
|
|
573
|
+
entry: IntersectionObserverEntry;
|
|
574
|
+
/**
|
|
575
|
+
* The current {@link IntersectionObserver} instance.
|
|
576
|
+
*
|
|
577
|
+
*/
|
|
578
|
+
observer: IntersectionObserver;
|
|
579
|
+
}
|
|
580
|
+
type OnIntersectStateHandlerOptions = OnIntersectHandlerOptions & IntersectionState;
|
|
510
581
|
/**
|
|
511
582
|
* A custom callback executed when target element's visibility has crossed one or more thresholds.
|
|
512
583
|
*
|
|
@@ -516,10 +587,21 @@ type MarginType = (MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValu
|
|
|
516
587
|
*
|
|
517
588
|
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
518
589
|
*
|
|
519
|
-
* @param
|
|
520
|
-
* @param observer The current {@link IntersectionObserver} instance.
|
|
590
|
+
* @param options An object defining the current intersection entry and observer.
|
|
521
591
|
*/
|
|
522
|
-
type
|
|
592
|
+
type OnIntersectHandler = (options: OnIntersectHandlerOptions) => void | Promise<void>;
|
|
593
|
+
/**
|
|
594
|
+
* A custom callback executed when target element's visibility has crossed one or more thresholds.
|
|
595
|
+
*
|
|
596
|
+
* This callback is awaited before any state update.
|
|
597
|
+
*
|
|
598
|
+
* If an error is thrown the React State update won't be fired.
|
|
599
|
+
*
|
|
600
|
+
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
601
|
+
*
|
|
602
|
+
* @param options An object defining the current intersection entry, observer and entry state.
|
|
603
|
+
*/
|
|
604
|
+
type OnIntersectStateHandler = (options: OnIntersectStateHandlerOptions) => void | Promise<void>;
|
|
523
605
|
interface UseInViewOptions {
|
|
524
606
|
/**
|
|
525
607
|
* Identifies the {@link Element} or {@link Document} whose bounds are treated as the bounding box of the viewport for the Element which is the observer's target.
|
|
@@ -554,9 +636,9 @@ interface UseInViewOptions {
|
|
|
554
636
|
*/
|
|
555
637
|
once?: boolean;
|
|
556
638
|
/**
|
|
557
|
-
* Initial value.
|
|
639
|
+
* Initial value. This value is used while server rendering then will be updated in the client based on target visibility.
|
|
558
640
|
*
|
|
559
|
-
* @default
|
|
641
|
+
* @default false
|
|
560
642
|
*/
|
|
561
643
|
initial?: boolean;
|
|
562
644
|
/**
|
|
@@ -574,10 +656,33 @@ interface UseInViewOptions {
|
|
|
574
656
|
*
|
|
575
657
|
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
576
658
|
*
|
|
577
|
-
* @param
|
|
578
|
-
|
|
659
|
+
* @param options An object defining the current intersection entry, observer and entry state.
|
|
660
|
+
*/
|
|
661
|
+
onIntersect?: OnIntersectStateHandler;
|
|
662
|
+
/**
|
|
663
|
+
* A custom callback executed when target element is entering the viewport.
|
|
664
|
+
*
|
|
665
|
+
* This callback is awaited before any state update.
|
|
666
|
+
*
|
|
667
|
+
* If an error is thrown the React State update won't be fired.
|
|
668
|
+
*
|
|
669
|
+
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
670
|
+
*
|
|
671
|
+
* @param options An object defining the current intersection entry and observer.
|
|
579
672
|
*/
|
|
580
|
-
|
|
673
|
+
onEnter?: OnIntersectHandler;
|
|
674
|
+
/**
|
|
675
|
+
* A custom callback executed when target element is exiting the viewport.
|
|
676
|
+
*
|
|
677
|
+
* This callback is awaited before any state update.
|
|
678
|
+
*
|
|
679
|
+
* If an error is thrown the React State update won't be fired.
|
|
680
|
+
*
|
|
681
|
+
* ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
|
|
682
|
+
*
|
|
683
|
+
* @param options An object defining the current intersection entry and observer.
|
|
684
|
+
*/
|
|
685
|
+
onExit?: OnIntersectHandler;
|
|
581
686
|
}
|
|
582
687
|
interface UseInViewReturnType {
|
|
583
688
|
/**
|
|
@@ -585,6 +690,11 @@ interface UseInViewReturnType {
|
|
|
585
690
|
*
|
|
586
691
|
*/
|
|
587
692
|
inView: boolean;
|
|
693
|
+
/**
|
|
694
|
+
* Indicates whether the target Element is exiting the viewport.
|
|
695
|
+
*
|
|
696
|
+
*/
|
|
697
|
+
isExiting: boolean;
|
|
588
698
|
/**
|
|
589
699
|
* Indicates whether the target Element is being observed or not.
|
|
590
700
|
*
|
|
@@ -626,6 +736,13 @@ declare const useInView: (target: React.RefObject<Element | null>, options?: Use
|
|
|
626
736
|
*/
|
|
627
737
|
declare const useScrollBlock: (target?: React.RefObject<HTMLElement | null>) => readonly [() => void, () => void];
|
|
628
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Modified version of `useEffect` that only run once on intial load.
|
|
741
|
+
*
|
|
742
|
+
* @param effect Imperative function that can return a cleanup function.
|
|
743
|
+
*/
|
|
744
|
+
declare const useEffectOnce: (effect: React.EffectCallback) => void;
|
|
745
|
+
|
|
629
746
|
/**
|
|
630
747
|
* Check if the React Hook or Component where this hook is executed is running in a browser environment.
|
|
631
748
|
*
|
|
@@ -937,4 +1054,4 @@ declare function useTimeout<T extends readonly unknown[]>(callback: TimerHandler
|
|
|
937
1054
|
*/
|
|
938
1055
|
declare const useLightTimeout: <T extends readonly unknown[]>(callback: TimerHandler<T>, options?: BasicTimerOptions<T>) => void;
|
|
939
1056
|
|
|
940
|
-
export { type AddEventListenerOptions, type BasicTimerOptions, type CommonListenerOptions, type CustomEventListenerOptions, type DocumentEventListener, type DocumentListenerOptions, type ElementEventListener, type ElementListenerOptions, type ListenerOptions, type MarginType, type MarginValue, type MediaQueryChangeListener, type MediaQueryEventListener, type MediaQueryListenerOptions, type
|
|
1057
|
+
export { type AddEventListenerOptions, type BasicTimerOptions, type CommonListenerOptions, type CustomEventListenerOptions, type DocumentEventListener, type DocumentListenerOptions, type ElementEventListener, type ElementListenerOptions, type IntersectionState, type ListenerOptions, type MarginType, type MarginValue, type MediaQueryChangeListener, type MediaQueryEventListener, type MediaQueryListenerOptions, type OnChangeHandler, type OnIntersectHandler, type OnIntersectStateHandler, type StartTimer, type StateTimerOptions, type StateTimerReturnType, type StopTimer, type TimerHandler, type TimerId, type TimerOptions, type TimerReturnType, type UseDarkModeOptions, type UseDarkModeOutput, type UseInViewOptions, type UseInViewReturnType, type UseIntervalWhenVisibleReturnType, type UseIntervalWhenVisibleStateReturnType, type UseMediaQueryOptions, type UseMediaQueryStateOptions, type WindowEventListener, type WindowListenerOptions, useDarkMode, useDebounce, useEffectOnce, useEventListener, useFocusTrap, useInView, useInterval, useIntervalWhenVisible, useIsClient, useIsFirstRender, useIsPortrait, useLightInterval, useLightTimeout, useLocalStorage, useMediaQuery, useScrollBlock, useSessionStorage, useStorage, useTimeout, useUpdateEffect };
|
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 _LocalStorage = require('@alessiofrittoli/web-utils/storage/LocalStorage');var _SessionStorage = require('@alessiofrittoli/web-utils/storage/SessionStorage');var
|
|
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 _LocalStorage = require('@alessiofrittoli/web-utils/storage/LocalStorage');var _SessionStorage = require('@alessiofrittoli/web-utils/storage/SessionStorage');var g=(e,o,t="local")=>{let s=_react.useCallback.call(void 0, ()=>_nullishCoalesce((t==="local"?_LocalStorage.LocalStorage:_SessionStorage.SessionStorage).get(e), () => (o)),[t,e,o]),[i,a]=_react.useState.call(void 0, o),r=_react.useCallback.call(void 0, n=>{a(u=>{let l=n instanceof Function?n(u):n;return(typeof window<"u"&&t==="local"?_LocalStorage.LocalStorage:_SessionStorage.SessionStorage).set(e,l),l})},[t,e]);return _react.useEffect.call(void 0, ()=>{a(s())},[s]),[i,r]};var K=(e,o)=>g(e,o,"local");var we=(e,o)=>g(e,o,"session");var O=()=>{let e=_react.useRef.call(void 0, !0);return e.current?(e.current=!1,!0):e.current};var $e=e=>{let o=O();_react.useEffect.call(void 0, ()=>{if(o)return e()},[])};var W=()=>{let[e,o]=_react.useState.call(void 0, !1);return _react.useEffect.call(void 0, ()=>o(!0),[]),e};var k=(e,o)=>{let t=O();_react.useEffect.call(void 0, ()=>{if(!t)return e()},o)};var _browserapi = require('@alessiofrittoli/web-utils/browser-api');function S(e,o={}){let{updateState:t=!0,onChange:s}=o,[i,a]=_react.useState.call(void 0, _browserapi.getMediaMatches.call(void 0, e)),r=_react.useCallback.call(void 0, ()=>{let n=_browserapi.getMediaMatches.call(void 0, e);t&&a(n),_optionalChain([s, 'optionalCall', _2 => _2(n)])},[e,t,s]);if(_react.useEffect.call(void 0, ()=>{let n=window.matchMedia(e),{matches:u}=n;return t&&a(u),_optionalChain([s, 'optionalCall', _3 => _3(u)]),n.addEventListener("change",r),()=>{n.removeEventListener("change",r)}},[e,t,s,r]),!!t)return i}var st=(e={})=>{let o=W(),t=S("(prefers-color-scheme: dark)"),{initial:s=t,docClassNames:i=[]}=e,[a,r]=K("dark-mode",s),n=_nullishCoalesce(a, () => (t)),[u,l]=i,c=_react.useRef.call(void 0, {light:"",dark:""});return k(()=>{r(t)},[t,r]),_react.useEffect.call(void 0, ()=>{u&&document.documentElement.classList.toggle(u,n),l&&document.documentElement.classList.toggle(l,!n)},[n,u,l]),_react.useEffect.call(void 0, ()=>{document.head.querySelectorAll('meta[name="theme-color"]').forEach(p=>{let d=p.getAttribute("media"),f=p.getAttribute("content");if(f){if(!d||d==="(prefers-color-scheme: light)"){c.current.light=f;return}c.current.dark=f}})},[]),k(()=>{let p=c.current.dark,d=c.current.light;a&&!p||!a&&!d||document.head.querySelectorAll('meta[name="theme-color"]').forEach(f=>{f.setAttribute("content",a?p:d)})},[a]),{isDarkMode:o?n:!1,isDarkOS:o?t:!1,toggleDarkMode:_react.useCallback.call(void 0, ()=>r(p=>!p),[r]),enableDarkMode:_react.useCallback.call(void 0, ()=>r(!0),[r]),disableDarkMode:_react.useCallback.call(void 0, ()=>r(!1),[r])}};function ct(e,o){let{target:t,query:s,options:i,listener:a,onLoad:r,onCleanUp:n}=o;_react.useEffect.call(void 0, ()=>{let u=Array.isArray(e)?e:[e],l=_nullishCoalesce((s?window.matchMedia(s):t&&"current"in t?t.current:t), () => (window));if(l.addEventListener)return _optionalChain([r, 'optionalCall', _4 => _4()]),u.map(c=>{l.addEventListener(c,a,i)}),()=>{u.map(c=>{l.removeEventListener(c,a,i)}),_optionalChain([n, 'optionalCall', _5 => _5()])}},[e,t,s,i,a,r,n])}var _device = require('@alessiofrittoli/web-utils/device');var ft=()=>S(_device.portraitMediaQuery);var ue=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),vt= exports.useFocusTrap =e=>{let[o,t]=_react.useState.call(void 0, !1),s=_react.useRef.call(void 0, null),i=_react.useCallback.call(void 0, r=>{s.current=document.activeElement;let n=r||_optionalChain([e, 'optionalAccess', _6 => _6.current])||!1;if(n)return t(n)},[e]),a=_react.useCallback.call(void 0, ()=>{_optionalChain([s, 'access', _7 => _7.current, 'optionalAccess', _8 => _8.focus, 'call', _9 => _9()]),t(!1)},[]);return _react.useEffect.call(void 0, ()=>{if(!o)return;let r=n=>{if(n.key!=="Tab")return;let u=Array.from(o.querySelectorAll(ue)),l=u.at(0),c=u.at(-1);if(!n.shiftKey){document.activeElement===c&&(n.preventDefault(),_optionalChain([l, 'optionalAccess', _10 => _10.focus, 'call', _11 => _11()]));return}document.activeElement===l&&(n.preventDefault(),_optionalChain([c, 'optionalAccess', _12 => _12.focus, 'call', _13 => _13()]))};return document.addEventListener("keydown",r),()=>{document.removeEventListener("keydown",r)}},[o]),[i,a]};var Ot=(e,o={})=>{let{initial:t=!1,once:s,amount:i,margin:a,root:r,enable:n=!0}=o,{onEnter:u,onExit:l,onIntersect:c}=o,p=_react.useRef.call(void 0, !0),[d,f]=_react.useState.call(void 0, t),[E,q]=_react.useState.call(void 0, n),b=_react.useRef.call(void 0, null),x=_react.useRef.call(void 0, !1),v=_react.useMemo.call(void 0, ()=>{if(!E||typeof IntersectionObserver>"u")return;let N=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([y],M)=>{if(!y)return;let T=y.isIntersecting;try{if(x.current=!T&&!!b.current,T&&u&&await u({entry:y,observer:M}),x.current&&l&&await l({entry:y,observer:M}),c&&(T||!T&&b.current!=null)){let L={isEntering:T,isExiting:x.current};await c({entry:y,observer:M,...L})}if(b.current=T,!p.current)return;f(T)}catch(L){console.error(L)}T&&s&&M.disconnect()},{root:r||void 0,rootMargin:a,threshold:N})}catch(y){console.error(y)}},[r,a,i,s,E,u,l,c]);return _react.useEffect.call(void 0, ()=>{if(p.current=!0,!(!E||!e.current||!v))return v.observe(e.current),()=>{p.current=!1,v.disconnect()}},[e,v,E]),{inView:d,enabled:E,observer:v,isExiting:x.current,setInView:f,setEnabled:q}};var _dom = require('@alessiofrittoli/web-utils/dom');var ht=e=>{let o=_react.useCallback.call(void 0, ()=>_dom.blockScroll.call(void 0, _optionalChain([e, 'optionalAccess', _14 => _14.current])||void 0),[e]),t=_react.useCallback.call(void 0, ()=>_dom.restoreScroll.call(void 0, _optionalChain([e, 'optionalAccess', _15 => _15.current])||void 0),[e]);return[o,t]};var j=(e,o={})=>{let{delay:t=1,args:s}=o;_react.useEffect.call(void 0, ()=>{let i=setTimeout(e,t,...s||[]);return()=>clearTimeout(i)},[t,s,e])};var Kt=(e,o=500)=>{let[t,s]=_react.useState.call(void 0, e),i=_react.useMemo.call(void 0, ()=>[e],[e]);return j(s,{delay:o,args:i}),t};function B(e,o={}){let{delay:t=1,args:s,autoplay:i=!0,runOnStart:a=!1,updateState:r=!1}=o,n=_react.useRef.call(void 0, void 0),[u,l]=_react.useState.call(void 0, i),c=_react.useCallback.call(void 0, ()=>n.current?(clearInterval(n.current),n.current=void 0,!0):!1,[]),p=_react.useCallback.call(void 0, ()=>{let f=c();return a&&(s?e(...s):e()),n.current=setInterval(e,t,...s||[]),!f&&r&&l(!0),n.current},[t,s,r,a,e,c]),d=_react.useCallback.call(void 0, ()=>{c()&&r&&l(!1)},[r,c]);return _react.useEffect.call(void 0, ()=>{if(i)return p(),d},[i,p,d]),r?{isActive:u,start:p,stop:d}:{start:p,stop:d}}var jt=(e,o={})=>{let{delay:t=1,args:s}=o;_react.useEffect.call(void 0, ()=>{let i=setInterval(e,t,...s||[]);return()=>clearInterval(i)},[t,s,e])};function zt(e,o={}){let{autoplay:t=!0}=o,s=B(e,{autoplay:!1,...o}),{start:i,stop:a}=s,r=_react.useCallback.call(void 0, ()=>document.hidden?a():i(),[i,a]),n=_react.useCallback.call(void 0, ()=>{if(document.addEventListener("visibilitychange",r),!document.hidden)return i()},[i,r]),u=_react.useCallback.call(void 0, ()=>{a(),document.removeEventListener("visibilitychange",r)},[a,r]);return _react.useEffect.call(void 0, ()=>{if(t)return n(),u},[t,n,u]),{...s,start:n,stop:u}}function Yt(e,o={}){let{delay:t=1,args:s,autoplay:i=!0,runOnStart:a=!1,updateState:r=!1}=o,n=_react.useRef.call(void 0, void 0),[u,l]=_react.useState.call(void 0, i),c=_react.useCallback.call(void 0, ()=>n.current?(clearTimeout(n.current),n.current=void 0,!0):!1,[]),p=_react.useCallback.call(void 0, ()=>{let f=c();return a&&(s?e(...s):e()),n.current=setTimeout(()=>{if(n.current=void 0,r&&l(!1),s)return e(...s);e()},t),!f&&r&&l(!0),n.current},[t,s,r,a,e,c]),d=_react.useCallback.call(void 0, ()=>{c()&&r&&l(!1)},[r,c]);return _react.useEffect.call(void 0, ()=>{if(i)return p(),d},[i,p,d]),r?{isActive:u,start:p,stop:d}:{start:p,stop:d}}exports.useDarkMode = st; exports.useDebounce = Kt; exports.useEffectOnce = $e; exports.useEventListener = ct; exports.useFocusTrap = vt; exports.useInView = Ot; exports.useInterval = B; exports.useIntervalWhenVisible = zt; exports.useIsClient = W; exports.useIsFirstRender = O; exports.useIsPortrait = ft; exports.useLightInterval = jt; exports.useLightTimeout = j; exports.useLocalStorage = K; exports.useMediaQuery = S; exports.useScrollBlock = ht; exports.useSessionStorage = we; exports.useStorage = g; exports.useTimeout = Yt; exports.useUpdateEffect = k;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useCallback as
|
|
1
|
+
import{useCallback as w,useEffect as z,useState as G}from"react";import{LocalStorage as V}from"@alessiofrittoli/web-utils/storage/LocalStorage";import{SessionStorage as K}from"@alessiofrittoli/web-utils/storage/SessionStorage";var O=(e,o,t="local")=>{let s=w(()=>(t==="local"?V:K).get(e)??o,[t,e,o]),[i,a]=G(o),r=w(n=>{a(u=>{let l=n instanceof Function?n(u):n;return(typeof window<"u"&&t==="local"?V:K).set(e,l),l})},[t,e]);return z(()=>{a(s())},[s]),[i,r]};var W=(e,o)=>O(e,o,"local");var Ve=(e,o)=>O(e,o,"session");import{useCallback as R,useEffect as Q,useRef as re}from"react";import{useEffect as X}from"react";import{useRef as J}from"react";var S=()=>{let e=J(!0);return e.current?(e.current=!1,!0):e.current};var je=e=>{let o=S();X(()=>{if(o)return e()},[])};import{useEffect as Y,useState as Z}from"react";var U=()=>{let[e,o]=Z(!1);return Y(()=>o(!0),[]),e};import{useEffect as _}from"react";var h=(e,o)=>{let t=S();_(()=>{if(!t)return e()},o)};import{useCallback as ee,useEffect as te,useState as ne}from"react";import{getMediaMatches as A}from"@alessiofrittoli/web-utils/browser-api";function b(e,o={}){let{updateState:t=!0,onChange:s}=o,[i,a]=ne(A(e)),r=ee(()=>{let n=A(e);t&&a(n),s?.(n)},[e,t,s]);if(te(()=>{let n=window.matchMedia(e),{matches:u}=n;return t&&a(u),s?.(u),n.addEventListener("change",r),()=>{n.removeEventListener("change",r)}},[e,t,s,r]),!!t)return i}var it=(e={})=>{let o=U(),t=b("(prefers-color-scheme: dark)"),{initial:s=t,docClassNames:i=[]}=e,[a,r]=W("dark-mode",s),n=a??t,[u,l]=i,c=re({light:"",dark:""});return h(()=>{r(t)},[t,r]),Q(()=>{u&&document.documentElement.classList.toggle(u,n),l&&document.documentElement.classList.toggle(l,!n)},[n,u,l]),Q(()=>{document.head.querySelectorAll('meta[name="theme-color"]').forEach(p=>{let f=p.getAttribute("media"),T=p.getAttribute("content");if(T){if(!f||f==="(prefers-color-scheme: light)"){c.current.light=T;return}c.current.dark=T}})},[]),h(()=>{let p=c.current.dark,f=c.current.light;a&&!p||!a&&!f||document.head.querySelectorAll('meta[name="theme-color"]').forEach(T=>{T.setAttribute("content",a?p:f)})},[a]),{isDarkMode:o?n:!1,isDarkOS:o?t:!1,toggleDarkMode:R(()=>r(p=>!p),[r]),enableDarkMode:R(()=>r(!0),[r]),disableDarkMode:R(()=>r(!1),[r])}};import{useEffect as oe}from"react";function lt(e,o){let{target:t,query:s,options:i,listener:a,onLoad:r,onCleanUp:n}=o;oe(()=>{let u=Array.isArray(e)?e:[e],l=(s?window.matchMedia(s):t&&"current"in t?t.current:t)??window;if(l.addEventListener)return r?.(),u.map(c=>{l.addEventListener(c,a,i)}),()=>{u.map(c=>{l.removeEventListener(c,a,i)}),n?.()}},[e,t,s,i,a,r,n])}import{portraitMediaQuery as se}from"@alessiofrittoli/web-utils/device";var Tt=()=>b(se);import{useCallback as F,useEffect as ie,useRef as ae,useState as ue}from"react";var ce=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),xt=e=>{let[o,t]=ue(!1),s=ae(null),i=F(r=>{s.current=document.activeElement;let n=r||e?.current||!1;if(n)return t(n)},[e]),a=F(()=>{s.current?.focus(),t(!1)},[]);return ie(()=>{if(!o)return;let r=n=>{if(n.key!=="Tab")return;let u=Array.from(o.querySelectorAll(ce)),l=u.at(0),c=u.at(-1);if(!n.shiftKey){document.activeElement===c&&(n.preventDefault(),l?.focus());return}document.activeElement===l&&(n.preventDefault(),c?.focus())};return document.addEventListener("keydown",r),()=>{document.removeEventListener("keydown",r)}},[o]),[i,a]};import{useEffect as le,useMemo as pe,useRef as I,useState as $}from"react";var St=(e,o={})=>{let{initial:t=!1,once:s,amount:i,margin:a,root:r,enable:n=!0}=o,{onEnter:u,onExit:l,onIntersect:c}=o,p=I(!0),[f,T]=$(t),[v,N]=$(n),L=I(null),M=I(!1),x=pe(()=>{if(!v||typeof IntersectionObserver>"u")return;let P=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([E],g)=>{if(!E)return;let y=E.isIntersecting;try{if(M.current=!y&&!!L.current,y&&u&&await u({entry:E,observer:g}),M.current&&l&&await l({entry:E,observer:g}),c&&(y||!y&&L.current!=null)){let k={isEntering:y,isExiting:M.current};await c({entry:E,observer:g,...k})}if(L.current=y,!p.current)return;T(y)}catch(k){console.error(k)}y&&s&&g.disconnect()},{root:r||void 0,rootMargin:a,threshold:P})}catch(E){console.error(E)}},[r,a,i,s,v,u,l,c]);return le(()=>{if(p.current=!0,!(!v||!e.current||!x))return x.observe(e.current),()=>{p.current=!1,x.disconnect()}},[e,x,v]),{inView:f,enabled:v,observer:x,isExiting:M.current,setInView:T,setEnabled:N}};import{useCallback as j}from"react";import{blockScroll as me,restoreScroll as de}from"@alessiofrittoli/web-utils/dom";var Rt=e=>{let o=j(()=>me(e?.current||void 0),[e]),t=j(()=>de(e?.current||void 0),[e]);return[o,t]};import{useMemo as Te,useState as ye}from"react";import{useEffect as fe}from"react";var B=(e,o={})=>{let{delay:t=1,args:s}=o;fe(()=>{let i=setTimeout(e,t,...s||[]);return()=>clearTimeout(i)},[t,s,e])};var Wt=(e,o=500)=>{let[t,s]=ye(e),i=Te(()=>[e],[e]);return B(s,{delay:o,args:i}),t};import{useCallback as H,useEffect as Ee,useRef as ve,useState as xe}from"react";function q(e,o={}){let{delay:t=1,args:s,autoplay:i=!0,runOnStart:a=!1,updateState:r=!1}=o,n=ve(void 0),[u,l]=xe(i),c=H(()=>n.current?(clearInterval(n.current),n.current=void 0,!0):!1,[]),p=H(()=>{let T=c();return a&&(s?e(...s):e()),n.current=setInterval(e,t,...s||[]),!T&&r&&l(!0),n.current},[t,s,r,a,e,c]),f=H(()=>{c()&&r&&l(!1)},[r,c]);return Ee(()=>{if(i)return p(),f},[i,p,f]),r?{isActive:u,start:p,stop:f}:{start:p,stop:f}}import{useEffect as Me}from"react";var Bt=(e,o={})=>{let{delay:t=1,args:s}=o;Me(()=>{let i=setInterval(e,t,...s||[]);return()=>clearInterval(i)},[t,s,e])};import{useCallback as C,useEffect as ge}from"react";function Gt(e,o={}){let{autoplay:t=!0}=o,s=q(e,{autoplay:!1,...o}),{start:i,stop:a}=s,r=C(()=>document.hidden?a():i(),[i,a]),n=C(()=>{if(document.addEventListener("visibilitychange",r),!document.hidden)return i()},[i,r]),u=C(()=>{a(),document.removeEventListener("visibilitychange",r)},[a,r]);return ge(()=>{if(t)return n(),u},[t,n,u]),{...s,start:n,stop:u}}import{useCallback as D,useEffect as Oe,useRef as Se,useState as be}from"react";function Zt(e,o={}){let{delay:t=1,args:s,autoplay:i=!0,runOnStart:a=!1,updateState:r=!1}=o,n=Se(void 0),[u,l]=be(i),c=D(()=>n.current?(clearTimeout(n.current),n.current=void 0,!0):!1,[]),p=D(()=>{let T=c();return a&&(s?e(...s):e()),n.current=setTimeout(()=>{if(n.current=void 0,r&&l(!1),s)return e(...s);e()},t),!T&&r&&l(!0),n.current},[t,s,r,a,e,c]),f=D(()=>{c()&&r&&l(!1)},[r,c]);return Oe(()=>{if(i)return p(),f},[i,p,f]),r?{isActive:u,start:p,stop:f}:{start:p,stop:f}}export{it as useDarkMode,Wt as useDebounce,je as useEffectOnce,lt as useEventListener,xt as useFocusTrap,St as useInView,q as useInterval,Gt as useIntervalWhenVisible,U as useIsClient,S as useIsFirstRender,Tt as useIsPortrait,Bt as useLightInterval,B as useLightTimeout,W as useLocalStorage,b as useMediaQuery,Rt as useScrollBlock,Ve as useSessionStorage,O as useStorage,Zt as useTimeout,h as useUpdateEffect};
|