@alessiofrittoli/react-hooks 2.0.0 → 3.0.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
@@ -682,9 +682,17 @@ Check if the given target Element is intersecting with an ancestor Element or wi
682
682
  | | | - `number` |
683
683
  | | | - `number[]` |
684
684
  | `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`. |
685
+ | `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
686
  | `options.enable` | `boolean` | (Optional) Defines the initial observation activity. Use the returned `setEnabled` to update this state. Default: `true`. |
687
- | `options.onStart` | `OnStartHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
687
+ | `options.onIntersect` | `OnIntersectStateHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
688
+ | | | This callback is awaited before any state update. |
689
+ | | | If an error is thrown the React State update won't be fired. |
690
+ | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
691
+ | `options.onEnter` | `OnIntersectHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
692
+ | | | This callback is awaited before any state update. |
693
+ | | | If an error is thrown the React State update won't be fired. |
694
+ | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
695
+ | `options.onExit` | `OnIntersectHandler` | (Optional) A custom callback executed when target element's visibility has crossed one or more thresholds. |
688
696
  | | | This callback is awaited before any state update. |
689
697
  | | | If an error is thrown the React State update won't be fired. |
690
698
  | | | ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation. |
@@ -839,21 +847,63 @@ const OnDemandObservation: React.FC = () => {
839
847
  'use client'
840
848
 
841
849
  import { useRef } from 'react'
842
- import { useInView, type OnStartHandler } from '@alessiofrittoli/react-hooks'
850
+ import { useInView, type OnIntersectStateHandler } from '@alessiofrittoli/react-hooks'
843
851
 
844
852
 
845
853
  const AsyncStartExample: React.FC = () => {
846
854
 
847
855
  const targetRef = useRef<HTMLDivElement>( null )
848
- const onStart = useCallback<OnStartHandler>( async entry => {
856
+ const onIntersect = useCallback<OnIntersectStateHandler>( async ( { entry, isEntering } ) => {
849
857
 
858
+ if ( isEntering ) {
859
+ console.log( 'Delaying state update...' )
860
+ await new Promise( resolve => setTimeout( resolve, 1000 ) ) // Simulate delay
861
+ console.log( 'Async task completed. `inView` will now be updated.' )
862
+ return
863
+ }
864
+
850
865
  console.log( 'Delaying state update...' )
851
866
  await new Promise( resolve => setTimeout( resolve, 1000 ) ) // Simulate delay
852
867
  console.log( 'Async task completed. `inView` will now be updated.' )
853
868
 
854
869
  }, [] )
855
870
 
856
- const { inView } = useInView( targetRef, { onStart } )
871
+ const { inView } = useInView( targetRef, { onIntersect } )
872
+
873
+ return (
874
+ <div
875
+ ref={ targetRef }
876
+ style={ {
877
+ height : 200,
878
+ background : inView ? 'lime' : 'gray',
879
+ } }
880
+ />
881
+ )
882
+ }
883
+ ```
884
+
885
+ ---
886
+
887
+ ###### Execute custom callback when `onEnter` and `onExit`
888
+
889
+ ```tsx
890
+ 'use client'
891
+
892
+ import { useRef } from 'react'
893
+ import { useInView, type OnIntersectHandler } from '@alessiofrittoli/react-hooks'
894
+
895
+
896
+ const AsyncStartExample: React.FC = () => {
897
+
898
+ const targetRef = useRef<HTMLDivElement>( null )
899
+ const onEnter = useCallback<OnIntersectHandler>( async ( { entry } ) => {
900
+ console.log( 'In viewport - ', entry )
901
+ }, [] )
902
+ const onExit = useCallback<OnIntersectHandler>( async ( { entry } ) => {
903
+ console.log( 'Exited viewport - ', entry )
904
+ }, [] )
905
+
906
+ const { inView } = useInView( targetRef, { onEnter, onExit } )
857
907
 
858
908
  return (
859
909
  <div
package/dist/index.d.mts CHANGED
@@ -507,6 +507,54 @@ declare const useFocusTrap: (target?: React.RefObject<HTMLElement | null>) => re
507
507
 
508
508
  type MarginValue = `${number}${'px' | '%'}`;
509
509
  type MarginType = (MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue} ${MarginValue}`);
510
+ type IntersectionState = ({
511
+ /**
512
+ * Indicates whether the {@link IntersectionObserverEntry} is entering the viewport.
513
+ *
514
+ */
515
+ isEntering: true;
516
+ /**
517
+ * Indicates whether the {@link IntersectionObserverEntry} is exiting the viewport.
518
+ *
519
+ */
520
+ isExiting: false;
521
+ } | {
522
+ /**
523
+ * Indicates whether the {@link IntersectionObserverEntry} is entering the viewport.
524
+ *
525
+ */
526
+ isEntering: false;
527
+ /**
528
+ * Indicates whether the {@link IntersectionObserverEntry} is exiting the viewport.
529
+ *
530
+ */
531
+ isExiting: true;
532
+ });
533
+ interface OnIntersectHandlerOptions {
534
+ /**
535
+ * The intersecting {@link IntersectionObserverEntry}.
536
+ *
537
+ */
538
+ entry: IntersectionObserverEntry;
539
+ /**
540
+ * The current {@link IntersectionObserver} instance.
541
+ *
542
+ */
543
+ observer: IntersectionObserver;
544
+ }
545
+ type OnIntersectStateHandlerOptions = OnIntersectHandlerOptions & IntersectionState;
546
+ /**
547
+ * A custom callback executed when target element's visibility has crossed one or more thresholds.
548
+ *
549
+ * This callback is awaited before any state update.
550
+ *
551
+ * If an error is thrown the React State update won't be fired.
552
+ *
553
+ * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
554
+ *
555
+ * @param options An object defining the current intersection entry and observer.
556
+ */
557
+ type OnIntersectHandler = (options: OnIntersectHandlerOptions) => void | Promise<void>;
510
558
  /**
511
559
  * A custom callback executed when target element's visibility has crossed one or more thresholds.
512
560
  *
@@ -516,10 +564,9 @@ type MarginType = (MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValu
516
564
  *
517
565
  * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
518
566
  *
519
- * @param entry The intersecting {@link IntersectionObserverEntry}.
520
- * @param observer The current {@link IntersectionObserver} instance.
567
+ * @param options An object defining the current intersection entry, observer and entry state.
521
568
  */
522
- type OnStartHandler = (entry: IntersectionObserverEntry, observer: IntersectionObserver) => void | Promise<void>;
569
+ type OnIntersectStateHandler = (options: OnIntersectStateHandlerOptions) => void | Promise<void>;
523
570
  interface UseInViewOptions {
524
571
  /**
525
572
  * 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 +601,9 @@ interface UseInViewOptions {
554
601
  */
555
602
  once?: boolean;
556
603
  /**
557
- * Initial value.
604
+ * Initial value. This value is used while server rendering then will be updated in the client based on target visibility.
558
605
  *
559
- * @default true
606
+ * @default false
560
607
  */
561
608
  initial?: boolean;
562
609
  /**
@@ -574,10 +621,33 @@ interface UseInViewOptions {
574
621
  *
575
622
  * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
576
623
  *
577
- * @param entry The intersecting {@link IntersectionObserverEntry}.
578
- * @param observer The current {@link IntersectionObserver} instance.
624
+ * @param options An object defining the current intersection entry, observer and entry state.
625
+ */
626
+ onIntersect?: OnIntersectStateHandler;
627
+ /**
628
+ * A custom callback executed when target element is entering the viewport.
629
+ *
630
+ * This callback is awaited before any state update.
631
+ *
632
+ * If an error is thrown the React State update won't be fired.
633
+ *
634
+ * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
635
+ *
636
+ * @param options An object defining the current intersection entry and observer.
579
637
  */
580
- onStart?: OnStartHandler;
638
+ onEnter?: OnIntersectHandler;
639
+ /**
640
+ * A custom callback executed when target element is exiting the viewport.
641
+ *
642
+ * This callback is awaited before any state update.
643
+ *
644
+ * If an error is thrown the React State update won't be fired.
645
+ *
646
+ * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
647
+ *
648
+ * @param options An object defining the current intersection entry and observer.
649
+ */
650
+ onExit?: OnIntersectHandler;
581
651
  }
582
652
  interface UseInViewReturnType {
583
653
  /**
@@ -585,6 +655,11 @@ interface UseInViewReturnType {
585
655
  *
586
656
  */
587
657
  inView: boolean;
658
+ /**
659
+ * Indicates whether the target Element is exiting the viewport.
660
+ *
661
+ */
662
+ isExiting: boolean;
588
663
  /**
589
664
  * Indicates whether the target Element is being observed or not.
590
665
  *
@@ -937,4 +1012,4 @@ declare function useTimeout<T extends readonly unknown[]>(callback: TimerHandler
937
1012
  */
938
1013
  declare const useLightTimeout: <T extends readonly unknown[]>(callback: TimerHandler<T>, options?: BasicTimerOptions<T>) => void;
939
1014
 
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 OnStartHandler, type StartTimer, type StateTimerOptions, type StateTimerReturnType, type StopTimer, type TimerHandler, type TimerId, type TimerOptions, type TimerReturnType, type UseDarkModeOptions, type UseDarkModeOutput, type UseInViewOptions, type UseIntervalWhenVisibleReturnType, type UseIntervalWhenVisibleStateReturnType, type WindowEventListener, type WindowListenerOptions, useDarkMode, useDebounce, useEventListener, useFocusTrap, useInView, useInterval, useIntervalWhenVisible, useIsClient, useIsFirstRender, useIsPortrait, useLightInterval, useLightTimeout, useLocalStorage, useMediaQuery, useScrollBlock, useSessionStorage, useStorage, useTimeout, useUpdateEffect };
1015
+ 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 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 WindowEventListener, type WindowListenerOptions, useDarkMode, useDebounce, useEventListener, useFocusTrap, useInView, useInterval, useIntervalWhenVisible, useIsClient, useIsFirstRender, useIsPortrait, useLightInterval, useLightTimeout, useLocalStorage, useMediaQuery, useScrollBlock, useSessionStorage, useStorage, useTimeout, useUpdateEffect };
package/dist/index.d.ts CHANGED
@@ -507,6 +507,54 @@ declare const useFocusTrap: (target?: React.RefObject<HTMLElement | null>) => re
507
507
 
508
508
  type MarginValue = `${number}${'px' | '%'}`;
509
509
  type MarginType = (MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue} ${MarginValue}`);
510
+ type IntersectionState = ({
511
+ /**
512
+ * Indicates whether the {@link IntersectionObserverEntry} is entering the viewport.
513
+ *
514
+ */
515
+ isEntering: true;
516
+ /**
517
+ * Indicates whether the {@link IntersectionObserverEntry} is exiting the viewport.
518
+ *
519
+ */
520
+ isExiting: false;
521
+ } | {
522
+ /**
523
+ * Indicates whether the {@link IntersectionObserverEntry} is entering the viewport.
524
+ *
525
+ */
526
+ isEntering: false;
527
+ /**
528
+ * Indicates whether the {@link IntersectionObserverEntry} is exiting the viewport.
529
+ *
530
+ */
531
+ isExiting: true;
532
+ });
533
+ interface OnIntersectHandlerOptions {
534
+ /**
535
+ * The intersecting {@link IntersectionObserverEntry}.
536
+ *
537
+ */
538
+ entry: IntersectionObserverEntry;
539
+ /**
540
+ * The current {@link IntersectionObserver} instance.
541
+ *
542
+ */
543
+ observer: IntersectionObserver;
544
+ }
545
+ type OnIntersectStateHandlerOptions = OnIntersectHandlerOptions & IntersectionState;
546
+ /**
547
+ * A custom callback executed when target element's visibility has crossed one or more thresholds.
548
+ *
549
+ * This callback is awaited before any state update.
550
+ *
551
+ * If an error is thrown the React State update won't be fired.
552
+ *
553
+ * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
554
+ *
555
+ * @param options An object defining the current intersection entry and observer.
556
+ */
557
+ type OnIntersectHandler = (options: OnIntersectHandlerOptions) => void | Promise<void>;
510
558
  /**
511
559
  * A custom callback executed when target element's visibility has crossed one or more thresholds.
512
560
  *
@@ -516,10 +564,9 @@ type MarginType = (MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValu
516
564
  *
517
565
  * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
518
566
  *
519
- * @param entry The intersecting {@link IntersectionObserverEntry}.
520
- * @param observer The current {@link IntersectionObserver} instance.
567
+ * @param options An object defining the current intersection entry, observer and entry state.
521
568
  */
522
- type OnStartHandler = (entry: IntersectionObserverEntry, observer: IntersectionObserver) => void | Promise<void>;
569
+ type OnIntersectStateHandler = (options: OnIntersectStateHandlerOptions) => void | Promise<void>;
523
570
  interface UseInViewOptions {
524
571
  /**
525
572
  * 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 +601,9 @@ interface UseInViewOptions {
554
601
  */
555
602
  once?: boolean;
556
603
  /**
557
- * Initial value.
604
+ * Initial value. This value is used while server rendering then will be updated in the client based on target visibility.
558
605
  *
559
- * @default true
606
+ * @default false
560
607
  */
561
608
  initial?: boolean;
562
609
  /**
@@ -574,10 +621,33 @@ interface UseInViewOptions {
574
621
  *
575
622
  * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
576
623
  *
577
- * @param entry The intersecting {@link IntersectionObserverEntry}.
578
- * @param observer The current {@link IntersectionObserver} instance.
624
+ * @param options An object defining the current intersection entry, observer and entry state.
625
+ */
626
+ onIntersect?: OnIntersectStateHandler;
627
+ /**
628
+ * A custom callback executed when target element is entering the viewport.
629
+ *
630
+ * This callback is awaited before any state update.
631
+ *
632
+ * If an error is thrown the React State update won't be fired.
633
+ *
634
+ * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
635
+ *
636
+ * @param options An object defining the current intersection entry and observer.
579
637
  */
580
- onStart?: OnStartHandler;
638
+ onEnter?: OnIntersectHandler;
639
+ /**
640
+ * A custom callback executed when target element is exiting the viewport.
641
+ *
642
+ * This callback is awaited before any state update.
643
+ *
644
+ * If an error is thrown the React State update won't be fired.
645
+ *
646
+ * ⚠️ Wrap your callback with `useCallback` to avoid unnecessary `IntersectionObserver` recreation.
647
+ *
648
+ * @param options An object defining the current intersection entry and observer.
649
+ */
650
+ onExit?: OnIntersectHandler;
581
651
  }
582
652
  interface UseInViewReturnType {
583
653
  /**
@@ -585,6 +655,11 @@ interface UseInViewReturnType {
585
655
  *
586
656
  */
587
657
  inView: boolean;
658
+ /**
659
+ * Indicates whether the target Element is exiting the viewport.
660
+ *
661
+ */
662
+ isExiting: boolean;
588
663
  /**
589
664
  * Indicates whether the target Element is being observed or not.
590
665
  *
@@ -937,4 +1012,4 @@ declare function useTimeout<T extends readonly unknown[]>(callback: TimerHandler
937
1012
  */
938
1013
  declare const useLightTimeout: <T extends readonly unknown[]>(callback: TimerHandler<T>, options?: BasicTimerOptions<T>) => void;
939
1014
 
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 OnStartHandler, type StartTimer, type StateTimerOptions, type StateTimerReturnType, type StopTimer, type TimerHandler, type TimerId, type TimerOptions, type TimerReturnType, type UseDarkModeOptions, type UseDarkModeOutput, type UseInViewOptions, type UseIntervalWhenVisibleReturnType, type UseIntervalWhenVisibleStateReturnType, type WindowEventListener, type WindowListenerOptions, useDarkMode, useDebounce, useEventListener, useFocusTrap, useInView, useInterval, useIntervalWhenVisible, useIsClient, useIsFirstRender, useIsPortrait, useLightInterval, useLightTimeout, useLocalStorage, useMediaQuery, useScrollBlock, useSessionStorage, useStorage, useTimeout, useUpdateEffect };
1015
+ 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 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 WindowEventListener, type WindowListenerOptions, useDarkMode, useDebounce, 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 v=(e,r,t="local")=>{let n=_react.useCallback.call(void 0, ()=>_nullishCoalesce((t==="local"?_LocalStorage.LocalStorage:_SessionStorage.SessionStorage).get(e), () => (r)),[t,e,r]),[i,a]=_react.useState.call(void 0, r),o=_react.useCallback.call(void 0, s=>{a(l=>{let u=s instanceof Function?s(l):s;return(typeof window<"u"&&t==="local"?_LocalStorage.LocalStorage:_SessionStorage.SessionStorage).set(e,u),u})},[t,e]);return _react.useEffect.call(void 0, ()=>{a(n())},[n]),[i,o]};var D=(e,r)=>v(e,r,"local");var he=(e,r)=>v(e,r,"session");var w=()=>{let[e,r]=_react.useState.call(void 0, !1);return _react.useEffect.call(void 0, ()=>r(!0),[]),e};var I=()=>{let e=_react.useRef.call(void 0, !0);return e.current?(e.current=!1,!0):e.current};var M=(e,r)=>{let t=I();_react.useEffect.call(void 0, ()=>{if(!t)return e()},r)};var _browserapi = require('@alessiofrittoli/web-utils/browser-api');var E=e=>{let[r,t]=_react.useState.call(void 0, _browserapi.getMediaMatches.call(void 0, e)),n=_react.useCallback.call(void 0, ()=>t(_browserapi.getMediaMatches.call(void 0, e)),[e]);return _react.useEffect.call(void 0, ()=>{let i=window.matchMedia(e);return n(),i.addEventListener("change",n),()=>{i.removeEventListener("change",n)}},[e,n]),r};var Je=(e={})=>{let r=w(),t=E("(prefers-color-scheme: dark)"),{initial:n=t,docClassNames:i=[]}=e,[a,o]=D("dark-mode",n),s=_nullishCoalesce(a, () => (t)),[l,u]=i,c=_react.useRef.call(void 0, {light:"",dark:""});return M(()=>{o(t)},[t,o]),_react.useEffect.call(void 0, ()=>{l&&document.documentElement.classList.toggle(l,s),u&&document.documentElement.classList.toggle(u,!s)},[s,l,u]),_react.useEffect.call(void 0, ()=>{document.head.querySelectorAll('meta[name="theme-color"]').forEach(p=>{let m=p.getAttribute("media"),f=p.getAttribute("content");if(f){if(!m||m==="(prefers-color-scheme: light)"){c.current.light=f;return}c.current.dark=f}})},[]),M(()=>{let p=c.current.dark,m=c.current.light;a&&!p||!a&&!m||document.head.querySelectorAll('meta[name="theme-color"]').forEach(f=>{f.setAttribute("content",a?p:m)})},[a]),{isDarkMode:r?s:!1,isDarkOS:r?t:!1,toggleDarkMode:_react.useCallback.call(void 0, ()=>o(p=>!p),[o]),enableDarkMode:_react.useCallback.call(void 0, ()=>o(!0),[o]),disableDarkMode:_react.useCallback.call(void 0, ()=>o(!1),[o])}};function _e(e,r){let{target:t,query:n,options:i,listener:a,onLoad:o,onCleanUp:s}=r;_react.useEffect.call(void 0, ()=>{let l=Array.isArray(e)?e:[e],u=_nullishCoalesce((n?window.matchMedia(n):t&&"current"in t?t.current:t), () => (window));if(u.addEventListener)return _optionalChain([o, 'optionalCall', _2 => _2()]),l.map(c=>{u.addEventListener(c,a,i)}),()=>{l.map(c=>{u.removeEventListener(c,a,i)}),_optionalChain([s, 'optionalCall', _3 => _3()])}},[e,t,n,i,a,o,s])}var _device = require('@alessiofrittoli/web-utils/device');var ot=()=>E(_device.portraitMediaQuery);var ne=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),ut= exports.useFocusTrap =e=>{let[r,t]=_react.useState.call(void 0, !1),n=_react.useRef.call(void 0, null),i=_react.useCallback.call(void 0, o=>{n.current=document.activeElement;let s=o||_optionalChain([e, 'optionalAccess', _4 => _4.current])||!1;if(s)return t(s)},[e]),a=_react.useCallback.call(void 0, ()=>{_optionalChain([n, 'access', _5 => _5.current, 'optionalAccess', _6 => _6.focus, 'call', _7 => _7()]),t(!1)},[]);return _react.useEffect.call(void 0, ()=>{if(!r)return;let o=s=>{if(s.key!=="Tab")return;let l=Array.from(r.querySelectorAll(ne)),u=l.at(0),c=l.at(-1);if(!s.shiftKey){document.activeElement===c&&(s.preventDefault(),_optionalChain([u, 'optionalAccess', _8 => _8.focus, 'call', _9 => _9()]));return}document.activeElement===u&&(s.preventDefault(),_optionalChain([c, 'optionalAccess', _10 => _10.focus, 'call', _11 => _11()]))};return document.addEventListener("keydown",o),()=>{document.removeEventListener("keydown",o)}},[r]),[i,a]};var pt=(e,r={})=>{let{initial:t=!1,once:n,amount:i,margin:a,root:o,enable:s=!0,onStart:l}=r,u=_react.useRef.call(void 0, !0),[c,p]=_react.useState.call(void 0, t),[m,f]=_react.useState.call(void 0, s),T=_react.useMemo.call(void 0, ()=>{if(!m||typeof IntersectionObserver>"u")return;let F=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([y],g)=>{if(!y)return;let k=y.isIntersecting;try{if(await _optionalChain([l, 'optionalCall', _12 => _12(y,g)]),!u.current)return;p(k)}catch(Q){console.error(Q)}k&&n&&g.disconnect()},{root:o||void 0,rootMargin:a,threshold:F})}catch(y){console.error(y)}},[o,a,i,n,m,l]);return _react.useEffect.call(void 0, ()=>{if(u.current=!0,!(!m||!e.current||!T))return T.observe(e.current),()=>{u.current=!1,T.disconnect()}},[e,T,m]),{inView:c,enabled:m,observer:T,setInView:p,setEnabled:f}};var _dom = require('@alessiofrittoli/web-utils/dom');var vt=e=>{let r=_react.useCallback.call(void 0, ()=>_dom.blockScroll.call(void 0, _optionalChain([e, 'optionalAccess', _13 => _13.current])||void 0),[e]),t=_react.useCallback.call(void 0, ()=>_dom.restoreScroll.call(void 0, _optionalChain([e, 'optionalAccess', _14 => _14.current])||void 0),[e]);return[r,t]};var A=(e,r={})=>{let{delay:t=1,args:n}=r;_react.useEffect.call(void 0, ()=>{let i=setTimeout(e,t,...n||[]);return()=>clearTimeout(i)},[t,n,e])};var kt=(e,r=500)=>{let[t,n]=_react.useState.call(void 0, e),i=_react.useMemo.call(void 0, ()=>[e],[e]);return A(n,{delay:r,args:i}),t};function U(e,r={}){let{delay:t=1,args:n,autoplay:i=!0,runOnStart:a=!1,updateState:o=!1}=r,s=_react.useRef.call(void 0, void 0),[l,u]=_react.useState.call(void 0, i),c=_react.useCallback.call(void 0, ()=>s.current?(clearInterval(s.current),s.current=void 0,!0):!1,[]),p=_react.useCallback.call(void 0, ()=>{let f=c();return a&&(n?e(...n):e()),s.current=setInterval(e,t,...n||[]),!f&&o&&u(!0),s.current},[t,n,o,a,e,c]),m=_react.useCallback.call(void 0, ()=>{c()&&o&&u(!1)},[o,c]);return _react.useEffect.call(void 0, ()=>{if(i)return p(),m},[i,p,m]),o?{isActive:l,start:p,stop:m}:{start:p,stop:m}}var Ht=(e,r={})=>{let{delay:t=1,args:n}=r;_react.useEffect.call(void 0, ()=>{let i=setInterval(e,t,...n||[]);return()=>clearInterval(i)},[t,n,e])};function At(e,r={}){let{autoplay:t=!0}=r,n=U(e,{autoplay:!1,...r}),{start:i,stop:a}=n,o=_react.useCallback.call(void 0, ()=>document.hidden?a():i(),[i,a]),s=_react.useCallback.call(void 0, ()=>{if(document.addEventListener("visibilitychange",o),!document.hidden)return i()},[i,o]),l=_react.useCallback.call(void 0, ()=>{a(),document.removeEventListener("visibilitychange",o)},[a,o]);return _react.useEffect.call(void 0, ()=>{if(t)return s(),l},[t,s,l]),{...n,start:s,stop:l}}function $t(e,r={}){let{delay:t=1,args:n,autoplay:i=!0,runOnStart:a=!1,updateState:o=!1}=r,s=_react.useRef.call(void 0, void 0),[l,u]=_react.useState.call(void 0, i),c=_react.useCallback.call(void 0, ()=>s.current?(clearTimeout(s.current),s.current=void 0,!0):!1,[]),p=_react.useCallback.call(void 0, ()=>{let f=c();return a&&(n?e(...n):e()),s.current=setTimeout(()=>{if(s.current=void 0,o&&u(!1),n)return e(...n);e()},t),!f&&o&&u(!0),s.current},[t,n,o,a,e,c]),m=_react.useCallback.call(void 0, ()=>{c()&&o&&u(!1)},[o,c]);return _react.useEffect.call(void 0, ()=>{if(i)return p(),m},[i,p,m]),o?{isActive:l,start:p,stop:m}:{start:p,stop:m}}exports.useDarkMode = Je; exports.useDebounce = kt; exports.useEventListener = _e; exports.useFocusTrap = ut; exports.useInView = pt; exports.useInterval = U; exports.useIntervalWhenVisible = At; exports.useIsClient = w; exports.useIsFirstRender = I; exports.useIsPortrait = ot; exports.useLightInterval = Ht; exports.useLightTimeout = A; exports.useLocalStorage = D; exports.useMediaQuery = E; exports.useScrollBlock = vt; exports.useSessionStorage = he; exports.useStorage = v; exports.useTimeout = $t; exports.useUpdateEffect = M;
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,r,t="local")=>{let n=_react.useCallback.call(void 0, ()=>_nullishCoalesce((t==="local"?_LocalStorage.LocalStorage:_SessionStorage.SessionStorage).get(e), () => (r)),[t,e,r]),[i,a]=_react.useState.call(void 0, r),o=_react.useCallback.call(void 0, s=>{a(l=>{let c=s instanceof Function?s(l):s;return(typeof window<"u"&&t==="local"?_LocalStorage.LocalStorage:_SessionStorage.SessionStorage).set(e,c),c})},[t,e]);return _react.useEffect.call(void 0, ()=>{a(n())},[n]),[i,o]};var V=(e,r)=>g(e,r,"local");var we=(e,r)=>g(e,r,"session");var K=()=>{let[e,r]=_react.useState.call(void 0, !1);return _react.useEffect.call(void 0, ()=>r(!0),[]),e};var W=()=>{let e=_react.useRef.call(void 0, !0);return e.current?(e.current=!1,!0):e.current};var L=(e,r)=>{let t=W();_react.useEffect.call(void 0, ()=>{if(!t)return e()},r)};var _browserapi = require('@alessiofrittoli/web-utils/browser-api');var S=e=>{let[r,t]=_react.useState.call(void 0, _browserapi.getMediaMatches.call(void 0, e)),n=_react.useCallback.call(void 0, ()=>t(_browserapi.getMediaMatches.call(void 0, e)),[e]);return _react.useEffect.call(void 0, ()=>{let i=window.matchMedia(e);return n(),i.addEventListener("change",n),()=>{i.removeEventListener("change",n)}},[e,n]),r};var _e=(e={})=>{let r=K(),t=S("(prefers-color-scheme: dark)"),{initial:n=t,docClassNames:i=[]}=e,[a,o]=V("dark-mode",n),s=_nullishCoalesce(a, () => (t)),[l,c]=i,u=_react.useRef.call(void 0, {light:"",dark:""});return L(()=>{o(t)},[t,o]),_react.useEffect.call(void 0, ()=>{l&&document.documentElement.classList.toggle(l,s),c&&document.documentElement.classList.toggle(c,!s)},[s,l,c]),_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)"){u.current.light=f;return}u.current.dark=f}})},[]),L(()=>{let p=u.current.dark,d=u.current.light;a&&!p||!a&&!d||document.head.querySelectorAll('meta[name="theme-color"]').forEach(f=>{f.setAttribute("content",a?p:d)})},[a]),{isDarkMode:r?s:!1,isDarkOS:r?t:!1,toggleDarkMode:_react.useCallback.call(void 0, ()=>o(p=>!p),[o]),enableDarkMode:_react.useCallback.call(void 0, ()=>o(!0),[o]),disableDarkMode:_react.useCallback.call(void 0, ()=>o(!1),[o])}};function rt(e,r){let{target:t,query:n,options:i,listener:a,onLoad:o,onCleanUp:s}=r;_react.useEffect.call(void 0, ()=>{let l=Array.isArray(e)?e:[e],c=_nullishCoalesce((n?window.matchMedia(n):t&&"current"in t?t.current:t), () => (window));if(c.addEventListener)return _optionalChain([o, 'optionalCall', _2 => _2()]),l.map(u=>{c.addEventListener(u,a,i)}),()=>{l.map(u=>{c.removeEventListener(u,a,i)}),_optionalChain([s, 'optionalCall', _3 => _3()])}},[e,t,n,i,a,o,s])}var _device = require('@alessiofrittoli/web-utils/device');var ut=()=>S(_device.portraitMediaQuery);var ae=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),mt= exports.useFocusTrap =e=>{let[r,t]=_react.useState.call(void 0, !1),n=_react.useRef.call(void 0, null),i=_react.useCallback.call(void 0, o=>{n.current=document.activeElement;let s=o||_optionalChain([e, 'optionalAccess', _4 => _4.current])||!1;if(s)return t(s)},[e]),a=_react.useCallback.call(void 0, ()=>{_optionalChain([n, 'access', _5 => _5.current, 'optionalAccess', _6 => _6.focus, 'call', _7 => _7()]),t(!1)},[]);return _react.useEffect.call(void 0, ()=>{if(!r)return;let o=s=>{if(s.key!=="Tab")return;let l=Array.from(r.querySelectorAll(ae)),c=l.at(0),u=l.at(-1);if(!s.shiftKey){document.activeElement===u&&(s.preventDefault(),_optionalChain([c, 'optionalAccess', _8 => _8.focus, 'call', _9 => _9()]));return}document.activeElement===c&&(s.preventDefault(),_optionalChain([u, 'optionalAccess', _10 => _10.focus, 'call', _11 => _11()]))};return document.addEventListener("keydown",o),()=>{document.removeEventListener("keydown",o)}},[r]),[i,a]};var Et=(e,r={})=>{let{initial:t=!1,once:n,amount:i,margin:a,root:o,enable:s=!0}=r,{onEnter:l,onExit:c,onIntersect:u}=r,p=_react.useRef.call(void 0, !0),[d,f]=_react.useState.call(void 0, t),[y,N]=_react.useState.call(void 0, s),O=_react.useRef.call(void 0, null),x=_react.useRef.call(void 0, !1),v=_react.useMemo.call(void 0, ()=>{if(!y||typeof IntersectionObserver>"u")return;let P=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([E],M)=>{if(!E)return;let T=E.isIntersecting;try{if(x.current=!T&&!!O.current,T&&l&&await l({entry:E,observer:M}),x.current&&c&&await c({entry:E,observer:M}),u&&(T||!T&&O.current!=null)){let b={isEntering:T,isExiting:x.current};await u({entry:E,observer:M,...b})}if(O.current=T,!p.current)return;f(T)}catch(b){console.error(b)}T&&n&&M.disconnect()},{root:o||void 0,rootMargin:a,threshold:P})}catch(E){console.error(E)}},[o,a,i,n,y,l,c,u]);return _react.useEffect.call(void 0, ()=>{if(p.current=!0,!(!y||!e.current||!v))return v.observe(e.current),()=>{p.current=!1,v.disconnect()}},[e,v,y]),{inView:d,enabled:y,observer:v,isExiting:x.current,setInView:f,setEnabled:N}};var _dom = require('@alessiofrittoli/web-utils/dom');var gt=e=>{let r=_react.useCallback.call(void 0, ()=>_dom.blockScroll.call(void 0, _optionalChain([e, 'optionalAccess', _12 => _12.current])||void 0),[e]),t=_react.useCallback.call(void 0, ()=>_dom.restoreScroll.call(void 0, _optionalChain([e, 'optionalAccess', _13 => _13.current])||void 0),[e]);return[r,t]};var j=(e,r={})=>{let{delay:t=1,args:n}=r;_react.useEffect.call(void 0, ()=>{let i=setTimeout(e,t,...n||[]);return()=>clearTimeout(i)},[t,n,e])};var ht=(e,r=500)=>{let[t,n]=_react.useState.call(void 0, e),i=_react.useMemo.call(void 0, ()=>[e],[e]);return j(n,{delay:r,args:i}),t};function B(e,r={}){let{delay:t=1,args:n,autoplay:i=!0,runOnStart:a=!1,updateState:o=!1}=r,s=_react.useRef.call(void 0, void 0),[l,c]=_react.useState.call(void 0, i),u=_react.useCallback.call(void 0, ()=>s.current?(clearInterval(s.current),s.current=void 0,!0):!1,[]),p=_react.useCallback.call(void 0, ()=>{let f=u();return a&&(n?e(...n):e()),s.current=setInterval(e,t,...n||[]),!f&&o&&c(!0),s.current},[t,n,o,a,e,u]),d=_react.useCallback.call(void 0, ()=>{u()&&o&&c(!1)},[o,u]);return _react.useEffect.call(void 0, ()=>{if(i)return p(),d},[i,p,d]),o?{isActive:l,start:p,stop:d}:{start:p,stop:d}}var Wt=(e,r={})=>{let{delay:t=1,args:n}=r;_react.useEffect.call(void 0, ()=>{let i=setInterval(e,t,...n||[]);return()=>clearInterval(i)},[t,n,e])};function $t(e,r={}){let{autoplay:t=!0}=r,n=B(e,{autoplay:!1,...r}),{start:i,stop:a}=n,o=_react.useCallback.call(void 0, ()=>document.hidden?a():i(),[i,a]),s=_react.useCallback.call(void 0, ()=>{if(document.addEventListener("visibilitychange",o),!document.hidden)return i()},[i,o]),l=_react.useCallback.call(void 0, ()=>{a(),document.removeEventListener("visibilitychange",o)},[a,o]);return _react.useEffect.call(void 0, ()=>{if(t)return s(),l},[t,s,l]),{...n,start:s,stop:l}}function Pt(e,r={}){let{delay:t=1,args:n,autoplay:i=!0,runOnStart:a=!1,updateState:o=!1}=r,s=_react.useRef.call(void 0, void 0),[l,c]=_react.useState.call(void 0, i),u=_react.useCallback.call(void 0, ()=>s.current?(clearTimeout(s.current),s.current=void 0,!0):!1,[]),p=_react.useCallback.call(void 0, ()=>{let f=u();return a&&(n?e(...n):e()),s.current=setTimeout(()=>{if(s.current=void 0,o&&c(!1),n)return e(...n);e()},t),!f&&o&&c(!0),s.current},[t,n,o,a,e,u]),d=_react.useCallback.call(void 0, ()=>{u()&&o&&c(!1)},[o,u]);return _react.useEffect.call(void 0, ()=>{if(i)return p(),d},[i,p,d]),o?{isActive:l,start:p,stop:d}:{start:p,stop:d}}exports.useDarkMode = _e; exports.useDebounce = ht; exports.useEventListener = rt; exports.useFocusTrap = mt; exports.useInView = Et; exports.useInterval = B; exports.useIntervalWhenVisible = $t; exports.useIsClient = K; exports.useIsFirstRender = W; exports.useIsPortrait = ut; exports.useLightInterval = Wt; exports.useLightTimeout = j; exports.useLocalStorage = V; exports.useMediaQuery = S; exports.useScrollBlock = gt; exports.useSessionStorage = we; exports.useStorage = g; exports.useTimeout = Pt; exports.useUpdateEffect = L;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{useCallback as R,useEffect as j,useState as B}from"react";import{LocalStorage as h}from"@alessiofrittoli/web-utils/storage/LocalStorage";import{SessionStorage as D}from"@alessiofrittoli/web-utils/storage/SessionStorage";var E=(e,r,t="local")=>{let n=R(()=>(t==="local"?h:D).get(e)??r,[t,e,r]),[i,a]=B(r),o=R(s=>{a(l=>{let u=s instanceof Function?s(l):s;return(typeof window<"u"&&t==="local"?h:D).set(e,u),u})},[t,e]);return j(()=>{a(n())},[n]),[i,o]};var w=(e,r)=>E(e,r,"local");var De=(e,r)=>E(e,r,"session");import{useCallback as S,useEffect as V,useRef as Y}from"react";import{useEffect as N,useState as q}from"react";var I=()=>{let[e,r]=q(!1);return N(()=>r(!0),[]),e};import{useRef as P}from"react";var H=()=>{let e=P(!0);return e.current?(e.current=!1,!0):e.current};import{useEffect as z}from"react";var x=(e,r)=>{let t=H();z(()=>{if(!t)return e()},r)};import{useCallback as G,useEffect as J,useState as X}from"react";import{getMediaMatches as C}from"@alessiofrittoli/web-utils/browser-api";var M=e=>{let[r,t]=X(C(e)),n=G(()=>t(C(e)),[e]);return J(()=>{let i=window.matchMedia(e);return n(),i.addEventListener("change",n),()=>{i.removeEventListener("change",n)}},[e,n]),r};var Xe=(e={})=>{let r=I(),t=M("(prefers-color-scheme: dark)"),{initial:n=t,docClassNames:i=[]}=e,[a,o]=w("dark-mode",n),s=a??t,[l,u]=i,c=Y({light:"",dark:""});return x(()=>{o(t)},[t,o]),V(()=>{l&&document.documentElement.classList.toggle(l,s),u&&document.documentElement.classList.toggle(u,!s)},[s,l,u]),V(()=>{document.head.querySelectorAll('meta[name="theme-color"]').forEach(p=>{let m=p.getAttribute("media"),T=p.getAttribute("content");if(T){if(!m||m==="(prefers-color-scheme: light)"){c.current.light=T;return}c.current.dark=T}})},[]),x(()=>{let p=c.current.dark,m=c.current.light;a&&!p||!a&&!m||document.head.querySelectorAll('meta[name="theme-color"]').forEach(T=>{T.setAttribute("content",a?p:m)})},[a]),{isDarkMode:r?s:!1,isDarkOS:r?t:!1,toggleDarkMode:S(()=>o(p=>!p),[o]),enableDarkMode:S(()=>o(!0),[o]),disableDarkMode:S(()=>o(!1),[o])}};import{useEffect as Z}from"react";function et(e,r){let{target:t,query:n,options:i,listener:a,onLoad:o,onCleanUp:s}=r;Z(()=>{let l=Array.isArray(e)?e:[e],u=(n?window.matchMedia(n):t&&"current"in t?t.current:t)??window;if(u.addEventListener)return o?.(),l.map(c=>{u.addEventListener(c,a,i)}),()=>{l.map(c=>{u.removeEventListener(c,a,i)}),s?.()}},[e,t,n,i,a,o,s])}import{portraitMediaQuery as _}from"@alessiofrittoli/web-utils/device";var st=()=>M(_);import{useCallback as K,useEffect as ee,useRef as te,useState as ne}from"react";var re=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),ct=e=>{let[r,t]=ne(!1),n=te(null),i=K(o=>{n.current=document.activeElement;let s=o||e?.current||!1;if(s)return t(s)},[e]),a=K(()=>{n.current?.focus(),t(!1)},[]);return ee(()=>{if(!r)return;let o=s=>{if(s.key!=="Tab")return;let l=Array.from(r.querySelectorAll(re)),u=l.at(0),c=l.at(-1);if(!s.shiftKey){document.activeElement===c&&(s.preventDefault(),u?.focus());return}document.activeElement===u&&(s.preventDefault(),c?.focus())};return document.addEventListener("keydown",o),()=>{document.removeEventListener("keydown",o)}},[r]),[i,a]};import{useEffect as oe,useMemo as se,useRef as ie,useState as W}from"react";var dt=(e,r={})=>{let{initial:t=!1,once:n,amount:i,margin:a,root:o,enable:s=!0,onStart:l}=r,u=ie(!0),[c,p]=W(t),[m,T]=W(s),y=se(()=>{if(!m||typeof IntersectionObserver>"u")return;let Q=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([v],k)=>{if(!v)return;let O=v.isIntersecting;try{if(await l?.(v,k),!u.current)return;p(O)}catch($){console.error($)}O&&n&&k.disconnect()},{root:o||void 0,rootMargin:a,threshold:Q})}catch(v){console.error(v)}},[o,a,i,n,m,l]);return oe(()=>{if(u.current=!0,!(!m||!e.current||!y))return y.observe(e.current),()=>{u.current=!1,y.disconnect()}},[e,y,m]),{inView:c,enabled:m,observer:y,setInView:p,setEnabled:T}};import{useCallback as A}from"react";import{blockScroll as ae,restoreScroll as ue}from"@alessiofrittoli/web-utils/dom";var Et=e=>{let r=A(()=>ae(e?.current||void 0),[e]),t=A(()=>ue(e?.current||void 0),[e]);return[r,t]};import{useMemo as le,useState as me}from"react";import{useEffect as ce}from"react";var U=(e,r={})=>{let{delay:t=1,args:n}=r;ce(()=>{let i=setTimeout(e,t,...n||[]);return()=>clearTimeout(i)},[t,n,e])};var Ot=(e,r=500)=>{let[t,n]=me(e),i=le(()=>[e],[e]);return U(n,{delay:r,args:i}),t};import{useCallback as b,useEffect as pe,useRef as de,useState as fe}from"react";function F(e,r={}){let{delay:t=1,args:n,autoplay:i=!0,runOnStart:a=!1,updateState:o=!1}=r,s=de(void 0),[l,u]=fe(i),c=b(()=>s.current?(clearInterval(s.current),s.current=void 0,!0):!1,[]),p=b(()=>{let T=c();return a&&(n?e(...n):e()),s.current=setInterval(e,t,...n||[]),!T&&o&&u(!0),s.current},[t,n,o,a,e,c]),m=b(()=>{c()&&o&&u(!1)},[o,c]);return pe(()=>{if(i)return p(),m},[i,p,m]),o?{isActive:l,start:p,stop:m}:{start:p,stop:m}}import{useEffect as Te}from"react";var Ct=(e,r={})=>{let{delay:t=1,args:n}=r;Te(()=>{let i=setInterval(e,t,...n||[]);return()=>clearInterval(i)},[t,n,e])};import{useCallback as L,useEffect as ye}from"react";function Ut(e,r={}){let{autoplay:t=!0}=r,n=F(e,{autoplay:!1,...r}),{start:i,stop:a}=n,o=L(()=>document.hidden?a():i(),[i,a]),s=L(()=>{if(document.addEventListener("visibilitychange",o),!document.hidden)return i()},[i,o]),l=L(()=>{a(),document.removeEventListener("visibilitychange",o)},[a,o]);return ye(()=>{if(t)return s(),l},[t,s,l]),{...n,start:s,stop:l}}import{useCallback as g,useEffect as ve,useRef as Ee,useState as Me}from"react";function jt(e,r={}){let{delay:t=1,args:n,autoplay:i=!0,runOnStart:a=!1,updateState:o=!1}=r,s=Ee(void 0),[l,u]=Me(i),c=g(()=>s.current?(clearTimeout(s.current),s.current=void 0,!0):!1,[]),p=g(()=>{let T=c();return a&&(n?e(...n):e()),s.current=setTimeout(()=>{if(s.current=void 0,o&&u(!1),n)return e(...n);e()},t),!T&&o&&u(!0),s.current},[t,n,o,a,e,c]),m=g(()=>{c()&&o&&u(!1)},[o,c]);return ve(()=>{if(i)return p(),m},[i,p,m]),o?{isActive:l,start:p,stop:m}:{start:p,stop:m}}export{Xe as useDarkMode,Ot as useDebounce,et as useEventListener,ct as useFocusTrap,dt as useInView,F as useInterval,Ut as useIntervalWhenVisible,I as useIsClient,H as useIsFirstRender,st as useIsPortrait,Ct as useLightInterval,U as useLightTimeout,w as useLocalStorage,M as useMediaQuery,Et as useScrollBlock,De as useSessionStorage,E as useStorage,jt as useTimeout,x as useUpdateEffect};
1
+ import{useCallback as w,useEffect as z,useState as G}from"react";import{LocalStorage as C}from"@alessiofrittoli/web-utils/storage/LocalStorage";import{SessionStorage as V}from"@alessiofrittoli/web-utils/storage/SessionStorage";var S=(e,r,t="local")=>{let n=w(()=>(t==="local"?C:V).get(e)??r,[t,e,r]),[i,a]=G(r),o=w(s=>{a(l=>{let c=s instanceof Function?s(l):s;return(typeof window<"u"&&t==="local"?C:V).set(e,c),c})},[t,e]);return z(()=>{a(n())},[n]),[i,o]};var K=(e,r)=>S(e,r,"local");var Ce=(e,r)=>S(e,r,"session");import{useCallback as R,useEffect as F,useRef as ne}from"react";import{useEffect as J,useState as X}from"react";var W=()=>{let[e,r]=X(!1);return J(()=>r(!0),[]),e};import{useRef as Y}from"react";var A=()=>{let e=Y(!0);return e.current?(e.current=!1,!0):e.current};import{useEffect as Z}from"react";var k=(e,r)=>{let t=A();Z(()=>{if(!t)return e()},r)};import{useCallback as _,useEffect as ee,useState as te}from"react";import{getMediaMatches as U}from"@alessiofrittoli/web-utils/browser-api";var O=e=>{let[r,t]=te(U(e)),n=_(()=>t(U(e)),[e]);return ee(()=>{let i=window.matchMedia(e);return n(),i.addEventListener("change",n),()=>{i.removeEventListener("change",n)}},[e,n]),r};var et=(e={})=>{let r=W(),t=O("(prefers-color-scheme: dark)"),{initial:n=t,docClassNames:i=[]}=e,[a,o]=K("dark-mode",n),s=a??t,[l,c]=i,u=ne({light:"",dark:""});return k(()=>{o(t)},[t,o]),F(()=>{l&&document.documentElement.classList.toggle(l,s),c&&document.documentElement.classList.toggle(c,!s)},[s,l,c]),F(()=>{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)"){u.current.light=T;return}u.current.dark=T}})},[]),k(()=>{let p=u.current.dark,f=u.current.light;a&&!p||!a&&!f||document.head.querySelectorAll('meta[name="theme-color"]').forEach(T=>{T.setAttribute("content",a?p:f)})},[a]),{isDarkMode:r?s:!1,isDarkOS:r?t:!1,toggleDarkMode:R(()=>o(p=>!p),[o]),enableDarkMode:R(()=>o(!0),[o]),disableDarkMode:R(()=>o(!1),[o])}};import{useEffect as re}from"react";function ot(e,r){let{target:t,query:n,options:i,listener:a,onLoad:o,onCleanUp:s}=r;re(()=>{let l=Array.isArray(e)?e:[e],c=(n?window.matchMedia(n):t&&"current"in t?t.current:t)??window;if(c.addEventListener)return o?.(),l.map(u=>{c.addEventListener(u,a,i)}),()=>{l.map(u=>{c.removeEventListener(u,a,i)}),s?.()}},[e,t,n,i,a,o,s])}import{portraitMediaQuery as oe}from"@alessiofrittoli/web-utils/device";var ct=()=>O(oe);import{useCallback as Q,useEffect as se,useRef as ie,useState as ae}from"react";var ue=["input","select","textarea","button","[href]",'[tabindex]:not([tabindex="-1"])'].join(", "),dt=e=>{let[r,t]=ae(!1),n=ie(null),i=Q(o=>{n.current=document.activeElement;let s=o||e?.current||!1;if(s)return t(s)},[e]),a=Q(()=>{n.current?.focus(),t(!1)},[]);return se(()=>{if(!r)return;let o=s=>{if(s.key!=="Tab")return;let l=Array.from(r.querySelectorAll(ue)),c=l.at(0),u=l.at(-1);if(!s.shiftKey){document.activeElement===u&&(s.preventDefault(),c?.focus());return}document.activeElement===c&&(s.preventDefault(),u?.focus())};return document.addEventListener("keydown",o),()=>{document.removeEventListener("keydown",o)}},[r]),[i,a]};import{useEffect as ce,useMemo as le,useRef as I,useState as $}from"react";var yt=(e,r={})=>{let{initial:t=!1,once:n,amount:i,margin:a,root:o,enable:s=!0}=r,{onEnter:l,onExit:c,onIntersect:u}=r,p=I(!0),[f,T]=$(t),[v,P]=$(s),b=I(null),M=I(!1),x=le(()=>{if(!v||typeof IntersectionObserver>"u")return;let q=i==="all"?1:i==="some"?.5:i;try{return new IntersectionObserver(async([y],g)=>{if(!y)return;let E=y.isIntersecting;try{if(M.current=!E&&!!b.current,E&&l&&await l({entry:y,observer:g}),M.current&&c&&await c({entry:y,observer:g}),u&&(E||!E&&b.current!=null)){let L={isEntering:E,isExiting:M.current};await u({entry:y,observer:g,...L})}if(b.current=E,!p.current)return;T(E)}catch(L){console.error(L)}E&&n&&g.disconnect()},{root:o||void 0,rootMargin:a,threshold:q})}catch(y){console.error(y)}},[o,a,i,n,v,l,c,u]);return ce(()=>{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:P}};import{useCallback as j}from"react";import{blockScroll as pe,restoreScroll as me}from"@alessiofrittoli/web-utils/dom";var St=e=>{let r=j(()=>pe(e?.current||void 0),[e]),t=j(()=>me(e?.current||void 0),[e]);return[r,t]};import{useMemo as fe,useState as Te}from"react";import{useEffect as de}from"react";var B=(e,r={})=>{let{delay:t=1,args:n}=r;de(()=>{let i=setTimeout(e,t,...n||[]);return()=>clearTimeout(i)},[t,n,e])};var Ht=(e,r=500)=>{let[t,n]=Te(e),i=fe(()=>[e],[e]);return B(n,{delay:r,args:i}),t};import{useCallback as h,useEffect as Ee,useRef as ye,useState as ve}from"react";function N(e,r={}){let{delay:t=1,args:n,autoplay:i=!0,runOnStart:a=!1,updateState:o=!1}=r,s=ye(void 0),[l,c]=ve(i),u=h(()=>s.current?(clearInterval(s.current),s.current=void 0,!0):!1,[]),p=h(()=>{let T=u();return a&&(n?e(...n):e()),s.current=setInterval(e,t,...n||[]),!T&&o&&c(!0),s.current},[t,n,o,a,e,u]),f=h(()=>{u()&&o&&c(!1)},[o,u]);return Ee(()=>{if(i)return p(),f},[i,p,f]),o?{isActive:l,start:p,stop:f}:{start:p,stop:f}}import{useEffect as xe}from"react";var At=(e,r={})=>{let{delay:t=1,args:n}=r;xe(()=>{let i=setInterval(e,t,...n||[]);return()=>clearInterval(i)},[t,n,e])};import{useCallback as H,useEffect as Me}from"react";function jt(e,r={}){let{autoplay:t=!0}=r,n=N(e,{autoplay:!1,...r}),{start:i,stop:a}=n,o=H(()=>document.hidden?a():i(),[i,a]),s=H(()=>{if(document.addEventListener("visibilitychange",o),!document.hidden)return i()},[i,o]),l=H(()=>{a(),document.removeEventListener("visibilitychange",o)},[a,o]);return Me(()=>{if(t)return s(),l},[t,s,l]),{...n,start:s,stop:l}}import{useCallback as D,useEffect as ge,useRef as Se,useState as Oe}from"react";function qt(e,r={}){let{delay:t=1,args:n,autoplay:i=!0,runOnStart:a=!1,updateState:o=!1}=r,s=Se(void 0),[l,c]=Oe(i),u=D(()=>s.current?(clearTimeout(s.current),s.current=void 0,!0):!1,[]),p=D(()=>{let T=u();return a&&(n?e(...n):e()),s.current=setTimeout(()=>{if(s.current=void 0,o&&c(!1),n)return e(...n);e()},t),!T&&o&&c(!0),s.current},[t,n,o,a,e,u]),f=D(()=>{u()&&o&&c(!1)},[o,u]);return ge(()=>{if(i)return p(),f},[i,p,f]),o?{isActive:l,start:p,stop:f}:{start:p,stop:f}}export{et as useDarkMode,Ht as useDebounce,ot as useEventListener,dt as useFocusTrap,yt as useInView,N as useInterval,jt as useIntervalWhenVisible,W as useIsClient,A as useIsFirstRender,ct as useIsPortrait,At as useLightInterval,B as useLightTimeout,K as useLocalStorage,O as useMediaQuery,St as useScrollBlock,Ce as useSessionStorage,S as useStorage,qt as useTimeout,k as useUpdateEffect};
package/package.json CHANGED
@@ -1,133 +1,139 @@
1
1
  {
2
- "name": "@alessiofrittoli/react-hooks",
3
- "version": "2.0.0",
4
- "description": "TypeScript React utility Hooks",
5
- "author": {
6
- "name": "Alessio Frittoli",
7
- "email": "info@alessiofrittoli.it",
8
- "url": "https://alessiofrittoli.it"
9
- },
10
- "license": "MIT",
11
- "funding": [
12
- {
13
- "type": "github",
14
- "url": "https://github.com/sponsors/alessiofrittoli"
15
- }
16
- ],
17
- "keywords": [
18
- "react",
19
- "react-hooks"
20
- ],
21
- "homepage": "https://github.com/alessiofrittoli/react-hooks#readme",
22
- "bugs": {
23
- "url": "https://github.com/alessiofrittoli/react-hooks/issues",
24
- "email": "info@alessiofrittoli.it"
25
- },
26
- "repository": {
27
- "type": "git",
28
- "url": "git+https://github.com/alessiofrittoli/react-hooks.git"
29
- },
30
- "main": "./dist/index.js",
31
- "module": "./dist/index.mjs",
32
- "types": "./dist/index.d.ts",
33
- "files": [
34
- "dist"
35
- ],
36
- "exports": {
37
- ".": {
38
- "import": {
39
- "types": "./dist/index.d.mts",
40
- "default": "./dist/index.mjs"
41
- },
42
- "require": {
43
- "types": "./dist/index.d.ts",
44
- "default": "./dist/index.js"
45
- }
46
- },
47
- "./eslint": {
48
- "import": {
49
- "types": "./dist/eslint.d.mts",
50
- "default": "./dist/eslint.mjs"
51
- },
52
- "require": {
53
- "types": "./dist/eslint.d.ts",
54
- "default": "./dist/eslint.js"
55
- }
56
- }
57
- },
58
- "sideEffects": false,
59
- "devDependencies": {
60
- "@alessiofrittoli/event-emitter": "^1.4.0",
61
- "@alessiofrittoli/node-scripts": "^2.5.0",
62
- "@eslint/compat": "^1.2.9",
63
- "@eslint/eslintrc": "^3.3.1",
64
- "@eslint/js": "^9.26.0",
65
- "@jest/globals": "^29.7.0",
66
- "@testing-library/dom": "^10.4.0",
67
- "@testing-library/jest-dom": "^6.6.3",
68
- "@testing-library/react": "^16.3.0",
69
- "@testing-library/user-event": "^14.6.1",
70
- "@types/jest": "^29.5.14",
71
- "@types/node": "^22.15.18",
72
- "@types/react": "^19.1.4",
73
- "@types/react-dom": "^19.1.5",
74
- "concurrently": "^9.1.2",
75
- "dotenv": "^16.5.0",
76
- "eslint": "^9.26.0",
77
- "eslint-plugin-react": "^7.37.5",
78
- "eslint-plugin-react-hooks": "^5.2.0",
79
- "globals": "^16.1.0",
80
- "http-server": "^14.1.1",
81
- "jest": "^29.7.0",
82
- "jest-environment-jsdom": "^29.7.0",
83
- "react": "^19.1.0",
84
- "react-dom": "^19.1.0",
85
- "ts-jest": "^29.3.3",
86
- "ts-node": "^10.9.2",
87
- "tsup": "^8.4.0",
88
- "typescript": "^5.8.3",
89
- "typescript-eslint": "^8.32.1"
90
- },
91
- "dependencies": {
92
- "@alessiofrittoli/math-utils": "^1.13.0",
93
- "@alessiofrittoli/type-utils": "^1.8.0",
94
- "@alessiofrittoli/web-utils": "^1.10.0"
95
- },
96
- "peerDependencies": {
97
- "@types/react": "^19",
98
- "@types/react-dom": "^19",
99
- "react": "^19",
100
- "react-dom": "^19"
101
- },
102
- "scripts": {
103
- "//1a": "*********************************************************************",
104
- "//1b": "******************** DEV - BUILD - LINT - RELEASE *******************",
105
- "//1c": "*********************************************************************",
106
- "dev": "NODE_ENV=development tsup --watch",
107
- "build:prod": "NODE_ENV=production tsup",
108
- "build": "pnpm lint && pnpm test:ci && pnpm build:prod",
109
- "lint": "eslint",
110
- "release": "node scripts/publish.js --verbose --npm",
111
- "//2a": "*********************************************************************",
112
- "//2b": "***************************** UNIT TESTS ****************************",
113
- "//2c": "*********************************************************************",
114
- "test": "jest --verbose",
115
- "test:watch": "jest --watchAll --verbose",
116
- "test:ci": "jest --ci --verbose",
117
- "//3a": "*********************************************************************",
118
- "//3b": "************************ UNIT TESTS COVERAGE ************************",
119
- "//3c": "*********************************************************************",
120
- "test:coverage": "pnpm test:watch --coverage",
121
- "test:coverage:ci": "pnpm test:ci --coverage",
122
- "test:serve-coverage": "http-server ./coverage/lcov-report --gzip true -p 0 -o --silent",
123
- "test:coverage:serve": "concurrently --prefix none --kill-others \"pnpm test:coverage\" \"pnpm test:serve-coverage\"",
124
- "//4a": "*********************************************************************",
125
- "//4b": "************************ TARGETED UNIT TESTS ************************",
126
- "//4c": "*********************************************************************",
127
- "test:storage": "pnpm test:watch browser-api/storage/*",
128
- "test:browser-api": "pnpm test:watch --testPathPattern='browser-api/(?!storage/).*\\.test\\.(js|jsx|ts|tsx)'",
129
- "test:dom-api": "pnpm test:watch dom-api/*",
130
- "test:misc": "pnpm test:watch misc/*",
131
- "test:timers": "pnpm test:watch timers/*"
132
- }
133
- }
2
+ "name": "@alessiofrittoli/react-hooks",
3
+ "version": "3.0.0",
4
+ "description": "TypeScript React utility Hooks",
5
+ "author": {
6
+ "name": "Alessio Frittoli",
7
+ "email": "info@alessiofrittoli.it",
8
+ "url": "https://alessiofrittoli.it"
9
+ },
10
+ "license": "MIT",
11
+ "funding": [
12
+ {
13
+ "type": "github",
14
+ "url": "https://github.com/sponsors/alessiofrittoli"
15
+ }
16
+ ],
17
+ "keywords": [
18
+ "react",
19
+ "react-hooks"
20
+ ],
21
+ "homepage": "https://github.com/alessiofrittoli/react-hooks#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/alessiofrittoli/react-hooks/issues",
24
+ "email": "info@alessiofrittoli.it"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/alessiofrittoli/react-hooks.git"
29
+ },
30
+ "main": "./dist/index.js",
31
+ "module": "./dist/index.mjs",
32
+ "types": "./dist/index.d.ts",
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "exports": {
37
+ ".": {
38
+ "import": {
39
+ "types": "./dist/index.d.mts",
40
+ "default": "./dist/index.mjs"
41
+ },
42
+ "require": {
43
+ "types": "./dist/index.d.ts",
44
+ "default": "./dist/index.js"
45
+ }
46
+ },
47
+ "./eslint": {
48
+ "import": {
49
+ "types": "./dist/eslint.d.mts",
50
+ "default": "./dist/eslint.mjs"
51
+ },
52
+ "require": {
53
+ "types": "./dist/eslint.d.ts",
54
+ "default": "./dist/eslint.js"
55
+ }
56
+ }
57
+ },
58
+ "sideEffects": false,
59
+ "scripts": {
60
+ "//1a": "*********************************************************************",
61
+ "//1b": "******************** DEV - BUILD - LINT - RELEASE *******************",
62
+ "//1c": "*********************************************************************",
63
+ "dev": "NODE_ENV=development tsup --watch",
64
+ "build:prod": "NODE_ENV=production tsup",
65
+ "build": "pnpm lint && pnpm test:ci && pnpm build:prod",
66
+ "lint": "eslint",
67
+ "release": "node scripts/publish.js --verbose --npm",
68
+ "//2a": "*********************************************************************",
69
+ "//2b": "***************************** UNIT TESTS ****************************",
70
+ "//2c": "*********************************************************************",
71
+ "test": "jest --verbose",
72
+ "test:watch": "jest --watchAll --verbose",
73
+ "test:ci": "jest --ci --verbose",
74
+ "//3a": "*********************************************************************",
75
+ "//3b": "************************ UNIT TESTS COVERAGE ************************",
76
+ "//3c": "*********************************************************************",
77
+ "test:coverage": "pnpm test:watch --coverage",
78
+ "test:coverage:ci": "pnpm test:ci --coverage",
79
+ "test:serve-coverage": "http-server ./coverage/lcov-report --gzip true -p 0 -o --silent",
80
+ "test:coverage:serve": "concurrently --prefix none --kill-others \"pnpm test:coverage\" \"pnpm test:serve-coverage\"",
81
+ "//4a": "*********************************************************************",
82
+ "//4b": "************************ TARGETED UNIT TESTS ************************",
83
+ "//4c": "*********************************************************************",
84
+ "test:storage": "pnpm test:watch browser-api/storage/*",
85
+ "test:browser-api": "pnpm test:watch --testPathPattern='browser-api/(?!storage/).*\\.test\\.(js|jsx|ts|tsx)'",
86
+ "test:dom-api": "pnpm test:watch dom-api/*",
87
+ "test:misc": "pnpm test:watch misc/*",
88
+ "test:timers": "pnpm test:watch timers/*"
89
+ },
90
+ "pnpm": {
91
+ "onlyBuiltDependencies": [
92
+ "@alessiofrittoli/type-utils",
93
+ "esbuild"
94
+ ]
95
+ },
96
+ "devDependencies": {
97
+ "@alessiofrittoli/event-emitter": "^1.4.0",
98
+ "@alessiofrittoli/node-scripts": "^2.5.0",
99
+ "@eslint/compat": "^1.2.9",
100
+ "@eslint/eslintrc": "^3.3.1",
101
+ "@eslint/js": "^9.26.0",
102
+ "@jest/globals": "^29.7.0",
103
+ "@testing-library/dom": "^10.4.0",
104
+ "@testing-library/jest-dom": "^6.6.3",
105
+ "@testing-library/react": "^16.3.0",
106
+ "@testing-library/user-event": "^14.6.1",
107
+ "@types/jest": "^29.5.14",
108
+ "@types/node": "^22.15.18",
109
+ "@types/react": "^19.1.4",
110
+ "@types/react-dom": "^19.1.5",
111
+ "concurrently": "^9.1.2",
112
+ "dotenv": "^16.5.0",
113
+ "eslint": "^9.26.0",
114
+ "eslint-plugin-react": "^7.37.5",
115
+ "eslint-plugin-react-hooks": "^5.2.0",
116
+ "globals": "^16.1.0",
117
+ "http-server": "^14.1.1",
118
+ "jest": "^29.7.0",
119
+ "jest-environment-jsdom": "^29.7.0",
120
+ "react": "^19.1.0",
121
+ "react-dom": "^19.1.0",
122
+ "ts-jest": "^29.3.3",
123
+ "ts-node": "^10.9.2",
124
+ "tsup": "^8.4.0",
125
+ "typescript": "^5.8.3",
126
+ "typescript-eslint": "^8.32.1"
127
+ },
128
+ "dependencies": {
129
+ "@alessiofrittoli/math-utils": "^1.13.0",
130
+ "@alessiofrittoli/type-utils": "^1.8.0",
131
+ "@alessiofrittoli/web-utils": "^1.10.0"
132
+ },
133
+ "peerDependencies": {
134
+ "@types/react": "^19",
135
+ "@types/react-dom": "^19",
136
+ "react": "^19",
137
+ "react-dom": "^19"
138
+ }
139
+ }