@akbeniwal/react-native-hooks 1.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.
Files changed (70) hide show
  1. package/README.md +235 -0
  2. package/dist/hooks/useAfterInteractions.d.ts +10 -0
  3. package/dist/hooks/useAfterInteractions.d.ts.map +1 -0
  4. package/dist/hooks/useAfterInteractions.js +28 -0
  5. package/dist/hooks/useAfterInteractions.js.map +1 -0
  6. package/dist/hooks/useAppState.d.ts +17 -0
  7. package/dist/hooks/useAppState.d.ts.map +1 -0
  8. package/dist/hooks/useAppState.js +36 -0
  9. package/dist/hooks/useAppState.js.map +1 -0
  10. package/dist/hooks/useBackHandler.d.ts +7 -0
  11. package/dist/hooks/useBackHandler.d.ts.map +1 -0
  12. package/dist/hooks/useBackHandler.js +21 -0
  13. package/dist/hooks/useBackHandler.js.map +1 -0
  14. package/dist/hooks/useClipboard.d.ts +19 -0
  15. package/dist/hooks/useClipboard.d.ts.map +1 -0
  16. package/dist/hooks/useClipboard.js +44 -0
  17. package/dist/hooks/useClipboard.js.map +1 -0
  18. package/dist/hooks/useCountdown.d.ts +24 -0
  19. package/dist/hooks/useCountdown.d.ts.map +1 -0
  20. package/dist/hooks/useCountdown.js +67 -0
  21. package/dist/hooks/useCountdown.js.map +1 -0
  22. package/dist/hooks/useDebounce.d.ts +12 -0
  23. package/dist/hooks/useDebounce.d.ts.map +1 -0
  24. package/dist/hooks/useDebounce.js +29 -0
  25. package/dist/hooks/useDebounce.js.map +1 -0
  26. package/dist/hooks/useDeviceOrientation.d.ts +10 -0
  27. package/dist/hooks/useDeviceOrientation.d.ts.map +1 -0
  28. package/dist/hooks/useDeviceOrientation.js +33 -0
  29. package/dist/hooks/useDeviceOrientation.js.map +1 -0
  30. package/dist/hooks/useDoubleTapToExit.d.ts +17 -0
  31. package/dist/hooks/useDoubleTapToExit.d.ts.map +1 -0
  32. package/dist/hooks/useDoubleTapToExit.js +40 -0
  33. package/dist/hooks/useDoubleTapToExit.js.map +1 -0
  34. package/dist/hooks/useInterval.d.ts +10 -0
  35. package/dist/hooks/useInterval.d.ts.map +1 -0
  36. package/dist/hooks/useInterval.js +27 -0
  37. package/dist/hooks/useInterval.js.map +1 -0
  38. package/dist/hooks/useKeyboard.d.ts +23 -0
  39. package/dist/hooks/useKeyboard.d.ts.map +1 -0
  40. package/dist/hooks/useKeyboard.js +46 -0
  41. package/dist/hooks/useKeyboard.js.map +1 -0
  42. package/dist/hooks/useLayout.d.ts +19 -0
  43. package/dist/hooks/useLayout.d.ts.map +1 -0
  44. package/dist/hooks/useLayout.js +29 -0
  45. package/dist/hooks/useLayout.js.map +1 -0
  46. package/dist/hooks/useNetworkStatus.d.ts +45 -0
  47. package/dist/hooks/useNetworkStatus.d.ts.map +1 -0
  48. package/dist/hooks/useNetworkStatus.js +184 -0
  49. package/dist/hooks/useNetworkStatus.js.map +1 -0
  50. package/dist/hooks/usePrevious.d.ts +9 -0
  51. package/dist/hooks/usePrevious.d.ts.map +1 -0
  52. package/dist/hooks/usePrevious.js +16 -0
  53. package/dist/hooks/usePrevious.js.map +1 -0
  54. package/dist/hooks/useThrottle.d.ts +12 -0
  55. package/dist/hooks/useThrottle.d.ts.map +1 -0
  56. package/dist/hooks/useThrottle.js +47 -0
  57. package/dist/hooks/useThrottle.js.map +1 -0
  58. package/dist/hooks/useTimeout.d.ts +10 -0
  59. package/dist/hooks/useTimeout.d.ts.map +1 -0
  60. package/dist/hooks/useTimeout.js +27 -0
  61. package/dist/hooks/useTimeout.js.map +1 -0
  62. package/dist/hooks/useToggle.d.ts +19 -0
  63. package/dist/hooks/useToggle.d.ts.map +1 -0
  64. package/dist/hooks/useToggle.js +38 -0
  65. package/dist/hooks/useToggle.js.map +1 -0
  66. package/dist/index.d.ts +17 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +17 -0
  69. package/dist/index.js.map +1 -0
  70. package/package.json +52 -0
@@ -0,0 +1,33 @@
1
+ import { useEffect, useState, useRef } from 'react';
2
+ import { Dimensions } from 'react-native';
3
+ /**
4
+ * A custom React Native hook that monitors and returns the device orientation dynamically.
5
+ * Updates on device rotation or dimension changes, and optionally fires an onChange callback.
6
+ *
7
+ * @param onChange Optional callback triggered when the orientation changes, receiving the new orientation
8
+ * @returns The current orientation status ('PORTRAIT' | 'LANDSCAPE')
9
+ */
10
+ export function useDeviceOrientation(onChange) {
11
+ const checkLandscape = () => {
12
+ const { width, height } = Dimensions.get('window');
13
+ return width > height;
14
+ };
15
+ const [orientation, setOrientation] = useState(checkLandscape() ? 'LANDSCAPE' : 'PORTRAIT');
16
+ const onChangeRef = useRef(onChange);
17
+ onChangeRef.current = onChange;
18
+ useEffect(() => {
19
+ const handleDimensionsChange = () => {
20
+ const nextOrientation = checkLandscape() ? 'LANDSCAPE' : 'PORTRAIT';
21
+ setOrientation(nextOrientation);
22
+ if (onChangeRef.current) {
23
+ onChangeRef.current(nextOrientation);
24
+ }
25
+ };
26
+ const subscription = Dimensions.addEventListener('change', handleDimensionsChange);
27
+ return () => {
28
+ subscription.remove();
29
+ };
30
+ }, []);
31
+ return orientation;
32
+ }
33
+ //# sourceMappingURL=useDeviceOrientation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDeviceOrientation.js","sourceRoot":"","sources":["../../src/hooks/useDeviceOrientation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI1C;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAA6C;IAE7C,MAAM,cAAc,GAAG,GAAY,EAAE;QACnC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnD,OAAO,KAAK,GAAG,MAAM,CAAC;IACxB,CAAC,CAAC;IAEF,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAC5C,cAAc,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAC5C,CAAC;IAEF,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IAE/B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,sBAAsB,GAAG,GAAG,EAAE;YAClC,MAAM,eAAe,GAAG,cAAc,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC;YACpE,cAAc,CAAC,eAAe,CAAC,CAAC;YAChC,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;gBACxB,WAAW,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YACvC,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,YAAY,GAAG,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;QAEnF,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,17 @@
1
+ export interface UseDoubleTapToExitOptions {
2
+ /** The confirmation message to display in the Toast (defaults to "Press back again to exit") */
3
+ message?: string;
4
+ /** The maximum time window in milliseconds between the two back taps (defaults to 2000ms) */
5
+ delayMs?: number;
6
+ /** Callback triggered on the first back button press (replaces standard Toast alert if provided) */
7
+ onFirstTap?: () => void;
8
+ }
9
+ /**
10
+ * A custom React Native hook specifically for Android that exits the application
11
+ * only when the user taps the hardware back button twice within a specified delay window.
12
+ * Supports custom action/UI callbacks on the first tap.
13
+ *
14
+ * @param options Configuration options including message, delay window, and a first-tap callback
15
+ */
16
+ export declare function useDoubleTapToExit(options?: UseDoubleTapToExitOptions): void;
17
+ //# sourceMappingURL=useDoubleTapToExit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDoubleTapToExit.d.ts","sourceRoot":"","sources":["../../src/hooks/useDoubleTapToExit.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,yBAAyB;IACxC,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oGAAoG;IACpG,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAiC5E"}
@@ -0,0 +1,40 @@
1
+ import { useRef, useEffect } from 'react';
2
+ import { BackHandler, ToastAndroid, Platform } from 'react-native';
3
+ /**
4
+ * A custom React Native hook specifically for Android that exits the application
5
+ * only when the user taps the hardware back button twice within a specified delay window.
6
+ * Supports custom action/UI callbacks on the first tap.
7
+ *
8
+ * @param options Configuration options including message, delay window, and a first-tap callback
9
+ */
10
+ export function useDoubleTapToExit(options) {
11
+ const lastPressRef = useRef(0);
12
+ const optionsRef = useRef(options);
13
+ optionsRef.current = options;
14
+ useEffect(() => {
15
+ if (Platform.OS !== 'android')
16
+ return;
17
+ const delayMs = optionsRef.current?.delayMs ?? 2000;
18
+ const message = optionsRef.current?.message ?? 'Press back again to exit';
19
+ const onBackPress = () => {
20
+ const now = Date.now();
21
+ if (lastPressRef.current && now - lastPressRef.current < delayMs) {
22
+ BackHandler.exitApp();
23
+ return true;
24
+ }
25
+ lastPressRef.current = now;
26
+ if (optionsRef.current?.onFirstTap) {
27
+ optionsRef.current.onFirstTap();
28
+ }
29
+ else {
30
+ ToastAndroid.show(message, ToastAndroid.SHORT);
31
+ }
32
+ return true;
33
+ };
34
+ const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress);
35
+ return () => {
36
+ subscription.remove();
37
+ };
38
+ }, []);
39
+ }
40
+ //# sourceMappingURL=useDoubleTapToExit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDoubleTapToExit.js","sourceRoot":"","sources":["../../src/hooks/useDoubleTapToExit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAWnE;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAmC;IACpE,MAAM,YAAY,GAAG,MAAM,CAAS,CAAC,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS;YAAE,OAAO;QAEtC,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC;QACpD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,OAAO,IAAI,0BAA0B,CAAC;QAE1E,MAAM,WAAW,GAAG,GAAY,EAAE;YAChC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,IAAI,YAAY,CAAC,OAAO,IAAI,GAAG,GAAG,YAAY,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC;gBACjE,WAAW,CAAC,OAAO,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,YAAY,CAAC,OAAO,GAAG,GAAG,CAAC;YAE3B,IAAI,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;gBACnC,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YAClC,CAAC;iBAAM,CAAC;gBACN,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YACjD,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QAEF,MAAM,YAAY,GAAG,WAAW,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;QAEpF,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A declarative React hook wrapper for `setInterval`.
3
+ * Automatically clears the interval on unmount, safely handles dynamically changing callback references,
4
+ * and allows pausing the interval by passing `null` as the delay.
5
+ *
6
+ * @param callback The function to execute on each interval tick
7
+ * @param delay The interval timing in milliseconds. Pass `null` to pause/stop the interval.
8
+ */
9
+ export declare function useInterval(callback: () => void, delay: number | null): void;
10
+ //# sourceMappingURL=useInterval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInterval.d.ts","sourceRoot":"","sources":["../../src/hooks/useInterval.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAmB5E"}
@@ -0,0 +1,27 @@
1
+ import { useEffect, useRef } from 'react';
2
+ /**
3
+ * A declarative React hook wrapper for `setInterval`.
4
+ * Automatically clears the interval on unmount, safely handles dynamically changing callback references,
5
+ * and allows pausing the interval by passing `null` as the delay.
6
+ *
7
+ * @param callback The function to execute on each interval tick
8
+ * @param delay The interval timing in milliseconds. Pass `null` to pause/stop the interval.
9
+ */
10
+ export function useInterval(callback, delay) {
11
+ const savedCallback = useRef(callback);
12
+ // Keep track of the latest callback.
13
+ useEffect(() => {
14
+ savedCallback.current = callback;
15
+ }, [callback]);
16
+ // Set up the interval.
17
+ useEffect(() => {
18
+ if (delay === null)
19
+ return;
20
+ const tick = () => {
21
+ savedCallback.current();
22
+ };
23
+ const id = setInterval(tick, delay);
24
+ return () => clearInterval(id);
25
+ }, [delay]);
26
+ }
27
+ //# sourceMappingURL=useInterval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInterval.js","sourceRoot":"","sources":["../../src/hooks/useInterval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE1C;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,QAAoB,EAAE,KAAoB;IACpE,MAAM,aAAa,GAAG,MAAM,CAAa,QAAQ,CAAC,CAAC;IAEnD,qCAAqC;IACrC,SAAS,CAAC,GAAG,EAAE;QACb,aAAa,CAAC,OAAO,GAAG,QAAQ,CAAC;IACnC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,uBAAuB;IACvB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO;QAE3B,MAAM,IAAI,GAAG,GAAS,EAAE;YACtB,aAAa,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC,CAAC;QAEF,MAAM,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACpC,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;AACd,CAAC"}
@@ -0,0 +1,23 @@
1
+ export interface KeyboardState {
2
+ /** True if the virtual keyboard is currently visible on the screen */
3
+ keyboardShown: boolean;
4
+ /** The height of the virtual keyboard in screen points */
5
+ keyboardHeight: number;
6
+ }
7
+ export interface UseKeyboardOptions {
8
+ /** Callback triggered when the keyboard is shown, receiving the keyboard height */
9
+ onShow?: (height: number) => void;
10
+ /** Callback triggered when the keyboard is hidden */
11
+ onHide?: () => void;
12
+ /** Callback triggered when keyboard visibility changes, receiving the new visibility status and height */
13
+ onChange?: (keyboardShown: boolean, keyboardHeight: number) => void;
14
+ }
15
+ /**
16
+ * A custom React Native hook to monitor keyboard visibility and height.
17
+ * Handles platform differences (willShow/willHide on iOS for smoother transitions, didShow/didHide on Android).
18
+ *
19
+ * @param options Optional callbacks for keyboard events
20
+ * @returns An object containing `keyboardShown` and `keyboardHeight`
21
+ */
22
+ export declare function useKeyboard(options?: UseKeyboardOptions): KeyboardState;
23
+ //# sourceMappingURL=useKeyboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useKeyboard.d.ts","sourceRoot":"","sources":["../../src/hooks/useKeyboard.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,aAAa,EAAE,OAAO,CAAC;IACvB,0DAA0D;IAC1D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,mFAAmF;IACnF,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;CACrE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,aAAa,CA0CvE"}
@@ -0,0 +1,46 @@
1
+ import { useEffect, useState, useRef } from 'react';
2
+ import { Keyboard, Platform } from 'react-native';
3
+ /**
4
+ * A custom React Native hook to monitor keyboard visibility and height.
5
+ * Handles platform differences (willShow/willHide on iOS for smoother transitions, didShow/didHide on Android).
6
+ *
7
+ * @param options Optional callbacks for keyboard events
8
+ * @returns An object containing `keyboardShown` and `keyboardHeight`
9
+ */
10
+ export function useKeyboard(options) {
11
+ const [keyboardState, setKeyboardState] = useState({
12
+ keyboardShown: false,
13
+ keyboardHeight: 0,
14
+ });
15
+ const optionsRef = useRef(options);
16
+ optionsRef.current = options;
17
+ useEffect(() => {
18
+ const handleKeyboardShow = (e) => {
19
+ const height = e.endCoordinates.height;
20
+ setKeyboardState({
21
+ keyboardShown: true,
22
+ keyboardHeight: height,
23
+ });
24
+ optionsRef.current?.onShow?.(height);
25
+ optionsRef.current?.onChange?.(true, height);
26
+ };
27
+ const handleKeyboardHide = () => {
28
+ setKeyboardState({
29
+ keyboardShown: false,
30
+ keyboardHeight: 0,
31
+ });
32
+ optionsRef.current?.onHide?.();
33
+ optionsRef.current?.onChange?.(false, 0);
34
+ };
35
+ const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
36
+ const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
37
+ const showSubscription = Keyboard.addListener(showEvent, handleKeyboardShow);
38
+ const hideSubscription = Keyboard.addListener(hideEvent, handleKeyboardHide);
39
+ return () => {
40
+ showSubscription.remove();
41
+ hideSubscription.remove();
42
+ };
43
+ }, []);
44
+ return keyboardState;
45
+ }
46
+ //# sourceMappingURL=useKeyboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useKeyboard.js","sourceRoot":"","sources":["../../src/hooks/useKeyboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAiB,QAAQ,EAAE,MAAM,cAAc,CAAC;AAkBjE;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAA4B;IACtD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAgB;QAChE,aAAa,EAAE,KAAK;QACpB,cAAc,EAAE,CAAC;KAClB,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,kBAAkB,GAAG,CAAC,CAAgB,EAAQ,EAAE;YACpD,MAAM,MAAM,GAAG,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC;YACvC,gBAAgB,CAAC;gBACf,aAAa,EAAE,IAAI;gBACnB,cAAc,EAAE,MAAM;aACvB,CAAC,CAAC;YACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YACrC,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC/C,CAAC,CAAC;QAEF,MAAM,kBAAkB,GAAG,GAAS,EAAE;YACpC,gBAAgB,CAAC;gBACf,aAAa,EAAE,KAAK;gBACpB,cAAc,EAAE,CAAC;aAClB,CAAC,CAAC;YACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;YAC/B,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC3C,CAAC,CAAC;QAEF,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,iBAAiB,CAAC;QACjF,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,iBAAiB,CAAC;QAEjF,MAAM,gBAAgB,GAAG,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;QAC7E,MAAM,gBAAgB,GAAG,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;QAE7E,OAAO,GAAG,EAAE;YACV,gBAAgB,CAAC,MAAM,EAAE,CAAC;YAC1B,gBAAgB,CAAC,MAAM,EAAE,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,aAAa,CAAC;AACvB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { LayoutChangeEvent, LayoutRectangle } from 'react-native';
2
+ export interface UseLayoutOptions {
3
+ /** Callback triggered when the view dimensions or positions change */
4
+ onChange?: (layout: LayoutRectangle) => void;
5
+ }
6
+ /**
7
+ * A custom React Native hook to dynamically measure the size and position of a view.
8
+ * Useful for building custom alignments, dropdown positioning, and layouts.
9
+ *
10
+ * @param options Optional configuration, including an onChange callback
11
+ * @returns A read-only tuple containing:
12
+ * - `layout`: The current layout rectangle metrics (`x`, `y`, `width`, `height`)
13
+ * - `onLayout`: The event callback function to pass to the target component's `onLayout` prop
14
+ */
15
+ export declare function useLayout(options?: UseLayoutOptions): readonly [
16
+ LayoutRectangle,
17
+ (e: LayoutChangeEvent) => void
18
+ ];
19
+ //# sourceMappingURL=useLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLayout.d.ts","sourceRoot":"","sources":["../../src/hooks/useLayout.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS;IAC9D,eAAe;IACf,CAAC,CAAC,EAAE,iBAAiB,KAAK,IAAI;CAC/B,CAoBA"}
@@ -0,0 +1,29 @@
1
+ import { useState, useCallback, useRef } from 'react';
2
+ /**
3
+ * A custom React Native hook to dynamically measure the size and position of a view.
4
+ * Useful for building custom alignments, dropdown positioning, and layouts.
5
+ *
6
+ * @param options Optional configuration, including an onChange callback
7
+ * @returns A read-only tuple containing:
8
+ * - `layout`: The current layout rectangle metrics (`x`, `y`, `width`, `height`)
9
+ * - `onLayout`: The event callback function to pass to the target component's `onLayout` prop
10
+ */
11
+ export function useLayout(options) {
12
+ const [layout, setLayout] = useState({
13
+ x: 0,
14
+ y: 0,
15
+ width: 0,
16
+ height: 0,
17
+ });
18
+ const optionsRef = useRef(options);
19
+ optionsRef.current = options;
20
+ const onLayout = useCallback((e) => {
21
+ const nextLayout = e.nativeEvent.layout;
22
+ setLayout(nextLayout);
23
+ if (optionsRef.current?.onChange) {
24
+ optionsRef.current.onChange(nextLayout);
25
+ }
26
+ }, []);
27
+ return [layout, onLayout];
28
+ }
29
+ //# sourceMappingURL=useLayout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLayout.js","sourceRoot":"","sources":["../../src/hooks/useLayout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAQtD;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,OAA0B;IAIlD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAkB;QACpD,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,CAAC;QACJ,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,CAAC;KACV,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAoB,EAAQ,EAAE;QAC1D,MAAM,UAAU,GAAG,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC;QACxC,SAAS,CAAC,UAAU,CAAC,CAAC;QACtB,IAAI,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;YACjC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAU,CAAC;AACrC,CAAC"}
@@ -0,0 +1,45 @@
1
+ export interface NetworkStatus {
2
+ /** True if the network check succeeded, false if it failed or timed out */
3
+ isConnected: boolean | null;
4
+ /** True if internet reachability check succeeded */
5
+ isInternetReachable: boolean | null;
6
+ /** Round-trip ping latency in milliseconds */
7
+ latencyMs: number | null;
8
+ /** Connection quality based on latency ('poor' | 'good' | 'excellent') */
9
+ connectionQuality: 'poor' | 'good' | 'excellent' | null;
10
+ /** Estimated internet download speed in Mbps (automatically measured when online) */
11
+ internetSpeed: number | null;
12
+ /** True if a speed test is currently running */
13
+ isTestingSpeed: boolean;
14
+ /** Triggers a download speed test manually to measure speed in Mbps */
15
+ runSpeedTest: () => Promise<number | null>;
16
+ }
17
+ export interface UseNetworkStatusOptions {
18
+ /** The URL to ping to verify internet reachability (defaults to google connection test) */
19
+ pingUrl?: string;
20
+ /** The interval in milliseconds to poll the connection (defaults to 30000ms, set to 0 to disable polling) */
21
+ pingIntervalMs?: number;
22
+ /** The fetch timeout in milliseconds (defaults to 5000ms) */
23
+ timeoutMs?: number;
24
+ /** Custom URL to download for the speed test (defaults to a reliable jQuery CDN asset) */
25
+ speedTestUrl?: string;
26
+ /** File size of the speed test URL in bytes (defaults to 90000 bytes for jQuery) */
27
+ speedTestFileSizeInBytes?: number;
28
+ /** If true, disables the automatic speed test on mount/online transitions (defaults to false) */
29
+ disableAutoSpeedTest?: boolean;
30
+ /** Callback triggered when any network status updates */
31
+ onChange?: (status: NetworkStatus) => void;
32
+ /** Callback triggered when the device comes online */
33
+ onOnline?: () => void;
34
+ /** Callback triggered when the device goes offline */
35
+ onOffline?: () => void;
36
+ }
37
+ /**
38
+ * A custom React Native hook to monitor network connectivity status, latency, and speed.
39
+ * Uses lightweight network fetches combined with React Native's AppState transitions and web browser event listeners.
40
+ *
41
+ * @param options Configuration options for ping URL, timeout, polling interval, and speed test setup
42
+ * @returns The current NetworkStatus object containing status, speed, and triggers
43
+ */
44
+ export declare function useNetworkStatus(options?: UseNetworkStatusOptions): NetworkStatus;
45
+ //# sourceMappingURL=useNetworkStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNetworkStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useNetworkStatus.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,WAAW,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B,oDAAoD;IACpD,mBAAmB,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,8CAA8C;IAC9C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,0EAA0E;IAC1E,iBAAiB,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IACxD,qFAAqF;IACrF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gDAAgD;IAChD,cAAc,EAAE,OAAO,CAAC;IACxB,uEAAuE;IACvE,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,uBAAuB;IACtC,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6GAA6G;IAC7G,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,iGAAiG;IACjG,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC3C,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,aAAa,CAqMjF"}
@@ -0,0 +1,184 @@
1
+ import { useEffect, useState, useCallback, useRef } from 'react';
2
+ import { AppState } from 'react-native';
3
+ import { usePrevious } from './usePrevious';
4
+ /**
5
+ * A custom React Native hook to monitor network connectivity status, latency, and speed.
6
+ * Uses lightweight network fetches combined with React Native's AppState transitions and web browser event listeners.
7
+ *
8
+ * @param options Configuration options for ping URL, timeout, polling interval, and speed test setup
9
+ * @returns The current NetworkStatus object containing status, speed, and triggers
10
+ */
11
+ export function useNetworkStatus(options) {
12
+ const pingUrl = options?.pingUrl ?? 'https://clients3.google.com/generate_204';
13
+ const pingIntervalMs = options?.pingIntervalMs ?? 30000;
14
+ const timeoutMs = options?.timeoutMs ?? 5000;
15
+ const speedTestUrl = options?.speedTestUrl ?? 'https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js';
16
+ const speedTestFileSizeInBytes = options?.speedTestFileSizeInBytes ?? 90000;
17
+ const disableAutoSpeedTest = options?.disableAutoSpeedTest ?? false;
18
+ const [status, setStatus] = useState({
19
+ isConnected: null,
20
+ isInternetReachable: null,
21
+ latencyMs: null,
22
+ connectionQuality: null,
23
+ });
24
+ const [isTestingSpeed, setIsTestingSpeed] = useState(false);
25
+ const [downloadSpeedMbps, setDownloadSpeedMbps] = useState(null);
26
+ const optionsRef = useRef(options);
27
+ optionsRef.current = options;
28
+ const getConnectionQuality = (latency) => {
29
+ if (latency === null)
30
+ return null;
31
+ if (latency < 150)
32
+ return 'excellent';
33
+ if (latency < 400)
34
+ return 'good';
35
+ return 'poor';
36
+ };
37
+ const runSpeedTest = useCallback(async () => {
38
+ setIsTestingSpeed(true);
39
+ try {
40
+ const startTime = Date.now();
41
+ const response = await fetch(speedTestUrl, {
42
+ method: 'GET',
43
+ headers: { 'Cache-Control': 'no-cache' },
44
+ });
45
+ if (!response.ok)
46
+ throw new Error('Speed test fetch failed');
47
+ // Consume the body to calculate complete download duration
48
+ await response.text();
49
+ const durationSec = (Date.now() - startTime) / 1000;
50
+ if (durationSec <= 0)
51
+ return null;
52
+ const fileSizeBits = speedTestFileSizeInBytes * 8;
53
+ const speedMbps = fileSizeBits / durationSec / 1000000;
54
+ const roundedSpeed = Math.round(speedMbps * 100) / 100;
55
+ setDownloadSpeedMbps(roundedSpeed);
56
+ return roundedSpeed;
57
+ }
58
+ catch (error) {
59
+ console.warn('Network speed test failed:', error);
60
+ return null;
61
+ }
62
+ finally {
63
+ setIsTestingSpeed(false);
64
+ }
65
+ }, [speedTestUrl, speedTestFileSizeInBytes]);
66
+ const returnedStatus = {
67
+ ...status,
68
+ internetSpeed: downloadSpeedMbps,
69
+ isTestingSpeed,
70
+ runSpeedTest,
71
+ };
72
+ // Run speed test automatically on initial online transition
73
+ useEffect(() => {
74
+ if (status.isInternetReachable &&
75
+ downloadSpeedMbps === null &&
76
+ !isTestingSpeed &&
77
+ !disableAutoSpeedTest) {
78
+ runSpeedTest();
79
+ }
80
+ }, [status.isInternetReachable, downloadSpeedMbps, isTestingSpeed, runSpeedTest, disableAutoSpeedTest]);
81
+ // Handle callback triggers on network status change
82
+ const prevIsInternetReachable = usePrevious(status.isInternetReachable);
83
+ useEffect(() => {
84
+ if (status.isConnected === null)
85
+ return;
86
+ optionsRef.current?.onChange?.(returnedStatus);
87
+ if (status.isInternetReachable === true && prevIsInternetReachable === false) {
88
+ optionsRef.current?.onOnline?.();
89
+ }
90
+ else if (status.isInternetReachable === false && prevIsInternetReachable === true) {
91
+ optionsRef.current?.onOffline?.();
92
+ }
93
+ }, [
94
+ status.isConnected,
95
+ status.isInternetReachable,
96
+ status.latencyMs,
97
+ status.connectionQuality,
98
+ downloadSpeedMbps,
99
+ isTestingSpeed,
100
+ prevIsInternetReachable,
101
+ ]);
102
+ useEffect(() => {
103
+ let isMounted = true;
104
+ let intervalId = null;
105
+ const checkConnectivity = async () => {
106
+ const startTime = Date.now();
107
+ try {
108
+ const controller = new AbortController();
109
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
110
+ // Perform a HEAD request to save bandwidth
111
+ const response = await fetch(pingUrl, {
112
+ method: 'HEAD',
113
+ signal: controller.signal,
114
+ headers: { 'Cache-Control': 'no-cache' },
115
+ });
116
+ clearTimeout(timeoutId);
117
+ const isReachable = response.ok || response.status < 400;
118
+ const latencyMs = Date.now() - startTime;
119
+ return { isReachable, latencyMs };
120
+ }
121
+ catch {
122
+ return { isReachable: false, latencyMs: null };
123
+ }
124
+ };
125
+ const performCheck = async () => {
126
+ const { isReachable, latencyMs } = await checkConnectivity();
127
+ if (isMounted) {
128
+ setStatus({
129
+ isConnected: isReachable,
130
+ isInternetReachable: isReachable,
131
+ latencyMs,
132
+ connectionQuality: getConnectionQuality(latencyMs),
133
+ });
134
+ }
135
+ };
136
+ // Initial check on mount
137
+ performCheck();
138
+ // Periodic polling to catch silent connectivity drops
139
+ if (pingIntervalMs > 0) {
140
+ intervalId = setInterval(performCheck, pingIntervalMs);
141
+ }
142
+ // Trigger check immediately when app transitions to active foreground state
143
+ const appStateSubscription = AppState.addEventListener('change', (nextAppState) => {
144
+ if (nextAppState === 'active') {
145
+ performCheck();
146
+ }
147
+ });
148
+ // Support for React Native Web online/offline window listeners
149
+ const handleWebOnline = () => {
150
+ if (isMounted) {
151
+ setStatus((prev) => ({ ...prev, isConnected: true, isInternetReachable: true }));
152
+ performCheck();
153
+ }
154
+ };
155
+ const handleWebOffline = () => {
156
+ if (isMounted) {
157
+ setStatus({
158
+ isConnected: false,
159
+ isInternetReachable: false,
160
+ latencyMs: null,
161
+ connectionQuality: null,
162
+ });
163
+ }
164
+ };
165
+ const hasWindowListeners = typeof window !== 'undefined' && typeof window.addEventListener === 'function';
166
+ if (hasWindowListeners) {
167
+ window.addEventListener('online', handleWebOnline);
168
+ window.addEventListener('offline', handleWebOffline);
169
+ }
170
+ return () => {
171
+ isMounted = false;
172
+ if (intervalId) {
173
+ clearInterval(intervalId);
174
+ }
175
+ appStateSubscription.remove();
176
+ if (hasWindowListeners) {
177
+ window.removeEventListener('online', handleWebOnline);
178
+ window.removeEventListener('offline', handleWebOffline);
179
+ }
180
+ };
181
+ }, [pingUrl, pingIntervalMs, timeoutMs]);
182
+ return returnedStatus;
183
+ }
184
+ //# sourceMappingURL=useNetworkStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNetworkStatus.js","sourceRoot":"","sources":["../../src/hooks/useNetworkStatus.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A custom React hook to track the previous value of a state or prop.
3
+ *
4
+ * @template T The type of the value to track
5
+ * @param value The current value
6
+ * @returns The value from the previous render, or `undefined` on the initial render
7
+ */
8
+ export declare function usePrevious<T>(value: T): T | undefined;
9
+ //# sourceMappingURL=usePrevious.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePrevious.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrevious.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAQtD"}
@@ -0,0 +1,16 @@
1
+ import { useEffect, useRef } from 'react';
2
+ /**
3
+ * A custom React hook to track the previous value of a state or prop.
4
+ *
5
+ * @template T The type of the value to track
6
+ * @param value The current value
7
+ * @returns The value from the previous render, or `undefined` on the initial render
8
+ */
9
+ export function usePrevious(value) {
10
+ const ref = useRef(undefined);
11
+ useEffect(() => {
12
+ ref.current = value;
13
+ }, [value]);
14
+ return ref.current;
15
+ }
16
+ //# sourceMappingURL=usePrevious.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePrevious.js","sourceRoot":"","sources":["../../src/hooks/usePrevious.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAI,KAAQ;IACrC,MAAM,GAAG,GAAG,MAAM,CAAgB,SAAS,CAAC,CAAC;IAE7C,SAAS,CAAC,GAAG,EAAE;QACb,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;IACtB,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * A custom React hook to throttle updates to a state value.
3
+ * Useful for limiting performance-heavy operations triggered by frequent updates (e.g. dimensions, scroll events).
4
+ *
5
+ * @template T The type of the value to throttle
6
+ * @param value The value to throttle
7
+ * @param limit The throttle cooldown limit in milliseconds
8
+ * @param onChange Optional callback triggered when the throttled value changes
9
+ * @returns The throttled value
10
+ */
11
+ export declare function useThrottle<T>(value: T, limit: number, onChange?: (value: T) => void): T;
12
+ //# sourceMappingURL=useThrottle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useThrottle.d.ts","sourceRoot":"","sources":["../../src/hooks/useThrottle.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAC5B,CAAC,CAwCH"}
@@ -0,0 +1,47 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ /**
3
+ * A custom React hook to throttle updates to a state value.
4
+ * Useful for limiting performance-heavy operations triggered by frequent updates (e.g. dimensions, scroll events).
5
+ *
6
+ * @template T The type of the value to throttle
7
+ * @param value The value to throttle
8
+ * @param limit The throttle cooldown limit in milliseconds
9
+ * @param onChange Optional callback triggered when the throttled value changes
10
+ * @returns The throttled value
11
+ */
12
+ export function useThrottle(value, limit, onChange) {
13
+ const [throttledValue, setThrottledValue] = useState(value);
14
+ const lastRan = useRef(0);
15
+ const timeoutRef = useRef(null);
16
+ const onChangeRef = useRef(onChange);
17
+ onChangeRef.current = onChange;
18
+ useEffect(() => {
19
+ const now = Date.now();
20
+ const timeRemaining = limit - (now - lastRan.current);
21
+ const updateValue = (val) => {
22
+ setThrottledValue(val);
23
+ lastRan.current = Date.now();
24
+ if (onChangeRef.current) {
25
+ onChangeRef.current(val);
26
+ }
27
+ };
28
+ if (timeRemaining <= 0) {
29
+ updateValue(value);
30
+ }
31
+ else {
32
+ if (timeoutRef.current) {
33
+ clearTimeout(timeoutRef.current);
34
+ }
35
+ timeoutRef.current = setTimeout(() => {
36
+ updateValue(value);
37
+ }, timeRemaining);
38
+ }
39
+ return () => {
40
+ if (timeoutRef.current) {
41
+ clearTimeout(timeoutRef.current);
42
+ }
43
+ };
44
+ }, [value, limit]);
45
+ return throttledValue;
46
+ }
47
+ //# sourceMappingURL=useThrottle.js.map