react-simplikit 0.0.34 → 0.0.36

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 (56) hide show
  1. package/README.md +1 -1
  2. package/dist/components/ImpressionArea/index.cjs +17 -20
  3. package/dist/components/ImpressionArea/index.d.cts +2 -2
  4. package/dist/components/SwitchCase/index.d.cts +4 -4
  5. package/dist/hooks/useAsyncEffect/index.d.cts +1 -1
  6. package/dist/hooks/useCallbackOncePerRender/index.d.cts +2 -3
  7. package/dist/hooks/useControlledState/index.d.cts +1 -1
  8. package/dist/hooks/useCounter/index.cjs +1 -1
  9. package/dist/hooks/useCounter/index.d.cts +3 -5
  10. package/dist/hooks/useDebouncedCallback/index.cjs +144 -0
  11. package/dist/hooks/useDebouncedCallback/index.d.cts +32 -0
  12. package/dist/hooks/useDoubleClick/index.d.cts +4 -4
  13. package/dist/hooks/useGeolocation/index.cjs +153 -0
  14. package/dist/hooks/useGeolocation/index.d.cts +106 -0
  15. package/dist/hooks/useImpressionRef/index.d.cts +2 -2
  16. package/dist/hooks/useIntersectionObserver/index.d.cts +2 -2
  17. package/dist/hooks/useIsomorphicLayoutEffect/index.d.cts +3 -3
  18. package/dist/hooks/useLongPress/index.cjs +123 -0
  19. package/dist/hooks/useLongPress/index.d.cts +70 -0
  20. package/dist/hooks/useMap/index.cjs +77 -0
  21. package/dist/hooks/useMap/index.d.cts +46 -0
  22. package/dist/hooks/usePrevious/index.d.cts +2 -2
  23. package/dist/hooks/useRefEffect/index.d.cts +1 -1
  24. package/dist/hooks/useStorageState/index.d.cts +1 -1
  25. package/dist/hooks/useThrottle/index.d.cts +1 -1
  26. package/dist/hooks/useVisibilityEvent/index.d.cts +1 -1
  27. package/dist/index.cjs +453 -98
  28. package/dist/index.d.cts +9 -1
  29. package/esm/components/ImpressionArea/index.d.ts +2 -2
  30. package/esm/components/ImpressionArea/index.js +17 -20
  31. package/esm/components/SwitchCase/index.d.ts +4 -4
  32. package/esm/hooks/useAsyncEffect/index.d.ts +1 -1
  33. package/esm/hooks/useCallbackOncePerRender/index.d.ts +2 -3
  34. package/esm/hooks/useControlledState/index.d.ts +1 -1
  35. package/esm/hooks/useCounter/index.d.ts +3 -5
  36. package/esm/hooks/useCounter/index.js +1 -1
  37. package/esm/hooks/useDebouncedCallback/index.d.ts +32 -0
  38. package/esm/hooks/useDebouncedCallback/index.js +117 -0
  39. package/esm/hooks/useDoubleClick/index.d.ts +4 -4
  40. package/esm/hooks/useGeolocation/index.d.ts +106 -0
  41. package/esm/hooks/useGeolocation/index.js +126 -0
  42. package/esm/hooks/useImpressionRef/index.d.ts +2 -2
  43. package/esm/hooks/useIntersectionObserver/index.d.ts +2 -2
  44. package/esm/hooks/useIsomorphicLayoutEffect/index.d.ts +3 -3
  45. package/esm/hooks/useLongPress/index.d.ts +70 -0
  46. package/esm/hooks/useLongPress/index.js +96 -0
  47. package/esm/hooks/useMap/index.d.ts +46 -0
  48. package/esm/hooks/useMap/index.js +50 -0
  49. package/esm/hooks/usePrevious/index.d.ts +2 -2
  50. package/esm/hooks/useRefEffect/index.d.ts +1 -1
  51. package/esm/hooks/useStorageState/index.d.ts +1 -1
  52. package/esm/hooks/useThrottle/index.d.ts +1 -1
  53. package/esm/hooks/useVisibilityEvent/index.d.ts +1 -1
  54. package/esm/index.d.ts +9 -1
  55. package/esm/index.js +421 -74
  56. package/package.json +2 -3
package/dist/index.d.cts CHANGED
@@ -4,13 +4,20 @@ export { SwitchCase } from './components/SwitchCase/index.cjs';
4
4
  export { useAsyncEffect } from './hooks/useAsyncEffect/index.cjs';
5
5
  export { useBooleanState } from './hooks/useBooleanState/index.cjs';
6
6
  export { useCallbackOncePerRender } from './hooks/useCallbackOncePerRender/index.cjs';
7
+ export { useControlledState } from './hooks/useControlledState/index.cjs';
7
8
  export { useCounter } from './hooks/useCounter/index.cjs';
8
9
  export { useDebounce } from './hooks/useDebounce/index.cjs';
10
+ export { useDebouncedCallback } from './hooks/useDebouncedCallback/index.cjs';
11
+ export { useDoubleClick } from './hooks/useDoubleClick/index.cjs';
12
+ export { useGeolocation } from './hooks/useGeolocation/index.cjs';
9
13
  export { useImpressionRef } from './hooks/useImpressionRef/index.cjs';
10
14
  export { useInputState } from './hooks/useInputState/index.cjs';
11
15
  export { useIntersectionObserver } from './hooks/useIntersectionObserver/index.cjs';
12
16
  export { useInterval } from './hooks/useInterval/index.cjs';
17
+ export { useIsomorphicLayoutEffect } from './hooks/useIsomorphicLayoutEffect/index.cjs';
13
18
  export { useLoading } from './hooks/useLoading/index.cjs';
19
+ export { useLongPress } from './hooks/useLongPress/index.cjs';
20
+ export { useMap } from './hooks/useMap/index.cjs';
14
21
  export { useOutsideClickEffect } from './hooks/useOutsideClickEffect/index.cjs';
15
22
  export { usePreservedCallback } from './hooks/usePreservedCallback/index.cjs';
16
23
  export { usePreservedReference } from './hooks/usePreservedReference/index.cjs';
@@ -22,6 +29,7 @@ export { useTimeout } from './hooks/useTimeout/index.cjs';
22
29
  export { useToggle } from './hooks/useToggle/index.cjs';
23
30
  export { useVisibilityEvent } from './hooks/useVisibilityEvent/index.cjs';
24
31
  export { buildContext } from './utils/buildContext/index.cjs';
32
+ export { mergeProps } from './utils/mergeProps/index.cjs';
25
33
  export { mergeRefs } from './utils/mergeRefs/index.cjs';
26
- import 'react/jsx-runtime';
27
34
  import 'react';
35
+ import 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { ElementType, Ref, ReactNode } from 'react';
3
3
  import { UseImpressionRefOptions } from '../../hooks/useImpressionRef/index.js';
4
4
 
@@ -40,6 +40,6 @@ type Props<Tag extends ElementType> = React.ComponentPropsWithoutRef<Tag> & UseI
40
40
  * );
41
41
  * }
42
42
  */
43
- declare function ImpressionArea<T extends ElementType = 'div'>({ as, rootMargin, areaThreshold, timeThreshold, onImpressionStart, onImpressionEnd, ref, ...props }: Props<T>): react_jsx_runtime.JSX.Element;
43
+ declare const ImpressionArea: react.ForwardRefExoticComponent<Omit<Props<ElementType>, "ref"> & react.RefAttributes<Element>>;
44
44
 
45
45
  export { ImpressionArea };
@@ -1,3 +1,6 @@
1
+ // src/components/ImpressionArea/ImpressionArea.tsx
2
+ import { forwardRef } from "react";
3
+
1
4
  // src/hooks/useImpressionRef/useImpressionRef.ts
2
5
  import { useRef as useRef4 } from "react";
3
6
 
@@ -241,26 +244,20 @@ function mergeRefs(...refs) {
241
244
 
242
245
  // src/components/ImpressionArea/ImpressionArea.tsx
243
246
  import { jsx } from "react/jsx-runtime";
244
- function ImpressionArea({
245
- as,
246
- rootMargin,
247
- areaThreshold,
248
- timeThreshold,
249
- onImpressionStart,
250
- onImpressionEnd,
251
- ref,
252
- ...props
253
- }) {
254
- const Component = as ?? "div";
255
- const impressionRef = useImpressionRef({
256
- onImpressionStart,
257
- onImpressionEnd,
258
- areaThreshold,
259
- timeThreshold,
260
- rootMargin
261
- });
262
- return /* @__PURE__ */ jsx(Component, { ref: mergeRefs(ref, impressionRef), ...props });
263
- }
247
+ var ImpressionArea = forwardRef(
248
+ ({ as, rootMargin, areaThreshold, timeThreshold, onImpressionStart, onImpressionEnd, ...props }, ref) => {
249
+ const Component = as ?? "div";
250
+ const impressionRef = useImpressionRef({
251
+ onImpressionStart,
252
+ onImpressionEnd,
253
+ areaThreshold,
254
+ timeThreshold,
255
+ rootMargin
256
+ });
257
+ return /* @__PURE__ */ jsx(Component, { ref: mergeRefs(ref, impressionRef), ...props });
258
+ }
259
+ );
260
+ ImpressionArea.displayName = "ImpressionArea";
264
261
  export {
265
262
  ImpressionArea
266
263
  };
@@ -14,15 +14,15 @@ type Props<Case> = {
14
14
  * similar to a `switch-case` statement. It is useful when you need to conditionally render different
15
15
  * components depending on a specific state.
16
16
  *
17
- * @param {string | number} value - The value to compare against.
17
+ * @param {Case} value - The value to compare against.
18
18
  * The component associated with the matching key in `caseBy` will be rendered.
19
- * @param {Record<string | number, () => JSX.Element>} caseBy - An object that maps values to
19
+ * @param {Partial<{ [P in StringifiedValue<Case>]: () => ReactElement | null }>} caseBy - An object that maps values to
20
20
  * components to render. The keys represent possible values, and the values are functions returning
21
21
  * the corresponding components.
22
- * @param {() => JSX.Element} [defaultComponent] - The component to render if `value` does not match
22
+ * @param {() => ReactElement | null} [defaultComponent] - The component to render if `value` does not match
23
23
  * any key in `caseBy`.
24
24
  *
25
- * @returns {JSX.Element} A React component that conditionally renders based on cases.
25
+ * @returns {ReactElement | null} A React component that conditionally renders based on cases.
26
26
  *
27
27
  * @example
28
28
  * function App() {
@@ -2,7 +2,7 @@ import { DependencyList } from 'react';
2
2
 
3
3
  /**
4
4
  * @description
5
- * `useAsyncEffect` is a custom hook for handling asynchronous side effects in React components.
5
+ * `useAsyncEffect` is a React hook for handling asynchronous side effects in React components.
6
6
  * It follows the same cleanup pattern as `useEffect` while ensuring async operations are handled safely.
7
7
  *
8
8
  * @param {() => Promise<void | (() => void)>} [effect] - An asynchronous function executed in the `useEffect` pattern.
@@ -2,9 +2,8 @@ import { DependencyList } from 'react';
2
2
 
3
3
  /**
4
4
  * @description
5
- * A React hook that ensures a callback function is executed only once, regardless of
6
- * how many times it's called. This is useful for one-time operations that should not
7
- * be repeated, even if the component re-renders.
5
+ * `useCallbackOncePerRender` is a React hook that ensures a callback function is executed only once, regardless of how many times it's called.
6
+ * This is useful for one-time operations that should not be repeated, even if the component re-renders.
8
7
  *
9
8
  * @param {() => void} callback - The callback function to be executed once.
10
9
  * @param {DependencyList} deps - Dependencies array that will trigger a new one-time execution when changed.
@@ -13,7 +13,7 @@ type UseControlledStateProps<T> = ControlledState<T> & {
13
13
  };
14
14
  /**
15
15
  * @description
16
- * `useControlledState` is a hook that allows you to control both controlled and uncontrolled states.
16
+ * `useControlledState` is a React hook that allows you to control both controlled and uncontrolled states.
17
17
  * If you pass the state to `value`, it will be a controlled state, and if you pass the state to `defaultValue`, it will be an uncontrolled state.
18
18
  * If both `value` and `defaultValue` are passed, `value` will take precedence.
19
19
  *
@@ -1,5 +1,4 @@
1
1
  type UseCounterOptions = {
2
- initialValue?: number;
3
2
  min?: number;
4
3
  max?: number;
5
4
  step?: number;
@@ -16,8 +15,8 @@ type UseCounterReturn = {
16
15
  * `useCounter` is a React hook that manages a numeric counter state with increment, decrement, and reset capabilities.
17
16
  * Optionally, you can provide minimum and maximum values to constrain the counter's range.
18
17
  *
18
+ * @param {number} [initialValue=0] - Initial value for the counter. Defaults to 0.
19
19
  * @param {UseCounterOptions} options - The options for the counter.
20
- * @param {number} [options.initialValue=0] - Initial value for the counter. Defaults to 0.
21
20
  * @param {number} [options.min] - Minimum value the counter can reach. If not provided, there is no lower limit.
22
21
  * @param {number} [options.max] - Maximum value the counter can reach. If not provided, there is no upper limit.
23
22
  * @param {number} [options.step=1] - Value to increment or decrement by. Defaults to 1.
@@ -33,8 +32,7 @@ type UseCounterReturn = {
33
32
  * import { useCounter } from 'react-simplikit';
34
33
  *
35
34
  * function ShoppingCart() {
36
- * const { count, increment, decrement, reset } = useCounter({
37
- * initialValue: 1,
35
+ * const { count, increment, decrement, reset } = useCounter(1, {
38
36
  * min: 1,
39
37
  * max: 10,
40
38
  * });
@@ -49,6 +47,6 @@ type UseCounterReturn = {
49
47
  * );
50
48
  * }
51
49
  */
52
- declare function useCounter({ initialValue, min, max, step }?: UseCounterOptions): UseCounterReturn;
50
+ declare function useCounter(initialValue?: number, { min, max, step }?: UseCounterOptions): UseCounterReturn;
53
51
 
54
52
  export { useCounter };
@@ -1,6 +1,6 @@
1
1
  // src/hooks/useCounter/useCounter.ts
2
2
  import { useCallback, useState } from "react";
3
- function useCounter({ initialValue = 0, min, max, step = 1 } = {}) {
3
+ function useCounter(initialValue = 0, { min, max, step = 1 } = {}) {
4
4
  const validateValue = (value) => {
5
5
  let validatedValue = value;
6
6
  if (min !== void 0 && validatedValue < min) {
@@ -0,0 +1,32 @@
1
+ type DebounceOptions = {
2
+ leading?: boolean;
3
+ trailing?: boolean;
4
+ };
5
+ /**
6
+ * @description
7
+ * `useDebouncedCallback` is a React hook that returns a debounced version of the provided callback function.
8
+ * It helps optimize event handling by delaying function execution and grouping multiple calls into one.
9
+ *
10
+ * Note that if both 'leading' and 'trailing' are set, the function will be called at both the start and end of the delay period. However, it must be called at least twice within debounceMs interval for this to happen, since one debounced function call cannot trigger the function twice.
11
+ *
12
+ * @param {Object} options - The options object.
13
+ * @param {Function} options.onChange - The callback function to debounce.
14
+ * @param {number} options.timeThreshold - The number of milliseconds to delay the function execution.
15
+ * @param {boolean} [options.leading=false] - If `true`, the function is called at the start of the sequence.
16
+ * @param {boolean} [options.trailing=true] - If `true`, the function is called at the end of the sequence.
17
+ *
18
+ * @returns {Function} A debounced function that delays invoking the callback.
19
+ *
20
+ * @example
21
+ * function SearchInput() {
22
+ * const [query, setQuery] = useState('');
23
+ * const debouncedSetQuery = useDebouncedCallback({ onChange: setQuery, timeThreshold: 100 });
24
+ * return <input type="text" onChange={(e) => debouncedSetQuery(e.target.value)} />;
25
+ * }
26
+ */
27
+ declare function useDebouncedCallback({ onChange, timeThreshold, leading, trailing, }: DebounceOptions & {
28
+ onChange: (newValue: boolean) => void;
29
+ timeThreshold: number;
30
+ }): (nextValue: boolean) => void;
31
+
32
+ export { useDebouncedCallback };
@@ -0,0 +1,117 @@
1
+ // src/hooks/useDebouncedCallback/useDebouncedCallback.ts
2
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
3
+
4
+ // src/hooks/useDebounce/debounce.ts
5
+ function debounce(func, debounceMs, { edges = ["leading", "trailing"] } = {}) {
6
+ let pendingThis = void 0;
7
+ let pendingArgs = null;
8
+ const leading = edges != null && edges.includes("leading");
9
+ const trailing = edges == null || edges.includes("trailing");
10
+ const invoke = () => {
11
+ if (pendingArgs !== null) {
12
+ func.apply(pendingThis, pendingArgs);
13
+ pendingThis = void 0;
14
+ pendingArgs = null;
15
+ }
16
+ };
17
+ const onTimerEnd = () => {
18
+ if (trailing) {
19
+ invoke();
20
+ }
21
+ cancel();
22
+ };
23
+ let timeoutId = null;
24
+ const schedule = () => {
25
+ if (timeoutId != null) {
26
+ clearTimeout(timeoutId);
27
+ }
28
+ timeoutId = setTimeout(() => {
29
+ timeoutId = null;
30
+ onTimerEnd();
31
+ }, debounceMs);
32
+ };
33
+ const cancelTimer = () => {
34
+ if (timeoutId !== null) {
35
+ clearTimeout(timeoutId);
36
+ timeoutId = null;
37
+ }
38
+ };
39
+ const cancel = () => {
40
+ cancelTimer();
41
+ pendingThis = void 0;
42
+ pendingArgs = null;
43
+ };
44
+ const debounced = function(...args) {
45
+ pendingThis = this;
46
+ pendingArgs = args;
47
+ const isFirstCall = timeoutId == null;
48
+ schedule();
49
+ if (leading && isFirstCall) {
50
+ invoke();
51
+ }
52
+ };
53
+ debounced.cancel = cancel;
54
+ return debounced;
55
+ }
56
+
57
+ // src/hooks/usePreservedCallback/usePreservedCallback.ts
58
+ import { useCallback, useEffect, useRef } from "react";
59
+ function usePreservedCallback(callback) {
60
+ const callbackRef = useRef(callback);
61
+ useEffect(() => {
62
+ callbackRef.current = callback;
63
+ }, [callback]);
64
+ return useCallback((...args) => {
65
+ return callbackRef.current(...args);
66
+ }, []);
67
+ }
68
+
69
+ // src/hooks/useDebouncedCallback/useDebouncedCallback.ts
70
+ function useDebouncedCallback({
71
+ onChange,
72
+ timeThreshold,
73
+ leading = false,
74
+ trailing = true
75
+ }) {
76
+ const handleChange = usePreservedCallback(onChange);
77
+ const ref = useRef2({ value: false, clearPreviousDebounce: () => {
78
+ } });
79
+ useEffect2(() => {
80
+ const current = ref.current;
81
+ return () => {
82
+ current.clearPreviousDebounce();
83
+ };
84
+ }, []);
85
+ const edges = useMemo(() => {
86
+ const _edges = [];
87
+ if (leading) {
88
+ _edges.push("leading");
89
+ }
90
+ if (trailing) {
91
+ _edges.push("trailing");
92
+ }
93
+ return _edges;
94
+ }, [leading, trailing]);
95
+ return useCallback2(
96
+ (nextValue) => {
97
+ if (nextValue === ref.current.value) {
98
+ return;
99
+ }
100
+ const debounced = debounce(
101
+ () => {
102
+ handleChange(nextValue);
103
+ ref.current.value = nextValue;
104
+ },
105
+ timeThreshold,
106
+ { edges }
107
+ );
108
+ ref.current.clearPreviousDebounce();
109
+ debounced();
110
+ ref.current.clearPreviousDebounce = debounced.cancel;
111
+ },
112
+ [handleChange, timeThreshold, edges]
113
+ );
114
+ }
115
+ export {
116
+ useDebouncedCallback
117
+ };
@@ -11,10 +11,10 @@ type UseDoubleClickProps<E extends HTMLElement> = {
11
11
  * It delays the single click callback execution for a specified time, and cancels it if a second click (i.e. a double click) occurs within that time.
12
12
  *
13
13
  * @template {HTMLElement} E - The specific type of HTMLElement to be used with this hook (e.g., HTMLButtonElement, HTMLDivElement).
14
- * @param {Object} params - Configuration options for click handling.
15
- * @param {number} [params.delay=250] - The number of milliseconds to wait before triggering the single click callback. Defaults to 250ms.
16
- * @param {(event: MouseEvent<E>) => void} [params.click] - The callback function to be executed on a single click.
17
- * @param {(event: MouseEvent<E>) => void} params.doubleClick - The callback function to be executed on a double click. Required.
14
+ * @param {Object} props - Configuration options for click handling.
15
+ * @param {number} [props.delay=250] - The number of milliseconds to wait before triggering the single click callback. Defaults to 250ms.
16
+ * @param {(event: MouseEvent<E>) => void} [props.click] - The callback function to be executed on a single click.
17
+ * @param {(event: MouseEvent<E>) => void} props.doubleClick - The callback function to be executed on a double click. Required.
18
18
  *
19
19
  * @returns {(event: MouseEvent<E>) => void} A click handler function to attach to an element's `onClick` event.
20
20
  *
@@ -0,0 +1,106 @@
1
+ declare class CustomGeoLocationError extends Error {
2
+ code: number;
3
+ constructor({ code, message }: {
4
+ message: string;
5
+ code: number;
6
+ });
7
+ }
8
+ type GeolocationData = {
9
+ latitude: number;
10
+ longitude: number;
11
+ accuracy: number;
12
+ altitude: number | null;
13
+ altitudeAccuracy: number | null;
14
+ heading: number | null;
15
+ speed: number | null;
16
+ timestamp: number;
17
+ };
18
+ declare const GeolocationMountBehavior: {
19
+ readonly GET: "get";
20
+ readonly WATCH: "watch";
21
+ };
22
+ type GeolocationMountBehaviorType = (typeof GeolocationMountBehavior)[keyof typeof GeolocationMountBehavior];
23
+ type GeolocationOptions = {
24
+ mountBehavior?: GeolocationMountBehaviorType;
25
+ } & PositionOptions;
26
+ /**
27
+ * @description
28
+ * `useGeolocation` is a React hook that retrieves and tracks the user's geographical location.
29
+ * It uses the browser's `Geolocation API` to support both one-time position retrieval and continuous location tracking.
30
+ *
31
+ * @param {GeolocationOptions} [options] - Geolocation options configuration
32
+ * @param {GeolocationMountBehaviorType} [options.mountBehavior] - How the hook behaves on mount:
33
+ * -- If not provided, no automatic location fetching occurs
34
+ * -- `get`: automatically fetches location once when component mounts
35
+ * -- `watch`: automatically starts tracking location changes when component mounts
36
+ * @param {boolean} [options.enableHighAccuracy=false] - If true, provides more accurate position information (increases battery consumption)
37
+ * @param {number} [options.maximumAge=0] - Maximum age in milliseconds of a cached position that is acceptable to return
38
+ * @param {number} [options.timeout=Infinity] - Maximum time (in milliseconds) allowed for the location request
39
+ *
40
+ * @returns {Object} Object containing location data and related functions
41
+ * - loading `boolean` - Whether location data is currently being fetched;
42
+ * - error `CustomGeoLocationError|null` - Error object if an error occurred, or null
43
+ * The hook uses standard Geolocation API error codes (`1-3`) and adds a custom code (`0`)
44
+ * : `0` - Geolocation is not supported by the environment
45
+ * : `1` - User denied permission to access geolocation
46
+ * : `2` - Position unavailable
47
+ * : `3` - Timeout - geolocation request took too long;
48
+ * - data `GeolocationData|null` - Location data object or null
49
+ * : latitude `number` - The latitude in decimal degrees
50
+ * : longitude `number` - The longitude in decimal degrees
51
+ * : accuracy `number` - The accuracy of position in meters
52
+ * : altitude `number|null` - The altitude in meters above the WGS84 ellipsoid
53
+ * : altitudeAccuracy `number|null` - The altitude accuracy in meters
54
+ * : heading `number|null` - The heading in degrees clockwise from true north
55
+ * : speed `number|null` - The speed in meters per second
56
+ * : timestamp `number` - The time when the position was retrieved;
57
+ * - getCurrentPosition `Function` - Function to get the current position once;
58
+ * - startTracking `Function` - Function to start tracking location changes;
59
+ * - stopTracking `Function` - Function to stop tracking location;
60
+ * - isTracking `boolean` - Whether location tracking is currently active;
61
+ *
62
+ * @example
63
+ * // Basic usage
64
+ * const {
65
+ * loading,
66
+ * error,
67
+ * data,
68
+ * getCurrentPosition
69
+ * } = useGeolocation();
70
+ *
71
+ * // Automatically fetch location when component mounts
72
+ * const {
73
+ * loading,
74
+ * error,
75
+ * data
76
+ * } = useGeolocation({ mountBehavior: 'get' });
77
+ *
78
+ * // Location tracking
79
+ * const {
80
+ * loading,
81
+ * error,
82
+ * data,
83
+ * startTracking,
84
+ * stopTracking,
85
+ * isTracking
86
+ * } = useGeolocation();
87
+ *
88
+ * const handleStartTracking = () => {
89
+ * startTracking();
90
+ * };
91
+ *
92
+ * const handleStopTracking = () => {
93
+ * stopTracking();
94
+ * };
95
+ */
96
+ declare function useGeolocation(options?: GeolocationOptions): {
97
+ getCurrentPosition: () => void;
98
+ startTracking: () => void;
99
+ stopTracking: () => void;
100
+ isTracking: boolean;
101
+ loading: boolean;
102
+ error: CustomGeoLocationError | null;
103
+ data: GeolocationData | null;
104
+ };
105
+
106
+ export { useGeolocation };
@@ -0,0 +1,126 @@
1
+ // src/hooks/useGeolocation/useGeolocation.ts
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ var CustomGeoLocationError = class extends Error {
4
+ code;
5
+ constructor({ code, message }) {
6
+ super(message);
7
+ this.name = "CustomGeoLocationError";
8
+ this.code = code;
9
+ }
10
+ };
11
+ var GeolocationMountBehavior = {
12
+ GET: "get",
13
+ WATCH: "watch"
14
+ };
15
+ function useGeolocation(options) {
16
+ const [state, setState] = useState({
17
+ loading: !!options?.mountBehavior,
18
+ error: null,
19
+ data: null
20
+ });
21
+ const [isTracking, setIsTracking] = useState(false);
22
+ const watchIdRef = useRef(null);
23
+ const checkGeolocationSupport = useCallback(() => {
24
+ if (typeof window === "undefined" || navigator.geolocation === void 0) {
25
+ setState((prev) => ({
26
+ ...prev,
27
+ loading: false,
28
+ error: new CustomGeoLocationError({
29
+ code: 0,
30
+ message: "Geolocation is not supported by this environment."
31
+ })
32
+ }));
33
+ return false;
34
+ }
35
+ return true;
36
+ }, []);
37
+ const handleSuccess = useCallback((position) => {
38
+ const { coords } = position;
39
+ setState((prev) => ({
40
+ ...prev,
41
+ loading: false,
42
+ error: null,
43
+ data: {
44
+ latitude: coords.latitude,
45
+ longitude: coords.longitude,
46
+ accuracy: coords.accuracy,
47
+ altitude: coords.altitude,
48
+ altitudeAccuracy: coords.altitudeAccuracy,
49
+ heading: coords.heading,
50
+ speed: coords.speed,
51
+ timestamp: position.timestamp
52
+ }
53
+ }));
54
+ }, []);
55
+ const handleError = useCallback((error) => {
56
+ const { code, message } = error;
57
+ setState((prev) => ({
58
+ ...prev,
59
+ loading: false,
60
+ error: new CustomGeoLocationError({ code, message })
61
+ }));
62
+ }, []);
63
+ const getGeolocationOptions = useCallback(
64
+ () => ({
65
+ enableHighAccuracy: options?.enableHighAccuracy,
66
+ maximumAge: options?.maximumAge,
67
+ timeout: options?.timeout
68
+ }),
69
+ [options?.enableHighAccuracy, options?.maximumAge, options?.timeout]
70
+ );
71
+ const getCurrentPosition = useCallback(() => {
72
+ if (!checkGeolocationSupport()) {
73
+ return;
74
+ }
75
+ setState((prev) => ({ ...prev, loading: true }));
76
+ navigator.geolocation.getCurrentPosition(handleSuccess, handleError, getGeolocationOptions());
77
+ }, [handleSuccess, handleError, getGeolocationOptions, checkGeolocationSupport]);
78
+ const startTracking = useCallback(() => {
79
+ if (!checkGeolocationSupport()) {
80
+ return;
81
+ }
82
+ if (watchIdRef.current !== null) {
83
+ navigator.geolocation.clearWatch(watchIdRef.current);
84
+ }
85
+ setState((prev) => ({ ...prev, loading: true }));
86
+ watchIdRef.current = navigator.geolocation.watchPosition(
87
+ (position) => {
88
+ setIsTracking(true);
89
+ handleSuccess(position);
90
+ },
91
+ handleError,
92
+ getGeolocationOptions()
93
+ );
94
+ }, [handleSuccess, handleError, getGeolocationOptions, checkGeolocationSupport]);
95
+ const stopTracking = useCallback(() => {
96
+ if (watchIdRef.current === null) {
97
+ return;
98
+ }
99
+ navigator.geolocation.clearWatch(watchIdRef.current);
100
+ watchIdRef.current = null;
101
+ setIsTracking(false);
102
+ }, []);
103
+ useEffect(() => {
104
+ if (options?.mountBehavior === GeolocationMountBehavior.WATCH) {
105
+ startTracking();
106
+ } else if (options?.mountBehavior === GeolocationMountBehavior.GET) {
107
+ getCurrentPosition();
108
+ }
109
+ return () => {
110
+ if (watchIdRef.current !== null) {
111
+ navigator.geolocation.clearWatch(watchIdRef.current);
112
+ watchIdRef.current = null;
113
+ }
114
+ };
115
+ }, [options?.mountBehavior, getCurrentPosition, startTracking]);
116
+ return {
117
+ ...state,
118
+ getCurrentPosition,
119
+ startTracking,
120
+ stopTracking,
121
+ isTracking
122
+ };
123
+ }
124
+ export {
125
+ useGeolocation
126
+ };
@@ -7,8 +7,8 @@ type UseImpressionRefOptions = Partial<{
7
7
  }>;
8
8
  /**
9
9
  * @description
10
- * `useImpressionRef` is a custom hook that measures the time a specific DOM element is visible on the screen and executes callbacks when the element enters or exits the viewport.
11
- * This hook uses `IntersectionObserver` and the `Visibility API` to track the element's visibility.
10
+ * `useImpressionRef` is a React hook that measures the time a specific DOM element is visible on the screen and executes callbacks when the element enters or exits the viewport.
11
+ * It uses `IntersectionObserver` and the `Visibility API` to track the element's visibility.
12
12
  *
13
13
  * @param {UseImpressionRefOptions} options - Options for tracking the element's visibility.
14
14
  * @param {() => void} [options.onImpressionStart] - Callback function executed when the element enters the view
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @description
3
- * `useIntersectionObserver` is a custom hook that detects whether a specific DOM element is visible on the screen.
4
- * This hook uses the `IntersectionObserver` API to execute a callback when the element enters or exits the viewport.
3
+ * `useIntersectionObserver` is a React hook that detects whether a specific DOM element is visible on the screen.
4
+ * It uses the `IntersectionObserver` API to execute a callback when the element enters or exits the viewport.
5
5
  *
6
6
  * @param {(entry: IntersectionObserverEntry) => void} callback - A callback function that is executed when the visibility of the element changes.
7
7
  * You can check `entry.isIntersecting` to determine if the element is in view.
@@ -2,11 +2,11 @@ import { useEffect } from 'react';
2
2
 
3
3
  /**
4
4
  * @description
5
- * During SSR, there is no DOM to synchronously measure or mutate, so React warns about using useLayoutEffect.
5
+ * `useIsomorphicLayoutEffect` is a React hook that provides the behavior of `useLayoutEffect` without triggering warnings during server-side rendering.
6
+ * During SSR, there is no DOM to synchronously measure or mutate, so React warns about using `useLayoutEffect`.
6
7
  *
7
- * This hook provides the behavior of useLayoutEffect in the browser without triggering SSR warnings.
8
+ * This hook runs synchronously after DOM updates but before paint, making it ideal for:
8
9
  *
9
- * It runs synchronously after DOM updates but before paint, making it ideal for:
10
10
  * - Measuring DOM elements after render
11
11
  * - Applying DOM changes before paint
12
12
  * - Preventing UI flashes or layout shifts