@versini/ui-hooks 5.3.2 → 6.0.1
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 +35 -9
- package/dist/__tests__/__mocks__/ResizeObserver.d.ts +21 -0
- package/dist/__tests__/useClickOutside.test.d.ts +1 -0
- package/dist/__tests__/useHaptic.test.d.ts +1 -0
- package/dist/__tests__/useHotkeys.test.d.ts +1 -0
- package/dist/__tests__/useInterval.test.d.ts +1 -0
- package/dist/__tests__/useIsMounted.test.d.ts +1 -0
- package/dist/__tests__/useLocalStorage.test.d.ts +1 -0
- package/dist/__tests__/useMergeRefs.test.d.ts +1 -0
- package/dist/__tests__/useResizeObserver.test.d.ts +1 -0
- package/dist/__tests__/useUncontrolled.test.d.ts +1 -0
- package/dist/__tests__/useUniqueId.test.d.ts +1 -0
- package/dist/__tests__/useViewportSize.test.d.ts +1 -0
- package/dist/__tests__/utilities.test.d.ts +1 -0
- package/dist/useClickOutside/useClickOutside.d.ts +17 -0
- package/dist/useClickOutside/useClickOutside.js +68 -0
- package/dist/useHaptic/useHaptic.d.ts +24 -0
- package/dist/useHaptic/useHaptic.js +200 -0
- package/dist/useHotkeys/useHotkeys.d.ts +10 -0
- package/dist/useHotkeys/useHotkeys.js +65 -0
- package/dist/useHotkeys/utilities.d.ts +19 -0
- package/dist/useHotkeys/utilities.js +87 -0
- package/dist/useInViewport/useInViewport.d.ts +10 -0
- package/dist/useInViewport/useInViewport.js +52 -0
- package/dist/useInterval/useInterval.d.ts +21 -0
- package/dist/useInterval/useInterval.js +75 -0
- package/dist/useIsMounted/useIsMounted.d.ts +13 -0
- package/dist/useIsMounted/useIsMounted.js +46 -0
- package/dist/useLocalStorage/useLocalStorage.d.ts +82 -0
- package/dist/useLocalStorage/useLocalStorage.js +236 -0
- package/dist/useMergeRefs/useMergeRefs.d.ts +20 -0
- package/dist/useMergeRefs/useMergeRefs.js +62 -0
- package/dist/useResizeObserver/useResizeObserver.d.ts +17 -0
- package/dist/useResizeObserver/useResizeObserver.js +94 -0
- package/dist/useUncontrolled/useUncontrolled.d.ts +14 -0
- package/dist/useUncontrolled/useUncontrolled.js +76 -0
- package/dist/useUniqueId/useUniqueId.d.ts +36 -0
- package/dist/useUniqueId/useUniqueId.js +41 -0
- package/dist/useViewportSize/useViewportSize.d.ts +13 -0
- package/dist/useViewportSize/useViewportSize.js +60 -0
- package/dist/useVisualViewportSize/useVisualViewportSize.d.ts +14 -0
- package/dist/useVisualViewportSize/useVisualViewportSize.js +70 -0
- package/package.json +56 -4
- package/dist/index.d.ts +0 -316
- package/dist/index.js +0 -958
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-hooks v6.0.1
|
|
3
|
+
© 2025 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
try {
|
|
6
|
+
if (!window.__VERSINI_UI_HOOKS__) {
|
|
7
|
+
window.__VERSINI_UI_HOOKS__ = {
|
|
8
|
+
version: "6.0.1",
|
|
9
|
+
buildTime: "12/24/2025 09:19 AM EST",
|
|
10
|
+
homepage: "https://www.npmjs.com/package/@versini/ui-hooks",
|
|
11
|
+
license: "MIT",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
} catch (error) {
|
|
15
|
+
// nothing to declare officer
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
import { useCallback, useRef, useState } from "react";
|
|
19
|
+
|
|
20
|
+
;// CONCATENATED MODULE: external "react"
|
|
21
|
+
|
|
22
|
+
;// CONCATENATED MODULE: ./src/hooks/useInViewport/useInViewport.ts
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Hook that checks if an element is visible in the viewport.
|
|
26
|
+
* @returns
|
|
27
|
+
* ref: React ref object to attach to the element you want to monitor.
|
|
28
|
+
* inViewport: Boolean indicating if the element is in the viewport.
|
|
29
|
+
*/ /* v8 ignore start */ function useInViewport() {
|
|
30
|
+
const observer = useRef(null);
|
|
31
|
+
const [inViewport, setInViewport] = useState(false);
|
|
32
|
+
const ref = useCallback((node)=>{
|
|
33
|
+
if (typeof IntersectionObserver !== "undefined") {
|
|
34
|
+
if (node && !observer.current) {
|
|
35
|
+
observer.current = new IntersectionObserver((entries)=>setInViewport(entries.some((entry)=>entry.isIntersecting)));
|
|
36
|
+
} else {
|
|
37
|
+
observer.current?.disconnect();
|
|
38
|
+
}
|
|
39
|
+
if (node) {
|
|
40
|
+
observer.current?.observe(node);
|
|
41
|
+
} else {
|
|
42
|
+
setInViewport(false);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, []);
|
|
46
|
+
return {
|
|
47
|
+
ref,
|
|
48
|
+
inViewport
|
|
49
|
+
};
|
|
50
|
+
} /* v8 ignore stop */
|
|
51
|
+
|
|
52
|
+
export { useInViewport };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom hook to call a function within a given interval.
|
|
3
|
+
*
|
|
4
|
+
* @param fn Callback function to be executed at each interval
|
|
5
|
+
* @param interval Interval time in milliseconds
|
|
6
|
+
* @returns An object containing start, stop, and active state
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const { start, stop, active } = useInterval(() => {
|
|
10
|
+
* console.log("Interval executed");
|
|
11
|
+
* }, 1000);
|
|
12
|
+
* start(); // To start the interval
|
|
13
|
+
* stop(); // To stop the interval
|
|
14
|
+
* console.log(active); // To check if the interval is active
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
export declare function useInterval(fn: () => void, interval: number): {
|
|
18
|
+
start: () => void;
|
|
19
|
+
stop: () => void;
|
|
20
|
+
active: boolean;
|
|
21
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-hooks v6.0.1
|
|
3
|
+
© 2025 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
try {
|
|
6
|
+
if (!window.__VERSINI_UI_HOOKS__) {
|
|
7
|
+
window.__VERSINI_UI_HOOKS__ = {
|
|
8
|
+
version: "6.0.1",
|
|
9
|
+
buildTime: "12/24/2025 09:19 AM EST",
|
|
10
|
+
homepage: "https://www.npmjs.com/package/@versini/ui-hooks",
|
|
11
|
+
license: "MIT",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
} catch (error) {
|
|
15
|
+
// nothing to declare officer
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
19
|
+
|
|
20
|
+
;// CONCATENATED MODULE: external "react"
|
|
21
|
+
|
|
22
|
+
;// CONCATENATED MODULE: ./src/hooks/useInterval/useInterval.ts
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Custom hook to call a function within a given interval.
|
|
26
|
+
*
|
|
27
|
+
* @param fn Callback function to be executed at each interval
|
|
28
|
+
* @param interval Interval time in milliseconds
|
|
29
|
+
* @returns An object containing start, stop, and active state
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const { start, stop, active } = useInterval(() => {
|
|
33
|
+
* console.log("Interval executed");
|
|
34
|
+
* }, 1000);
|
|
35
|
+
* start(); // To start the interval
|
|
36
|
+
* stop(); // To stop the interval
|
|
37
|
+
* console.log(active); // To check if the interval is active
|
|
38
|
+
*
|
|
39
|
+
*/ function useInterval(fn, interval) {
|
|
40
|
+
const [active, setActive] = useState(false);
|
|
41
|
+
const intervalRef = useRef(null);
|
|
42
|
+
const fnRef = useRef(null);
|
|
43
|
+
const start = useCallback(()=>{
|
|
44
|
+
setActive((old)=>{
|
|
45
|
+
/* v8 ignore start - interval reactivation edge case */ if (!old && (!intervalRef.current || intervalRef.current === -1)) {
|
|
46
|
+
intervalRef.current = window.setInterval(fnRef.current, interval);
|
|
47
|
+
}
|
|
48
|
+
/* v8 ignore stop */ return true;
|
|
49
|
+
});
|
|
50
|
+
}, [
|
|
51
|
+
interval
|
|
52
|
+
]);
|
|
53
|
+
const stop = useCallback(()=>{
|
|
54
|
+
setActive(false);
|
|
55
|
+
window.clearInterval(intervalRef.current || -1);
|
|
56
|
+
intervalRef.current = -1;
|
|
57
|
+
}, []);
|
|
58
|
+
useEffect(()=>{
|
|
59
|
+
fnRef.current = fn;
|
|
60
|
+
active && start();
|
|
61
|
+
return stop;
|
|
62
|
+
}, [
|
|
63
|
+
fn,
|
|
64
|
+
active,
|
|
65
|
+
start,
|
|
66
|
+
stop
|
|
67
|
+
]);
|
|
68
|
+
return {
|
|
69
|
+
start,
|
|
70
|
+
stop,
|
|
71
|
+
active
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { useInterval };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom hook that returns a function indicating whether the component
|
|
3
|
+
* is mounted or not.
|
|
4
|
+
*
|
|
5
|
+
* @returns A function that returns a boolean value indicating whether
|
|
6
|
+
* the component is mounted.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const isMounted = useIsMounted();
|
|
10
|
+
* console.log(isMounted()); // true
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
export declare function useIsMounted(): () => boolean;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-hooks v6.0.1
|
|
3
|
+
© 2025 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
try {
|
|
6
|
+
if (!window.__VERSINI_UI_HOOKS__) {
|
|
7
|
+
window.__VERSINI_UI_HOOKS__ = {
|
|
8
|
+
version: "6.0.1",
|
|
9
|
+
buildTime: "12/24/2025 09:19 AM EST",
|
|
10
|
+
homepage: "https://www.npmjs.com/package/@versini/ui-hooks",
|
|
11
|
+
license: "MIT",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
} catch (error) {
|
|
15
|
+
// nothing to declare officer
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
19
|
+
|
|
20
|
+
;// CONCATENATED MODULE: external "react"
|
|
21
|
+
|
|
22
|
+
;// CONCATENATED MODULE: ./src/hooks/useIsMounted/useIsMounted.ts
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Custom hook that returns a function indicating whether the component
|
|
26
|
+
* is mounted or not.
|
|
27
|
+
*
|
|
28
|
+
* @returns A function that returns a boolean value indicating whether
|
|
29
|
+
* the component is mounted.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const isMounted = useIsMounted();
|
|
33
|
+
* console.log(isMounted()); // true
|
|
34
|
+
*
|
|
35
|
+
*/ function useIsMounted() {
|
|
36
|
+
const isMounted = useRef(false);
|
|
37
|
+
useEffect(()=>{
|
|
38
|
+
isMounted.current = true;
|
|
39
|
+
return ()=>{
|
|
40
|
+
isMounted.current = false;
|
|
41
|
+
};
|
|
42
|
+
}, []);
|
|
43
|
+
return useCallback(()=>isMounted.current, []);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { useIsMounted };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration properties for the useLocalStorage hook.
|
|
3
|
+
*
|
|
4
|
+
* @template T - The type of the value stored in localStorage
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
export interface StorageProperties<T> {
|
|
8
|
+
/**
|
|
9
|
+
* The localStorage key under which the value will be stored. Must be unique
|
|
10
|
+
* within your application to avoid conflicts.
|
|
11
|
+
*/
|
|
12
|
+
key: string;
|
|
13
|
+
/**
|
|
14
|
+
* Optional default value that will be set in localStorage if no value exists.
|
|
15
|
+
* This value will be used on initial mount and when resetValue() is called.
|
|
16
|
+
*/
|
|
17
|
+
initialValue?: T;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A React hook for managing state synchronized with localStorage. Uses React
|
|
21
|
+
* 19's useSyncExternalStore for optimal concurrent rendering support and
|
|
22
|
+
* automatic synchronization across components. Changes are automatically
|
|
23
|
+
* persisted to localStorage and synchronized across all components using the
|
|
24
|
+
* same key, including across browser tabs.
|
|
25
|
+
*
|
|
26
|
+
* Features:
|
|
27
|
+
* - Automatic serialization/deserialization with JSON
|
|
28
|
+
* - Type-safe with TypeScript generics
|
|
29
|
+
* - Supports functional updates (similar to setState)
|
|
30
|
+
* - Synchronized across components and browser tabs
|
|
31
|
+
* - Handles edge cases (null, undefined, errors)
|
|
32
|
+
* - Compatible with React 19+ concurrent features
|
|
33
|
+
*
|
|
34
|
+
* @template T - The type of the value stored in localStorage
|
|
35
|
+
* @param {StorageProperties<T>} config - Configuration object with key and optional initialValue
|
|
36
|
+
* @returns {[T | null, (value: T | ((current: T) => T)) => void, () => void, () => void]} A tuple containing:
|
|
37
|
+
* - [0] current value from localStorage (or null if not set)
|
|
38
|
+
* - [1] setValue function to update the stored value (supports direct value or function updater)
|
|
39
|
+
* - [2] resetValue function to restore the initialValue
|
|
40
|
+
* - [3] removeValue function to remove the value from localStorage
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```js
|
|
44
|
+
* // Basic usage with a string value
|
|
45
|
+
* import { useLocalStorage } from '@versini/ui-hooks';
|
|
46
|
+
* const [model, setModel, resetModel, removeModel] = useLocalStorage({
|
|
47
|
+
* key: 'gpt-model',
|
|
48
|
+
* initialValue: 'gpt-3',
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* // Direct update
|
|
52
|
+
* setModel('gpt-4'); // Stores "gpt-4"
|
|
53
|
+
*
|
|
54
|
+
* // Functional update (receives current value)
|
|
55
|
+
* setModel((current) => (current === 'gpt-3' ? 'gpt-4' : 'gpt-3'));
|
|
56
|
+
*
|
|
57
|
+
* // Reset to initial value
|
|
58
|
+
* resetModel(); // Restores "gpt-3"
|
|
59
|
+
*
|
|
60
|
+
* // Remove from localStorage
|
|
61
|
+
* removeModel(); // Sets value to null and removes from storage
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```js
|
|
66
|
+
* // Usage with complex objects
|
|
67
|
+
* interface UserPreferences {
|
|
68
|
+
* theme: 'light' | 'dark';
|
|
69
|
+
* fontSize: number;
|
|
70
|
+
* }
|
|
71
|
+
*
|
|
72
|
+
* const [prefs, setPrefs] = useLocalStorage<UserPreferences>({
|
|
73
|
+
* key: 'user-preferences',
|
|
74
|
+
* initialValue: { theme: 'light', fontSize: 14 }
|
|
75
|
+
* });
|
|
76
|
+
*
|
|
77
|
+
* // Update specific property
|
|
78
|
+
* setPrefs(current => ({ ...current, theme: 'dark' }));
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
*/
|
|
82
|
+
export declare function useLocalStorage<T>({ key, initialValue, }: StorageProperties<T>): any[];
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-hooks v6.0.1
|
|
3
|
+
© 2025 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
try {
|
|
6
|
+
if (!window.__VERSINI_UI_HOOKS__) {
|
|
7
|
+
window.__VERSINI_UI_HOOKS__ = {
|
|
8
|
+
version: "6.0.1",
|
|
9
|
+
buildTime: "12/24/2025 09:19 AM EST",
|
|
10
|
+
homepage: "https://www.npmjs.com/package/@versini/ui-hooks",
|
|
11
|
+
license: "MIT",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
} catch (error) {
|
|
15
|
+
// nothing to declare officer
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
import { useCallback, useEffect, useSyncExternalStore } from "react";
|
|
19
|
+
|
|
20
|
+
;// CONCATENATED MODULE: external "react"
|
|
21
|
+
|
|
22
|
+
;// CONCATENATED MODULE: ./src/hooks/useLocalStorage/useLocalStorage.ts
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Dispatches a custom storage event to notify other parts of the application
|
|
26
|
+
* about localStorage changes. This is necessary because the native storage
|
|
27
|
+
* event only fires in other tabs/windows, not in the current one. By manually
|
|
28
|
+
* dispatching the event, we ensure that all components using the same key stay
|
|
29
|
+
* synchronized.
|
|
30
|
+
*
|
|
31
|
+
* @param key - The localStorage key that was modified
|
|
32
|
+
* @param newValue - The new value (stringified) or null if the item was removed
|
|
33
|
+
*
|
|
34
|
+
*/ function dispatchStorageEvent(key, newValue) {
|
|
35
|
+
window.dispatchEvent(new StorageEvent("storage", {
|
|
36
|
+
key,
|
|
37
|
+
newValue
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sets an item in localStorage and dispatches a storage event. Handles both
|
|
42
|
+
* direct values and function updaters (similar to React's setState). Values are
|
|
43
|
+
* automatically serialized to JSON before storage.
|
|
44
|
+
*
|
|
45
|
+
* @param key - The localStorage key to set
|
|
46
|
+
* @param value - The value to store, or a function that returns the value to store
|
|
47
|
+
*
|
|
48
|
+
*/ const setLocalStorageItem = (key, value)=>{
|
|
49
|
+
/**
|
|
50
|
+
* If value is a function, call it to get the actual value (supports functional
|
|
51
|
+
* updates).
|
|
52
|
+
*/ const stringifiedValue = JSON.stringify(typeof value === "function" ? value() : value);
|
|
53
|
+
window.localStorage.setItem(key, stringifiedValue);
|
|
54
|
+
// Dispatch event to notify other components in the same window/tab.
|
|
55
|
+
dispatchStorageEvent(key, stringifiedValue);
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Removes an item from localStorage and dispatches a storage event with null
|
|
59
|
+
* value. This ensures all components using this key are notified of the
|
|
60
|
+
* removal.
|
|
61
|
+
*
|
|
62
|
+
* @param key - The localStorage key to remove
|
|
63
|
+
*
|
|
64
|
+
*/ const removeLocalStorageItem = (key)=>{
|
|
65
|
+
window.localStorage.removeItem(key);
|
|
66
|
+
// Dispatch event with null to signal removal.
|
|
67
|
+
dispatchStorageEvent(key, null);
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Retrieves an item from localStorage. Returns the raw stringified value or
|
|
71
|
+
* null if the key doesn't exist.
|
|
72
|
+
*
|
|
73
|
+
* @param key - The localStorage key to retrieve
|
|
74
|
+
* @returns The stored string value or null if not found
|
|
75
|
+
*
|
|
76
|
+
*/ const getLocalStorageItem = (key)=>{
|
|
77
|
+
return window.localStorage.getItem(key);
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Creates a subscription to localStorage changes for use with React's
|
|
81
|
+
* useSyncExternalStore. This function is called by React to set up and tear
|
|
82
|
+
* down event listeners. It listens to both native storage events (from other
|
|
83
|
+
* tabs) and custom dispatched events (from this tab).
|
|
84
|
+
*
|
|
85
|
+
* @param callback - The callback function to invoke when storage changes occur
|
|
86
|
+
* @returns A cleanup function that removes the event listener
|
|
87
|
+
*
|
|
88
|
+
*/ const useLocalStorageSubscribe = (callback)=>{
|
|
89
|
+
window.addEventListener("storage", callback);
|
|
90
|
+
// Return cleanup function for React to call on unmount.
|
|
91
|
+
return ()=>window.removeEventListener("storage", callback);
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* A React hook for managing state synchronized with localStorage. Uses React
|
|
95
|
+
* 19's useSyncExternalStore for optimal concurrent rendering support and
|
|
96
|
+
* automatic synchronization across components. Changes are automatically
|
|
97
|
+
* persisted to localStorage and synchronized across all components using the
|
|
98
|
+
* same key, including across browser tabs.
|
|
99
|
+
*
|
|
100
|
+
* Features:
|
|
101
|
+
* - Automatic serialization/deserialization with JSON
|
|
102
|
+
* - Type-safe with TypeScript generics
|
|
103
|
+
* - Supports functional updates (similar to setState)
|
|
104
|
+
* - Synchronized across components and browser tabs
|
|
105
|
+
* - Handles edge cases (null, undefined, errors)
|
|
106
|
+
* - Compatible with React 19+ concurrent features
|
|
107
|
+
*
|
|
108
|
+
* @template T - The type of the value stored in localStorage
|
|
109
|
+
* @param {StorageProperties<T>} config - Configuration object with key and optional initialValue
|
|
110
|
+
* @returns {[T | null, (value: T | ((current: T) => T)) => void, () => void, () => void]} A tuple containing:
|
|
111
|
+
* - [0] current value from localStorage (or null if not set)
|
|
112
|
+
* - [1] setValue function to update the stored value (supports direct value or function updater)
|
|
113
|
+
* - [2] resetValue function to restore the initialValue
|
|
114
|
+
* - [3] removeValue function to remove the value from localStorage
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```js
|
|
118
|
+
* // Basic usage with a string value
|
|
119
|
+
* import { useLocalStorage } from '@versini/ui-hooks';
|
|
120
|
+
* const [model, setModel, resetModel, removeModel] = useLocalStorage({
|
|
121
|
+
* key: 'gpt-model',
|
|
122
|
+
* initialValue: 'gpt-3',
|
|
123
|
+
* });
|
|
124
|
+
*
|
|
125
|
+
* // Direct update
|
|
126
|
+
* setModel('gpt-4'); // Stores "gpt-4"
|
|
127
|
+
*
|
|
128
|
+
* // Functional update (receives current value)
|
|
129
|
+
* setModel((current) => (current === 'gpt-3' ? 'gpt-4' : 'gpt-3'));
|
|
130
|
+
*
|
|
131
|
+
* // Reset to initial value
|
|
132
|
+
* resetModel(); // Restores "gpt-3"
|
|
133
|
+
*
|
|
134
|
+
* // Remove from localStorage
|
|
135
|
+
* removeModel(); // Sets value to null and removes from storage
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```js
|
|
140
|
+
* // Usage with complex objects
|
|
141
|
+
* interface UserPreferences {
|
|
142
|
+
* theme: 'light' | 'dark';
|
|
143
|
+
* fontSize: number;
|
|
144
|
+
* }
|
|
145
|
+
*
|
|
146
|
+
* const [prefs, setPrefs] = useLocalStorage<UserPreferences>({
|
|
147
|
+
* key: 'user-preferences',
|
|
148
|
+
* initialValue: { theme: 'light', fontSize: 14 }
|
|
149
|
+
* });
|
|
150
|
+
*
|
|
151
|
+
* // Update specific property
|
|
152
|
+
* setPrefs(current => ({ ...current, theme: 'dark' }));
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
*/ function useLocalStorage({ key, initialValue }) {
|
|
156
|
+
/**
|
|
157
|
+
* Snapshot function for useSyncExternalStore - returns current localStorage
|
|
158
|
+
* value.
|
|
159
|
+
*/ const getSnapshot = ()=>getLocalStorageItem(key);
|
|
160
|
+
/**
|
|
161
|
+
* Use React's useSyncExternalStore to subscribe to localStorage changes. This
|
|
162
|
+
* ensures proper integration with React 19+ concurrent rendering and provides
|
|
163
|
+
* automatic re-rendering when the stored value changes (either from this
|
|
164
|
+
* component, other components, or other browser tabs).
|
|
165
|
+
*/ const store = useSyncExternalStore(useLocalStorageSubscribe, getSnapshot);
|
|
166
|
+
/**
|
|
167
|
+
* Updates the stored value in localStorage. Accepts either a direct value or a
|
|
168
|
+
* function that receives the current value. Setting to null or undefined will
|
|
169
|
+
* remove the item from localStorage.
|
|
170
|
+
*/ const setValue = useCallback((v)=>{
|
|
171
|
+
try {
|
|
172
|
+
// Support both direct values and functional updates.
|
|
173
|
+
const nextState = typeof v === "function" ? v(JSON.parse(store)) : v;
|
|
174
|
+
// Remove from storage if value is null or undefined.
|
|
175
|
+
if (nextState === undefined || nextState === null) {
|
|
176
|
+
removeLocalStorageItem(key);
|
|
177
|
+
} else {
|
|
178
|
+
setLocalStorageItem(key, nextState);
|
|
179
|
+
}
|
|
180
|
+
/* v8 ignore start */ } catch (e) {
|
|
181
|
+
// Log parsing or storage errors without breaking the application.
|
|
182
|
+
console.warn(e);
|
|
183
|
+
}
|
|
184
|
+
/* v8 ignore stop */ }, [
|
|
185
|
+
key,
|
|
186
|
+
store
|
|
187
|
+
]);
|
|
188
|
+
/**
|
|
189
|
+
* Resets the stored value back to the initialValue provided in the
|
|
190
|
+
* configuration. If no initialValue was provided, this will remove the item
|
|
191
|
+
* from localStorage.
|
|
192
|
+
*/ const resetValue = useCallback(()=>{
|
|
193
|
+
setValue(initialValue);
|
|
194
|
+
}, [
|
|
195
|
+
initialValue,
|
|
196
|
+
setValue
|
|
197
|
+
]);
|
|
198
|
+
/**
|
|
199
|
+
* Removes the value from localStorage entirely. After calling this, the hook
|
|
200
|
+
* will return null until a new value is set.
|
|
201
|
+
*/ const removeValue = useCallback(()=>{
|
|
202
|
+
setValue(null);
|
|
203
|
+
}, [
|
|
204
|
+
setValue
|
|
205
|
+
]);
|
|
206
|
+
/**
|
|
207
|
+
* Initialize localStorage with the initialValue on first mount if the key
|
|
208
|
+
* doesn't exist. This effect only runs once when the component mounts and
|
|
209
|
+
* ensures that the initialValue is persisted to localStorage if no value is
|
|
210
|
+
* currently stored.
|
|
211
|
+
*/ useEffect(()=>{
|
|
212
|
+
try {
|
|
213
|
+
// Only set initialValue if key doesn't exist and initialValue is defined.
|
|
214
|
+
if (getLocalStorageItem(key) === null && typeof initialValue !== "undefined") {
|
|
215
|
+
setLocalStorageItem(key, initialValue);
|
|
216
|
+
}
|
|
217
|
+
/* v8 ignore start */ } catch (e) {
|
|
218
|
+
// Log initialization errors without breaking the application.
|
|
219
|
+
console.warn(e);
|
|
220
|
+
}
|
|
221
|
+
/* v8 ignore stop */ }, [
|
|
222
|
+
key,
|
|
223
|
+
initialValue
|
|
224
|
+
]);
|
|
225
|
+
/**
|
|
226
|
+
* Return tuple: [currentValue, setValue, resetValue, removeValue] Parse the
|
|
227
|
+
* stored JSON string back to its original type, or return null if empty.
|
|
228
|
+
*/ return [
|
|
229
|
+
store ? JSON.parse(store) : null,
|
|
230
|
+
setValue,
|
|
231
|
+
resetValue,
|
|
232
|
+
removeValue
|
|
233
|
+
];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export { useLocalStorage };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React utility to merge refs.
|
|
3
|
+
*
|
|
4
|
+
* When developing low level UI components, it is common to have to use a local
|
|
5
|
+
* ref but also support an external one using React.forwardRef. Natively, React
|
|
6
|
+
* does not offer a way to set two refs inside the ref property.
|
|
7
|
+
*
|
|
8
|
+
* @param Array of refs (object, function, etc.)
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
*
|
|
12
|
+
* const Example = React.forwardRef(function Example(props, ref) {
|
|
13
|
+
* const localRef = React.useRef();
|
|
14
|
+
* const mergedRefs = useMergeRefs([localRef, ref]);
|
|
15
|
+
*
|
|
16
|
+
* return <div ref={mergedRefs} />;
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
export declare function useMergeRefs<T = any>(refs: Array<React.MutableRefObject<T> | React.LegacyRef<T> | undefined | null>): React.RefCallback<T>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
@versini/ui-hooks v6.0.1
|
|
3
|
+
© 2025 gizmette.com
|
|
4
|
+
*/
|
|
5
|
+
try {
|
|
6
|
+
if (!window.__VERSINI_UI_HOOKS__) {
|
|
7
|
+
window.__VERSINI_UI_HOOKS__ = {
|
|
8
|
+
version: "6.0.1",
|
|
9
|
+
buildTime: "12/24/2025 09:19 AM EST",
|
|
10
|
+
homepage: "https://www.npmjs.com/package/@versini/ui-hooks",
|
|
11
|
+
license: "MIT",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
} catch (error) {
|
|
15
|
+
// nothing to declare officer
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
import { useMemo } from "react";
|
|
19
|
+
|
|
20
|
+
;// CONCATENATED MODULE: external "react"
|
|
21
|
+
|
|
22
|
+
;// CONCATENATED MODULE: ./src/hooks/useMergeRefs/useMergeRefs.ts
|
|
23
|
+
/**
|
|
24
|
+
* React utility to merge refs.
|
|
25
|
+
*
|
|
26
|
+
* When developing low level UI components, it is common to have to use a local
|
|
27
|
+
* ref but also support an external one using React.forwardRef. Natively, React
|
|
28
|
+
* does not offer a way to set two refs inside the ref property.
|
|
29
|
+
*
|
|
30
|
+
* @param Array of refs (object, function, etc.)
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
*
|
|
34
|
+
* const Example = React.forwardRef(function Example(props, ref) {
|
|
35
|
+
* const localRef = React.useRef();
|
|
36
|
+
* const mergedRefs = useMergeRefs([localRef, ref]);
|
|
37
|
+
*
|
|
38
|
+
* return <div ref={mergedRefs} />;
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
*/
|
|
42
|
+
function useMergeRefs(refs) {
|
|
43
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: refs array is used as dependency but spread for proper comparison
|
|
44
|
+
return useMemo(()=>{
|
|
45
|
+
/* v8 ignore start - all refs null edge case */ if (refs.every((ref)=>ref == null)) {
|
|
46
|
+
return ()=>{};
|
|
47
|
+
}
|
|
48
|
+
/* v8 ignore stop */ /* v8 ignore start - ref assignment handling */ return (value)=>{
|
|
49
|
+
refs.forEach((ref)=>{
|
|
50
|
+
if (typeof ref === "function") {
|
|
51
|
+
ref(value);
|
|
52
|
+
} else if (ref != null) {
|
|
53
|
+
ref.current = value;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
/* v8 ignore stop */ }, [
|
|
58
|
+
...refs
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { useMergeRefs };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type ObserverRect = Omit<DOMRectReadOnly, "toJSON">;
|
|
2
|
+
/**
|
|
3
|
+
* A custom hook that uses the ResizeObserver API to track the size changes of a DOM element.
|
|
4
|
+
*
|
|
5
|
+
* @template T - The type of the DOM element being observed.
|
|
6
|
+
* @param {ResizeObserverOptions} [options] - The options to configure the ResizeObserver.
|
|
7
|
+
* @returns {[React.RefObject<T>, ObserverRect]} - A tuple containing the ref object and
|
|
8
|
+
* the observed rectangle.
|
|
9
|
+
* @example
|
|
10
|
+
*
|
|
11
|
+
* const [rightElementRef, rect] = useResizeObserver<HTMLDivElement>();
|
|
12
|
+
* <div ref={componentRef}>
|
|
13
|
+
* Observed: <code>{JSON.stringify(rect)}</code>
|
|
14
|
+
* </div>
|
|
15
|
+
*/
|
|
16
|
+
export declare function useResizeObserver<T extends HTMLElement = any>(options?: ResizeObserverOptions): readonly [import("react").RefObject<T | null>, ObserverRect];
|
|
17
|
+
export {};
|