@structyl/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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 your-lib contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # @structyl/hooks
2
+
3
+ > A collection of SSR-safe, tree-shakeable React hooks for state, refs, the DOM, browser APIs, and performance.
4
+
5
+ ![npm](https://img.shields.io/npm/v/@structyl/hooks)
6
+ ![license](https://img.shields.io/npm/l/@structyl/hooks)
7
+
8
+ `@structyl/hooks` is the shared hook layer of [structyl](https://structyl.dev), the React UI library with structure. It packages the small, single-responsibility hooks that the primitives and styled components rely on, such as `useControllableState` for controlled/uncontrolled state and `useComposedRefs` for merging refs. Every hook is SSR-safe (no unguarded `window`/`document` access) and tree-shakeable, so it is equally useful on its own in any React 18 or 19 application.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ # pnpm
14
+ pnpm add @structyl/hooks
15
+
16
+ # npm
17
+ npm install @structyl/hooks
18
+
19
+ # yarn
20
+ yarn add @structyl/hooks
21
+ ```
22
+
23
+ React 18 or 19 (and `react-dom`) are peer dependencies.
24
+
25
+ ## Usage
26
+
27
+ ```tsx
28
+ import {
29
+ useControllableState,
30
+ useDebounce,
31
+ useLocalStorage,
32
+ } from '@structyl/hooks';
33
+
34
+ function SearchField({ value, defaultValue = '', onChange }: {
35
+ value?: string;
36
+ defaultValue?: string;
37
+ onChange?: (next: string) => void;
38
+ }) {
39
+ // Works in both controlled and uncontrolled modes.
40
+ const [query, setQuery] = useControllableState({
41
+ prop: value,
42
+ defaultProp: defaultValue,
43
+ onChange,
44
+ });
45
+
46
+ // Only react to the value after the user stops typing.
47
+ const debouncedQuery = useDebounce(query ?? '', 300);
48
+
49
+ // Persist the last search across reloads (SSR-safe).
50
+ const [recent, setRecent] = useLocalStorage<string[]>('recent-searches', []);
51
+
52
+ return (
53
+ <input
54
+ type="search"
55
+ value={query ?? ''}
56
+ onChange={(event) => setQuery(event.target.value)}
57
+ onBlur={() => debouncedQuery && setRecent([debouncedQuery, ...recent])}
58
+ placeholder="Search…"
59
+ />
60
+ );
61
+ }
62
+ ```
63
+
64
+ ## Features
65
+
66
+ - **Controllable state** — `useControllableState` is the cornerstone hook for building components that work in both controlled and uncontrolled modes.
67
+ - **SSR-safe by default** — browser-bound hooks guard `window`/`document` access and degrade gracefully on the server.
68
+ - **Tree-shakeable** — named exports and `sideEffects: false` mean you only ship the hooks you import.
69
+ - **Ref composition** — `useComposedRefs` / `composeRefs` merge multiple refs onto a single node, the foundation of `asChild` patterns.
70
+ - **TypeScript-first** — fully typed signatures with generics and tuple returns; no `any`.
71
+ - **ESM + CJS** — ships both module formats plus type declarations.
72
+
73
+ ## API
74
+
75
+ ### State
76
+
77
+ | Hook | Description |
78
+ | --- | --- |
79
+ | `useControllableState` | Manage state that may be controlled by a parent or held internally. |
80
+ | `useToggle` | Boolean state with a toggle and explicit setter `[value, toggle, set]`. |
81
+ | `useBoolean` | Boolean state with `{ value, on, off, toggle, set }` helpers. |
82
+ | `useCounter` | Numeric state with `increment`, `decrement`, `reset`, and `set`. |
83
+ | `usePrevious` | Returns the value from the previous render. |
84
+
85
+ ### Refs
86
+
87
+ | Hook | Description |
88
+ | --- | --- |
89
+ | `useComposedRefs` / `composeRefs` | Merge multiple refs (callback or object) onto one node. |
90
+ | `useCallbackRef` | Stabilize a callback as a ref to avoid re-renders. |
91
+ | `useLatest` | Keep a ref pointing at the latest value without re-rendering. |
92
+
93
+ ### DOM
94
+
95
+ | Hook | Description |
96
+ | --- | --- |
97
+ | `useClickOutside` | Run a handler when a click/touch lands outside a ref. |
98
+ | `useEventListener` | Typed `addEventListener` for `window`, `document`, or an element. |
99
+ | `useKeyPress` | Run a handler when a specific key is pressed. |
100
+
101
+ ### Browser
102
+
103
+ | Hook | Description |
104
+ | --- | --- |
105
+ | `useMediaQuery` | Subscribe to a CSS media query, with an SSR default. |
106
+ | `useLocalStorage` | JSON-backed `localStorage` state `[value, set, remove]`, synced across tabs. |
107
+ | `useCopyToClipboard` | Copy text and track `copied` state via the Clipboard API. |
108
+ | `useDarkMode` | Boolean for the `prefers-color-scheme: dark` query. |
109
+
110
+ ### Performance
111
+
112
+ | Hook | Description |
113
+ | --- | --- |
114
+ | `useDebounce` | Debounce a value by a delay (default `300ms`). |
115
+ | `useThrottle` | Throttle a value by a delay (default `300ms`). |
116
+
117
+ ### Utility
118
+
119
+ | Hook | Description |
120
+ | --- | --- |
121
+ | `useId` | SSR-safe stable ID wrapping `React.useId` with an optional prefix. |
122
+ | `useMount` | Run a callback once on mount. |
123
+ | `useUnmount` | Run a callback on unmount. |
124
+ | `useUpdateEffect` | Like `useEffect`, but skips the first run. |
125
+ | `useIsomorphicLayoutEffect` | `useLayoutEffect` on the client, `useEffect` on the server. |
126
+ | `useWindowSize` | Track `{ width, height }` of the window. |
127
+ | `useHotkeys` | Bind keyboard shortcuts (e.g. `mod+k`) with modifier support. |
128
+
129
+ ## Part of structyl
130
+
131
+ Part of [structyl](https://github.com/imirfanul/structyl) — see the full documentation at [structyl.dev](https://structyl.dev).
132
+
133
+ ## License
134
+
135
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,335 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/use-controllable-state.ts
6
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
7
+
8
+ // src/use-callback-ref.ts
9
+ function useCallbackRef(callback) {
10
+ const callbackRef = react.useRef(callback);
11
+ useIsomorphicLayoutEffect(() => {
12
+ callbackRef.current = callback;
13
+ });
14
+ return react.useMemo(
15
+ () => ((...args) => callbackRef.current?.(...args)),
16
+ []
17
+ );
18
+ }
19
+
20
+ // src/use-controllable-state.ts
21
+ function useControllableState({
22
+ prop,
23
+ defaultProp,
24
+ onChange = () => {
25
+ }
26
+ }) {
27
+ const [uncontrolledProp, setUncontrolledProp] = useUncontrolledState({
28
+ defaultProp,
29
+ onChange
30
+ });
31
+ const isControlled = prop !== void 0;
32
+ const value = isControlled ? prop : uncontrolledProp;
33
+ const handleChange = useCallbackRef(onChange);
34
+ const setValue = react.useCallback(
35
+ (nextValue) => {
36
+ if (isControlled) {
37
+ const setter = nextValue;
38
+ const resolved = typeof nextValue === "function" ? setter(prop) : nextValue;
39
+ if (resolved !== prop) handleChange(resolved);
40
+ } else {
41
+ setUncontrolledProp(nextValue);
42
+ }
43
+ },
44
+ [isControlled, prop, setUncontrolledProp, handleChange]
45
+ );
46
+ return [value, setValue];
47
+ }
48
+ function useUncontrolledState({
49
+ defaultProp,
50
+ onChange
51
+ }) {
52
+ const uncontrolledState = react.useState(defaultProp);
53
+ const [value] = uncontrolledState;
54
+ const prevValueRef = react.useRef(value);
55
+ const handleChange = useCallbackRef(onChange);
56
+ react.useEffect(() => {
57
+ if (prevValueRef.current !== value) {
58
+ handleChange(value);
59
+ prevValueRef.current = value;
60
+ }
61
+ }, [value, prevValueRef, handleChange]);
62
+ return uncontrolledState;
63
+ }
64
+ function useToggle(initial = false) {
65
+ const [value, setValue] = react.useState(initial);
66
+ const toggle = react.useCallback(() => setValue((v) => !v), []);
67
+ return [value, toggle, setValue];
68
+ }
69
+ function useBoolean(initial = false) {
70
+ const [value, setValue] = react.useState(initial);
71
+ const on = react.useCallback(() => setValue(true), []);
72
+ const off = react.useCallback(() => setValue(false), []);
73
+ const toggle = react.useCallback(() => setValue((v) => !v), []);
74
+ return { value, on, off, toggle, set: setValue };
75
+ }
76
+ function useCounter(initial = 0) {
77
+ const [count, setCount] = react.useState(initial);
78
+ const increment = react.useCallback((by = 1) => setCount((c) => c + by), []);
79
+ const decrement = react.useCallback((by = 1) => setCount((c) => c - by), []);
80
+ const reset = react.useCallback(() => setCount(initial), [initial]);
81
+ return { count, increment, decrement, reset, set: setCount };
82
+ }
83
+ function usePrevious(value) {
84
+ const ref = react.useRef(void 0);
85
+ react.useEffect(() => {
86
+ ref.current = value;
87
+ }, [value]);
88
+ return ref.current;
89
+ }
90
+ function setRef(ref, value) {
91
+ if (typeof ref === "function") ref(value);
92
+ else if (ref != null && typeof ref === "object")
93
+ ref.current = value;
94
+ }
95
+ function composeRefs(...refs) {
96
+ return (node) => refs.forEach((ref) => setRef(ref, node));
97
+ }
98
+ function useComposedRefs(...refs) {
99
+ return react.useCallback(composeRefs(...refs), refs);
100
+ }
101
+ function useLatest(value) {
102
+ const ref = react.useRef(value);
103
+ useIsomorphicLayoutEffect(() => {
104
+ ref.current = value;
105
+ });
106
+ return ref;
107
+ }
108
+ function useClickOutside(ref, handler, enabled = true) {
109
+ react.useEffect(() => {
110
+ if (!enabled) return;
111
+ const listener = (event) => {
112
+ const el = ref.current;
113
+ if (!el || el.contains(event.target)) return;
114
+ handler(event);
115
+ };
116
+ document.addEventListener("mousedown", listener);
117
+ document.addEventListener("touchstart", listener);
118
+ return () => {
119
+ document.removeEventListener("mousedown", listener);
120
+ document.removeEventListener("touchstart", listener);
121
+ };
122
+ }, [ref, handler, enabled]);
123
+ }
124
+ function useEventListener(eventName, handler, element) {
125
+ const savedHandler = react.useRef(handler);
126
+ react.useEffect(() => {
127
+ savedHandler.current = handler;
128
+ }, [handler]);
129
+ react.useEffect(() => {
130
+ const target = element ?? (typeof window !== "undefined" ? window : null);
131
+ if (!target?.addEventListener) return;
132
+ const listener = (event) => savedHandler.current(event);
133
+ target.addEventListener(eventName, listener);
134
+ return () => target.removeEventListener(eventName, listener);
135
+ }, [eventName, element]);
136
+ }
137
+
138
+ // src/use-key-press.ts
139
+ function useKeyPress(targetKey, handler) {
140
+ useEventListener("keydown", (event) => {
141
+ if (event.key === targetKey) handler(event);
142
+ });
143
+ }
144
+ function useMediaQuery(query, defaultValue = false) {
145
+ const [matches, setMatches] = react.useState(defaultValue);
146
+ react.useEffect(() => {
147
+ if (typeof window === "undefined") return;
148
+ const mediaQuery = window.matchMedia(query);
149
+ setMatches(mediaQuery.matches);
150
+ const listener = (e) => setMatches(e.matches);
151
+ mediaQuery.addEventListener("change", listener);
152
+ return () => mediaQuery.removeEventListener("change", listener);
153
+ }, [query]);
154
+ return matches;
155
+ }
156
+ function useLocalStorage(key, initialValue) {
157
+ const readValue = react.useCallback(() => {
158
+ if (typeof window === "undefined") return initialValue;
159
+ try {
160
+ const item = window.localStorage.getItem(key);
161
+ return item ? JSON.parse(item) : initialValue;
162
+ } catch {
163
+ return initialValue;
164
+ }
165
+ }, [key, initialValue]);
166
+ const [storedValue, setStoredValue] = react.useState(readValue);
167
+ const setValue = react.useCallback(
168
+ (value) => {
169
+ try {
170
+ const newValue = value instanceof Function ? value(storedValue) : value;
171
+ setStoredValue(newValue);
172
+ if (typeof window !== "undefined") {
173
+ window.localStorage.setItem(key, JSON.stringify(newValue));
174
+ window.dispatchEvent(new StorageEvent("storage", { key }));
175
+ }
176
+ } catch (error) {
177
+ console.warn(`Error setting localStorage key "${key}":`, error);
178
+ }
179
+ },
180
+ [key, storedValue]
181
+ );
182
+ const remove = react.useCallback(() => {
183
+ try {
184
+ setStoredValue(initialValue);
185
+ if (typeof window !== "undefined") {
186
+ window.localStorage.removeItem(key);
187
+ window.dispatchEvent(new StorageEvent("storage", { key }));
188
+ }
189
+ } catch (error) {
190
+ console.warn(`Error removing localStorage key "${key}":`, error);
191
+ }
192
+ }, [key, initialValue]);
193
+ react.useEffect(() => {
194
+ const onStorage = (e) => {
195
+ if (e.key === key) setStoredValue(readValue());
196
+ };
197
+ window.addEventListener("storage", onStorage);
198
+ return () => window.removeEventListener("storage", onStorage);
199
+ }, [key, readValue]);
200
+ return [storedValue, setValue, remove];
201
+ }
202
+ function useCopyToClipboard() {
203
+ const [copied, setCopied] = react.useState(false);
204
+ const copy = react.useCallback(async (text) => {
205
+ if (typeof navigator === "undefined" || !navigator.clipboard) return false;
206
+ try {
207
+ await navigator.clipboard.writeText(text);
208
+ setCopied(true);
209
+ setTimeout(() => setCopied(false), 2e3);
210
+ return true;
211
+ } catch {
212
+ return false;
213
+ }
214
+ }, []);
215
+ const reset = react.useCallback(() => setCopied(false), []);
216
+ return { copy, copied, reset };
217
+ }
218
+
219
+ // src/use-dark-mode.ts
220
+ function useDarkMode() {
221
+ return useMediaQuery("(prefers-color-scheme: dark)");
222
+ }
223
+ function useDebounce(value, delay = 300) {
224
+ const [debounced, setDebounced] = react.useState(value);
225
+ react.useEffect(() => {
226
+ const id = setTimeout(() => setDebounced(value), delay);
227
+ return () => clearTimeout(id);
228
+ }, [value, delay]);
229
+ return debounced;
230
+ }
231
+ function useThrottle(value, delay = 300) {
232
+ const [throttled, setThrottled] = react.useState(value);
233
+ const lastRan = react.useRef(Date.now());
234
+ react.useEffect(() => {
235
+ const handler = setTimeout(
236
+ () => {
237
+ if (Date.now() - lastRan.current >= delay) {
238
+ setThrottled(value);
239
+ lastRan.current = Date.now();
240
+ }
241
+ },
242
+ delay - (Date.now() - lastRan.current)
243
+ );
244
+ return () => clearTimeout(handler);
245
+ }, [value, delay]);
246
+ return throttled;
247
+ }
248
+ function useId(prefix) {
249
+ const id = react.useId();
250
+ return prefix ? `${prefix}-${id}` : id;
251
+ }
252
+ function useMount(callback) {
253
+ react.useEffect(() => {
254
+ callback();
255
+ }, []);
256
+ }
257
+ function useUnmount(callback) {
258
+ const cbRef = useLatest(callback);
259
+ react.useEffect(() => () => cbRef.current?.(), [cbRef]);
260
+ }
261
+ function useUpdateEffect(effect, deps) {
262
+ const isMounted = react.useRef(false);
263
+ react.useEffect(() => {
264
+ if (isMounted.current) return effect();
265
+ isMounted.current = true;
266
+ return void 0;
267
+ }, deps);
268
+ }
269
+ function useWindowSize() {
270
+ const [size, setSize] = react.useState({
271
+ width: typeof window === "undefined" ? 0 : window.innerWidth,
272
+ height: typeof window === "undefined" ? 0 : window.innerHeight
273
+ });
274
+ react.useEffect(() => {
275
+ if (typeof window === "undefined") return;
276
+ const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight });
277
+ window.addEventListener("resize", handler);
278
+ return () => window.removeEventListener("resize", handler);
279
+ }, []);
280
+ return size;
281
+ }
282
+ function useHotkeys(keys, handler, options = {}) {
283
+ react.useEffect(() => {
284
+ const { enableOnFormTags = false, preventDefault = true } = options;
285
+ const tokens = keys.toLowerCase().split("+").map((s) => s.trim());
286
+ const targetKey = tokens[tokens.length - 1];
287
+ const listener = (event) => {
288
+ const target = event.target;
289
+ if (!enableOnFormTags && target && ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName)) {
290
+ return;
291
+ }
292
+ const expectsCtrl = tokens.includes("ctrl");
293
+ const expectsMeta = tokens.includes("meta");
294
+ const expectsMod = tokens.includes("mod");
295
+ const expectsShift = tokens.includes("shift");
296
+ const expectsAlt = tokens.includes("alt");
297
+ const isMacLike = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
298
+ const modOk = expectsMod ? isMacLike ? event.metaKey : event.ctrlKey : true;
299
+ if (event.key.toLowerCase() === targetKey && (expectsCtrl ? event.ctrlKey : !expectsMod || !event.ctrlKey || isMacLike) && (expectsMeta ? event.metaKey : !expectsMod || !event.metaKey || !isMacLike) && modOk && (expectsShift ? event.shiftKey : !event.shiftKey || expectsShift) && (expectsAlt ? event.altKey : !event.altKey || expectsAlt)) {
300
+ if (preventDefault) event.preventDefault();
301
+ handler(event);
302
+ }
303
+ };
304
+ document.addEventListener("keydown", listener);
305
+ return () => document.removeEventListener("keydown", listener);
306
+ }, [keys, handler, options]);
307
+ }
308
+
309
+ exports.composeRefs = composeRefs;
310
+ exports.useBoolean = useBoolean;
311
+ exports.useCallbackRef = useCallbackRef;
312
+ exports.useClickOutside = useClickOutside;
313
+ exports.useComposedRefs = useComposedRefs;
314
+ exports.useControllableState = useControllableState;
315
+ exports.useCopyToClipboard = useCopyToClipboard;
316
+ exports.useCounter = useCounter;
317
+ exports.useDarkMode = useDarkMode;
318
+ exports.useDebounce = useDebounce;
319
+ exports.useEventListener = useEventListener;
320
+ exports.useHotkeys = useHotkeys;
321
+ exports.useId = useId;
322
+ exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
323
+ exports.useKeyPress = useKeyPress;
324
+ exports.useLatest = useLatest;
325
+ exports.useLocalStorage = useLocalStorage;
326
+ exports.useMediaQuery = useMediaQuery;
327
+ exports.useMount = useMount;
328
+ exports.usePrevious = usePrevious;
329
+ exports.useThrottle = useThrottle;
330
+ exports.useToggle = useToggle;
331
+ exports.useUnmount = useUnmount;
332
+ exports.useUpdateEffect = useUpdateEffect;
333
+ exports.useWindowSize = useWindowSize;
334
+ //# sourceMappingURL=index.cjs.map
335
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/use-isomorphic-layout-effect.ts","../src/use-callback-ref.ts","../src/use-controllable-state.ts","../src/use-toggle.ts","../src/use-boolean.ts","../src/use-counter.ts","../src/use-previous.ts","../src/use-composed-refs.ts","../src/use-latest.ts","../src/use-click-outside.ts","../src/use-event-listener.ts","../src/use-key-press.ts","../src/use-media-query.ts","../src/use-local-storage.ts","../src/use-copy-to-clipboard.ts","../src/use-dark-mode.ts","../src/use-debounce.ts","../src/use-throttle.ts","../src/use-id.ts","../src/use-mount.ts","../src/use-unmount.ts","../src/use-update-effect.ts","../src/use-window-size.ts","../src/use-hotkeys.ts"],"names":["useLayoutEffect","useEffect","useRef","useMemo","useCallback","useState","useReactId"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useEffect, useLayoutEffect } from 'react';\n\n/** Uses useLayoutEffect on the client, useEffect on the server. */\nexport const useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n","import { useMemo, useRef } from 'react';\nimport { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';\n\n/**\n * A custom hook that converts a callback to a ref to avoid triggering re-renders\n * when passed as a prop or in dependency arrays.\n */\nexport function useCallbackRef<T extends (...args: never[]) => unknown>(\n callback: T | undefined,\n): T {\n const callbackRef = useRef(callback);\n\n useIsomorphicLayoutEffect(() => {\n callbackRef.current = callback;\n });\n\n return useMemo(\n () => ((...args) => callbackRef.current?.(...args)) as T,\n [],\n );\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { useCallbackRef } from './use-callback-ref';\n\ntype UseControllableStateParams<T> = {\n prop?: T | undefined;\n defaultProp?: T | undefined;\n onChange?: (state: T) => void;\n};\n\ntype SetStateFn<T> = (prevState?: T) => T;\n\n/**\n * Manages state that can be either controlled by parent or uncontrolled internally.\n * The cornerstone hook of accessible component design.\n */\nexport function useControllableState<T>({\n prop,\n defaultProp,\n onChange = () => {},\n}: UseControllableStateParams<T>): [T | undefined, (next: T | SetStateFn<T>) => void] {\n const [uncontrolledProp, setUncontrolledProp] = useUncontrolledState({\n defaultProp,\n onChange,\n });\n const isControlled = prop !== undefined;\n const value = isControlled ? prop : uncontrolledProp;\n const handleChange = useCallbackRef(onChange);\n\n const setValue = useCallback<(next: T | SetStateFn<T>) => void>(\n (nextValue) => {\n if (isControlled) {\n const setter = nextValue as SetStateFn<T>;\n const resolved =\n typeof nextValue === 'function' ? setter(prop) : (nextValue as T);\n if (resolved !== prop) handleChange(resolved);\n } else {\n setUncontrolledProp(nextValue);\n }\n },\n [isControlled, prop, setUncontrolledProp, handleChange],\n );\n\n return [value, setValue];\n}\n\nfunction useUncontrolledState<T>({\n defaultProp,\n onChange,\n}: Omit<UseControllableStateParams<T>, 'prop'>): [T | undefined, (next: T | SetStateFn<T>) => void] {\n const uncontrolledState = useState<T | undefined>(defaultProp);\n const [value] = uncontrolledState;\n const prevValueRef = useRef(value);\n const handleChange = useCallbackRef(onChange);\n\n useEffect(() => {\n if (prevValueRef.current !== value) {\n handleChange(value as T);\n prevValueRef.current = value;\n }\n }, [value, prevValueRef, handleChange]);\n\n return uncontrolledState;\n}\n","import { useCallback, useState } from 'react';\n\nexport function useToggle(initial = false): [boolean, () => void, (value: boolean) => void] {\n const [value, setValue] = useState(initial);\n const toggle = useCallback(() => setValue((v) => !v), []);\n return [value, toggle, setValue];\n}\n","import { useCallback, useState } from 'react';\n\nexport function useBoolean(initial = false) {\n const [value, setValue] = useState(initial);\n const on = useCallback(() => setValue(true), []);\n const off = useCallback(() => setValue(false), []);\n const toggle = useCallback(() => setValue((v) => !v), []);\n return { value, on, off, toggle, set: setValue };\n}\n","import { useCallback, useState } from 'react';\n\nexport function useCounter(initial = 0) {\n const [count, setCount] = useState(initial);\n const increment = useCallback((by = 1) => setCount((c) => c + by), []);\n const decrement = useCallback((by = 1) => setCount((c) => c - by), []);\n const reset = useCallback(() => setCount(initial), [initial]);\n return { count, increment, decrement, reset, set: setCount };\n}\n","import { useEffect, useRef } from 'react';\n\nexport function usePrevious<T>(value: T): T | undefined {\n const ref = useRef<T | undefined>(undefined);\n useEffect(() => {\n ref.current = value;\n }, [value]);\n return ref.current;\n}\n","import { useCallback, type Ref } from 'react';\n\ntype PossibleRef<T> = Ref<T> | undefined | ((value: T | null) => void);\n\nfunction setRef<T>(ref: PossibleRef<T>, value: T | null) {\n if (typeof ref === 'function') ref(value as T);\n else if (ref != null && typeof ref === 'object')\n (ref as { current: T | null }).current = value;\n}\n\nexport function composeRefs<T>(...refs: PossibleRef<T>[]): (node: T | null) => void {\n return (node) => refs.forEach((ref) => setRef(ref, node));\n}\n\nexport function useComposedRefs<T>(...refs: PossibleRef<T>[]): (node: T | null) => void {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return useCallback(composeRefs(...refs), refs);\n}\n","import { useRef } from 'react';\nimport { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';\n\nexport function useLatest<T>(value: T): { readonly current: T } {\n const ref = useRef(value);\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n","import { useEffect, type RefObject } from 'react';\n\nexport function useClickOutside<T extends HTMLElement>(\n ref: RefObject<T | null>,\n handler: (event: MouseEvent | TouchEvent) => void,\n enabled = true,\n): void {\n useEffect(() => {\n if (!enabled) return;\n const listener = (event: MouseEvent | TouchEvent) => {\n const el = ref.current;\n if (!el || el.contains(event.target as Node)) return;\n handler(event);\n };\n document.addEventListener('mousedown', listener);\n document.addEventListener('touchstart', listener);\n return () => {\n document.removeEventListener('mousedown', listener);\n document.removeEventListener('touchstart', listener);\n };\n }, [ref, handler, enabled]);\n}\n","import { useEffect, useRef } from 'react';\n\nexport function useEventListener<K extends keyof WindowEventMap>(\n eventName: K,\n handler: (event: WindowEventMap[K]) => void,\n element?: Window,\n): void;\nexport function useEventListener<K extends keyof DocumentEventMap>(\n eventName: K,\n handler: (event: DocumentEventMap[K]) => void,\n element: Document,\n): void;\nexport function useEventListener<K extends keyof HTMLElementEventMap>(\n eventName: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n element: HTMLElement | null,\n): void;\nexport function useEventListener(\n eventName: string,\n handler: (event: Event) => void,\n element?: Window | Document | HTMLElement | null,\n): void {\n const savedHandler = useRef(handler);\n\n useEffect(() => {\n savedHandler.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const target = element ?? (typeof window !== 'undefined' ? window : null);\n if (!target?.addEventListener) return;\n\n const listener: EventListener = (event) => savedHandler.current(event);\n target.addEventListener(eventName, listener);\n return () => target.removeEventListener(eventName, listener);\n }, [eventName, element]);\n}\n","import { useEventListener } from './use-event-listener';\n\nexport function useKeyPress(targetKey: string, handler: (event: KeyboardEvent) => void): void {\n useEventListener('keydown', (event) => {\n if (event.key === targetKey) handler(event);\n });\n}\n","import { useEffect, useState } from 'react';\n\nexport function useMediaQuery(query: string, defaultValue = false): boolean {\n const [matches, setMatches] = useState(defaultValue);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const mediaQuery = window.matchMedia(query);\n setMatches(mediaQuery.matches);\n const listener = (e: MediaQueryListEvent) => setMatches(e.matches);\n mediaQuery.addEventListener('change', listener);\n return () => mediaQuery.removeEventListener('change', listener);\n }, [query]);\n\n return matches;\n}\n","import { useCallback, useEffect, useState } from 'react';\n\nexport function useLocalStorage<T>(\n key: string,\n initialValue: T,\n): [T, (value: T | ((val: T) => T)) => void, () => void] {\n const readValue = useCallback((): T => {\n if (typeof window === 'undefined') return initialValue;\n try {\n const item = window.localStorage.getItem(key);\n return item ? (JSON.parse(item) as T) : initialValue;\n } catch {\n return initialValue;\n }\n }, [key, initialValue]);\n\n const [storedValue, setStoredValue] = useState<T>(readValue);\n\n const setValue = useCallback(\n (value: T | ((val: T) => T)) => {\n try {\n const newValue =\n value instanceof Function ? (value as (val: T) => T)(storedValue) : value;\n setStoredValue(newValue);\n if (typeof window !== 'undefined') {\n window.localStorage.setItem(key, JSON.stringify(newValue));\n window.dispatchEvent(new StorageEvent('storage', { key }));\n }\n } catch (error) {\n console.warn(`Error setting localStorage key \"${key}\":`, error);\n }\n },\n [key, storedValue],\n );\n\n const remove = useCallback(() => {\n try {\n setStoredValue(initialValue);\n if (typeof window !== 'undefined') {\n window.localStorage.removeItem(key);\n window.dispatchEvent(new StorageEvent('storage', { key }));\n }\n } catch (error) {\n console.warn(`Error removing localStorage key \"${key}\":`, error);\n }\n }, [key, initialValue]);\n\n useEffect(() => {\n const onStorage = (e: StorageEvent) => {\n if (e.key === key) setStoredValue(readValue());\n };\n window.addEventListener('storage', onStorage);\n return () => window.removeEventListener('storage', onStorage);\n }, [key, readValue]);\n\n return [storedValue, setValue, remove];\n}\n","import { useCallback, useState } from 'react';\n\nexport function useCopyToClipboard(): {\n copy: (text: string) => Promise<boolean>;\n copied: boolean;\n reset: () => void;\n} {\n const [copied, setCopied] = useState(false);\n\n const copy = useCallback(async (text: string): Promise<boolean> => {\n if (typeof navigator === 'undefined' || !navigator.clipboard) return false;\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n return true;\n } catch {\n return false;\n }\n }, []);\n\n const reset = useCallback(() => setCopied(false), []);\n\n return { copy, copied, reset };\n}\n","import { useMediaQuery } from './use-media-query';\n\nexport function useDarkMode(): boolean {\n return useMediaQuery('(prefers-color-scheme: dark)');\n}\n","import { useEffect, useState } from 'react';\n\nexport function useDebounce<T>(value: T, delay = 300): T {\n const [debounced, setDebounced] = useState(value);\n useEffect(() => {\n const id = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(id);\n }, [value, delay]);\n return debounced;\n}\n","import { useEffect, useRef, useState } from 'react';\n\nexport function useThrottle<T>(value: T, delay = 300): T {\n const [throttled, setThrottled] = useState(value);\n const lastRan = useRef(Date.now());\n\n useEffect(() => {\n const handler = setTimeout(\n () => {\n if (Date.now() - lastRan.current >= delay) {\n setThrottled(value);\n lastRan.current = Date.now();\n }\n },\n delay - (Date.now() - lastRan.current),\n );\n return () => clearTimeout(handler);\n }, [value, delay]);\n\n return throttled;\n}\n","import { useId as useReactId } from 'react';\n\n/** SSR-safe stable ID. Wraps React.useId with optional prefix. */\nexport function useId(prefix?: string): string {\n const id = useReactId();\n return prefix ? `${prefix}-${id}` : id;\n}\n","import { useEffect } from 'react';\n\nexport function useMount(callback: () => void): void {\n useEffect(() => {\n callback();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n}\n","import { useEffect } from 'react';\nimport { useLatest } from './use-latest';\n\nexport function useUnmount(callback: () => void): void {\n const cbRef = useLatest(callback);\n useEffect(() => () => cbRef.current?.(), [cbRef]);\n}\n","import { useEffect, useRef, type DependencyList, type EffectCallback } from 'react';\n\n/** Like useEffect, but skips the first run (on mount). */\nexport function useUpdateEffect(effect: EffectCallback, deps?: DependencyList): void {\n const isMounted = useRef(false);\n useEffect(() => {\n if (isMounted.current) return effect();\n isMounted.current = true;\n return undefined;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n}\n","import { useEffect, useState } from 'react';\n\nexport function useWindowSize(): { width: number; height: number } {\n const [size, setSize] = useState({\n width: typeof window === 'undefined' ? 0 : window.innerWidth,\n height: typeof window === 'undefined' ? 0 : window.innerHeight,\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight });\n window.addEventListener('resize', handler);\n return () => window.removeEventListener('resize', handler);\n }, []);\n\n return size;\n}\n","import { useEffect } from 'react';\n\ntype Modifier = 'ctrl' | 'meta' | 'shift' | 'alt' | 'mod';\n\nexport function useHotkeys(\n keys: string,\n handler: (event: KeyboardEvent) => void,\n options: { enableOnFormTags?: boolean; preventDefault?: boolean } = {},\n): void {\n useEffect(() => {\n const { enableOnFormTags = false, preventDefault = true } = options;\n const tokens = keys.toLowerCase().split('+').map((s) => s.trim()) as (Modifier | string)[];\n const targetKey = tokens[tokens.length - 1];\n\n const listener = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n if (\n !enableOnFormTags &&\n target &&\n ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName)\n ) {\n return;\n }\n\n const expectsCtrl = tokens.includes('ctrl');\n const expectsMeta = tokens.includes('meta');\n const expectsMod = tokens.includes('mod');\n const expectsShift = tokens.includes('shift');\n const expectsAlt = tokens.includes('alt');\n\n const isMacLike =\n typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform);\n const modOk = expectsMod ? (isMacLike ? event.metaKey : event.ctrlKey) : true;\n\n if (\n event.key.toLowerCase() === targetKey &&\n (expectsCtrl ? event.ctrlKey : !expectsMod || !event.ctrlKey || isMacLike) &&\n (expectsMeta ? event.metaKey : !expectsMod || !event.metaKey || !isMacLike) &&\n modOk &&\n (expectsShift ? event.shiftKey : !event.shiftKey || expectsShift) &&\n (expectsAlt ? event.altKey : !event.altKey || expectsAlt)\n ) {\n if (preventDefault) event.preventDefault();\n handler(event);\n }\n };\n\n document.addEventListener('keydown', listener);\n return () => document.removeEventListener('keydown', listener);\n }, [keys, handler, options]);\n}\n"]}
@@ -0,0 +1,97 @@
1
+ import * as react from 'react';
2
+ import { Ref, RefObject, EffectCallback, DependencyList, useLayoutEffect } from 'react';
3
+
4
+ type UseControllableStateParams<T> = {
5
+ prop?: T | undefined;
6
+ defaultProp?: T | undefined;
7
+ onChange?: (state: T) => void;
8
+ };
9
+ type SetStateFn<T> = (prevState?: T) => T;
10
+ /**
11
+ * Manages state that can be either controlled by parent or uncontrolled internally.
12
+ * The cornerstone hook of accessible component design.
13
+ */
14
+ declare function useControllableState<T>({ prop, defaultProp, onChange, }: UseControllableStateParams<T>): [T | undefined, (next: T | SetStateFn<T>) => void];
15
+
16
+ declare function useToggle(initial?: boolean): [boolean, () => void, (value: boolean) => void];
17
+
18
+ declare function useBoolean(initial?: boolean): {
19
+ value: boolean;
20
+ on: () => void;
21
+ off: () => void;
22
+ toggle: () => void;
23
+ set: react.Dispatch<react.SetStateAction<boolean>>;
24
+ };
25
+
26
+ declare function useCounter(initial?: number): {
27
+ count: number;
28
+ increment: (by?: number) => void;
29
+ decrement: (by?: number) => void;
30
+ reset: () => void;
31
+ set: react.Dispatch<react.SetStateAction<number>>;
32
+ };
33
+
34
+ declare function usePrevious<T>(value: T): T | undefined;
35
+
36
+ type PossibleRef<T> = Ref<T> | undefined | ((value: T | null) => void);
37
+ declare function composeRefs<T>(...refs: PossibleRef<T>[]): (node: T | null) => void;
38
+ declare function useComposedRefs<T>(...refs: PossibleRef<T>[]): (node: T | null) => void;
39
+
40
+ /**
41
+ * A custom hook that converts a callback to a ref to avoid triggering re-renders
42
+ * when passed as a prop or in dependency arrays.
43
+ */
44
+ declare function useCallbackRef<T extends (...args: never[]) => unknown>(callback: T | undefined): T;
45
+
46
+ declare function useLatest<T>(value: T): {
47
+ readonly current: T;
48
+ };
49
+
50
+ declare function useClickOutside<T extends HTMLElement>(ref: RefObject<T | null>, handler: (event: MouseEvent | TouchEvent) => void, enabled?: boolean): void;
51
+
52
+ declare function useEventListener<K extends keyof WindowEventMap>(eventName: K, handler: (event: WindowEventMap[K]) => void, element?: Window): void;
53
+ declare function useEventListener<K extends keyof DocumentEventMap>(eventName: K, handler: (event: DocumentEventMap[K]) => void, element: Document): void;
54
+ declare function useEventListener<K extends keyof HTMLElementEventMap>(eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: HTMLElement | null): void;
55
+
56
+ declare function useKeyPress(targetKey: string, handler: (event: KeyboardEvent) => void): void;
57
+
58
+ declare function useMediaQuery(query: string, defaultValue?: boolean): boolean;
59
+
60
+ declare function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void, () => void];
61
+
62
+ declare function useCopyToClipboard(): {
63
+ copy: (text: string) => Promise<boolean>;
64
+ copied: boolean;
65
+ reset: () => void;
66
+ };
67
+
68
+ declare function useDarkMode(): boolean;
69
+
70
+ declare function useDebounce<T>(value: T, delay?: number): T;
71
+
72
+ declare function useThrottle<T>(value: T, delay?: number): T;
73
+
74
+ /** SSR-safe stable ID. Wraps React.useId with optional prefix. */
75
+ declare function useId(prefix?: string): string;
76
+
77
+ declare function useMount(callback: () => void): void;
78
+
79
+ declare function useUnmount(callback: () => void): void;
80
+
81
+ /** Like useEffect, but skips the first run (on mount). */
82
+ declare function useUpdateEffect(effect: EffectCallback, deps?: DependencyList): void;
83
+
84
+ /** Uses useLayoutEffect on the client, useEffect on the server. */
85
+ declare const useIsomorphicLayoutEffect: typeof useLayoutEffect;
86
+
87
+ declare function useWindowSize(): {
88
+ width: number;
89
+ height: number;
90
+ };
91
+
92
+ declare function useHotkeys(keys: string, handler: (event: KeyboardEvent) => void, options?: {
93
+ enableOnFormTags?: boolean;
94
+ preventDefault?: boolean;
95
+ }): void;
96
+
97
+ export { composeRefs, useBoolean, useCallbackRef, useClickOutside, useComposedRefs, useControllableState, useCopyToClipboard, useCounter, useDarkMode, useDebounce, useEventListener, useHotkeys, useId, useIsomorphicLayoutEffect, useKeyPress, useLatest, useLocalStorage, useMediaQuery, useMount, usePrevious, useThrottle, useToggle, useUnmount, useUpdateEffect, useWindowSize };
@@ -0,0 +1,97 @@
1
+ import * as react from 'react';
2
+ import { Ref, RefObject, EffectCallback, DependencyList, useLayoutEffect } from 'react';
3
+
4
+ type UseControllableStateParams<T> = {
5
+ prop?: T | undefined;
6
+ defaultProp?: T | undefined;
7
+ onChange?: (state: T) => void;
8
+ };
9
+ type SetStateFn<T> = (prevState?: T) => T;
10
+ /**
11
+ * Manages state that can be either controlled by parent or uncontrolled internally.
12
+ * The cornerstone hook of accessible component design.
13
+ */
14
+ declare function useControllableState<T>({ prop, defaultProp, onChange, }: UseControllableStateParams<T>): [T | undefined, (next: T | SetStateFn<T>) => void];
15
+
16
+ declare function useToggle(initial?: boolean): [boolean, () => void, (value: boolean) => void];
17
+
18
+ declare function useBoolean(initial?: boolean): {
19
+ value: boolean;
20
+ on: () => void;
21
+ off: () => void;
22
+ toggle: () => void;
23
+ set: react.Dispatch<react.SetStateAction<boolean>>;
24
+ };
25
+
26
+ declare function useCounter(initial?: number): {
27
+ count: number;
28
+ increment: (by?: number) => void;
29
+ decrement: (by?: number) => void;
30
+ reset: () => void;
31
+ set: react.Dispatch<react.SetStateAction<number>>;
32
+ };
33
+
34
+ declare function usePrevious<T>(value: T): T | undefined;
35
+
36
+ type PossibleRef<T> = Ref<T> | undefined | ((value: T | null) => void);
37
+ declare function composeRefs<T>(...refs: PossibleRef<T>[]): (node: T | null) => void;
38
+ declare function useComposedRefs<T>(...refs: PossibleRef<T>[]): (node: T | null) => void;
39
+
40
+ /**
41
+ * A custom hook that converts a callback to a ref to avoid triggering re-renders
42
+ * when passed as a prop or in dependency arrays.
43
+ */
44
+ declare function useCallbackRef<T extends (...args: never[]) => unknown>(callback: T | undefined): T;
45
+
46
+ declare function useLatest<T>(value: T): {
47
+ readonly current: T;
48
+ };
49
+
50
+ declare function useClickOutside<T extends HTMLElement>(ref: RefObject<T | null>, handler: (event: MouseEvent | TouchEvent) => void, enabled?: boolean): void;
51
+
52
+ declare function useEventListener<K extends keyof WindowEventMap>(eventName: K, handler: (event: WindowEventMap[K]) => void, element?: Window): void;
53
+ declare function useEventListener<K extends keyof DocumentEventMap>(eventName: K, handler: (event: DocumentEventMap[K]) => void, element: Document): void;
54
+ declare function useEventListener<K extends keyof HTMLElementEventMap>(eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: HTMLElement | null): void;
55
+
56
+ declare function useKeyPress(targetKey: string, handler: (event: KeyboardEvent) => void): void;
57
+
58
+ declare function useMediaQuery(query: string, defaultValue?: boolean): boolean;
59
+
60
+ declare function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void, () => void];
61
+
62
+ declare function useCopyToClipboard(): {
63
+ copy: (text: string) => Promise<boolean>;
64
+ copied: boolean;
65
+ reset: () => void;
66
+ };
67
+
68
+ declare function useDarkMode(): boolean;
69
+
70
+ declare function useDebounce<T>(value: T, delay?: number): T;
71
+
72
+ declare function useThrottle<T>(value: T, delay?: number): T;
73
+
74
+ /** SSR-safe stable ID. Wraps React.useId with optional prefix. */
75
+ declare function useId(prefix?: string): string;
76
+
77
+ declare function useMount(callback: () => void): void;
78
+
79
+ declare function useUnmount(callback: () => void): void;
80
+
81
+ /** Like useEffect, but skips the first run (on mount). */
82
+ declare function useUpdateEffect(effect: EffectCallback, deps?: DependencyList): void;
83
+
84
+ /** Uses useLayoutEffect on the client, useEffect on the server. */
85
+ declare const useIsomorphicLayoutEffect: typeof useLayoutEffect;
86
+
87
+ declare function useWindowSize(): {
88
+ width: number;
89
+ height: number;
90
+ };
91
+
92
+ declare function useHotkeys(keys: string, handler: (event: KeyboardEvent) => void, options?: {
93
+ enableOnFormTags?: boolean;
94
+ preventDefault?: boolean;
95
+ }): void;
96
+
97
+ export { composeRefs, useBoolean, useCallbackRef, useClickOutside, useComposedRefs, useControllableState, useCopyToClipboard, useCounter, useDarkMode, useDebounce, useEventListener, useHotkeys, useId, useIsomorphicLayoutEffect, useKeyPress, useLatest, useLocalStorage, useMediaQuery, useMount, usePrevious, useThrottle, useToggle, useUnmount, useUpdateEffect, useWindowSize };
package/dist/index.mjs ADDED
@@ -0,0 +1,309 @@
1
+ import { useLayoutEffect, useEffect, useRef, useMemo, useCallback, useState, useId as useId$1 } from 'react';
2
+
3
+ // src/use-controllable-state.ts
4
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
5
+
6
+ // src/use-callback-ref.ts
7
+ function useCallbackRef(callback) {
8
+ const callbackRef = useRef(callback);
9
+ useIsomorphicLayoutEffect(() => {
10
+ callbackRef.current = callback;
11
+ });
12
+ return useMemo(
13
+ () => ((...args) => callbackRef.current?.(...args)),
14
+ []
15
+ );
16
+ }
17
+
18
+ // src/use-controllable-state.ts
19
+ function useControllableState({
20
+ prop,
21
+ defaultProp,
22
+ onChange = () => {
23
+ }
24
+ }) {
25
+ const [uncontrolledProp, setUncontrolledProp] = useUncontrolledState({
26
+ defaultProp,
27
+ onChange
28
+ });
29
+ const isControlled = prop !== void 0;
30
+ const value = isControlled ? prop : uncontrolledProp;
31
+ const handleChange = useCallbackRef(onChange);
32
+ const setValue = useCallback(
33
+ (nextValue) => {
34
+ if (isControlled) {
35
+ const setter = nextValue;
36
+ const resolved = typeof nextValue === "function" ? setter(prop) : nextValue;
37
+ if (resolved !== prop) handleChange(resolved);
38
+ } else {
39
+ setUncontrolledProp(nextValue);
40
+ }
41
+ },
42
+ [isControlled, prop, setUncontrolledProp, handleChange]
43
+ );
44
+ return [value, setValue];
45
+ }
46
+ function useUncontrolledState({
47
+ defaultProp,
48
+ onChange
49
+ }) {
50
+ const uncontrolledState = useState(defaultProp);
51
+ const [value] = uncontrolledState;
52
+ const prevValueRef = useRef(value);
53
+ const handleChange = useCallbackRef(onChange);
54
+ useEffect(() => {
55
+ if (prevValueRef.current !== value) {
56
+ handleChange(value);
57
+ prevValueRef.current = value;
58
+ }
59
+ }, [value, prevValueRef, handleChange]);
60
+ return uncontrolledState;
61
+ }
62
+ function useToggle(initial = false) {
63
+ const [value, setValue] = useState(initial);
64
+ const toggle = useCallback(() => setValue((v) => !v), []);
65
+ return [value, toggle, setValue];
66
+ }
67
+ function useBoolean(initial = false) {
68
+ const [value, setValue] = useState(initial);
69
+ const on = useCallback(() => setValue(true), []);
70
+ const off = useCallback(() => setValue(false), []);
71
+ const toggle = useCallback(() => setValue((v) => !v), []);
72
+ return { value, on, off, toggle, set: setValue };
73
+ }
74
+ function useCounter(initial = 0) {
75
+ const [count, setCount] = useState(initial);
76
+ const increment = useCallback((by = 1) => setCount((c) => c + by), []);
77
+ const decrement = useCallback((by = 1) => setCount((c) => c - by), []);
78
+ const reset = useCallback(() => setCount(initial), [initial]);
79
+ return { count, increment, decrement, reset, set: setCount };
80
+ }
81
+ function usePrevious(value) {
82
+ const ref = useRef(void 0);
83
+ useEffect(() => {
84
+ ref.current = value;
85
+ }, [value]);
86
+ return ref.current;
87
+ }
88
+ function setRef(ref, value) {
89
+ if (typeof ref === "function") ref(value);
90
+ else if (ref != null && typeof ref === "object")
91
+ ref.current = value;
92
+ }
93
+ function composeRefs(...refs) {
94
+ return (node) => refs.forEach((ref) => setRef(ref, node));
95
+ }
96
+ function useComposedRefs(...refs) {
97
+ return useCallback(composeRefs(...refs), refs);
98
+ }
99
+ function useLatest(value) {
100
+ const ref = useRef(value);
101
+ useIsomorphicLayoutEffect(() => {
102
+ ref.current = value;
103
+ });
104
+ return ref;
105
+ }
106
+ function useClickOutside(ref, handler, enabled = true) {
107
+ useEffect(() => {
108
+ if (!enabled) return;
109
+ const listener = (event) => {
110
+ const el = ref.current;
111
+ if (!el || el.contains(event.target)) return;
112
+ handler(event);
113
+ };
114
+ document.addEventListener("mousedown", listener);
115
+ document.addEventListener("touchstart", listener);
116
+ return () => {
117
+ document.removeEventListener("mousedown", listener);
118
+ document.removeEventListener("touchstart", listener);
119
+ };
120
+ }, [ref, handler, enabled]);
121
+ }
122
+ function useEventListener(eventName, handler, element) {
123
+ const savedHandler = useRef(handler);
124
+ useEffect(() => {
125
+ savedHandler.current = handler;
126
+ }, [handler]);
127
+ useEffect(() => {
128
+ const target = element ?? (typeof window !== "undefined" ? window : null);
129
+ if (!target?.addEventListener) return;
130
+ const listener = (event) => savedHandler.current(event);
131
+ target.addEventListener(eventName, listener);
132
+ return () => target.removeEventListener(eventName, listener);
133
+ }, [eventName, element]);
134
+ }
135
+
136
+ // src/use-key-press.ts
137
+ function useKeyPress(targetKey, handler) {
138
+ useEventListener("keydown", (event) => {
139
+ if (event.key === targetKey) handler(event);
140
+ });
141
+ }
142
+ function useMediaQuery(query, defaultValue = false) {
143
+ const [matches, setMatches] = useState(defaultValue);
144
+ useEffect(() => {
145
+ if (typeof window === "undefined") return;
146
+ const mediaQuery = window.matchMedia(query);
147
+ setMatches(mediaQuery.matches);
148
+ const listener = (e) => setMatches(e.matches);
149
+ mediaQuery.addEventListener("change", listener);
150
+ return () => mediaQuery.removeEventListener("change", listener);
151
+ }, [query]);
152
+ return matches;
153
+ }
154
+ function useLocalStorage(key, initialValue) {
155
+ const readValue = useCallback(() => {
156
+ if (typeof window === "undefined") return initialValue;
157
+ try {
158
+ const item = window.localStorage.getItem(key);
159
+ return item ? JSON.parse(item) : initialValue;
160
+ } catch {
161
+ return initialValue;
162
+ }
163
+ }, [key, initialValue]);
164
+ const [storedValue, setStoredValue] = useState(readValue);
165
+ const setValue = useCallback(
166
+ (value) => {
167
+ try {
168
+ const newValue = value instanceof Function ? value(storedValue) : value;
169
+ setStoredValue(newValue);
170
+ if (typeof window !== "undefined") {
171
+ window.localStorage.setItem(key, JSON.stringify(newValue));
172
+ window.dispatchEvent(new StorageEvent("storage", { key }));
173
+ }
174
+ } catch (error) {
175
+ console.warn(`Error setting localStorage key "${key}":`, error);
176
+ }
177
+ },
178
+ [key, storedValue]
179
+ );
180
+ const remove = useCallback(() => {
181
+ try {
182
+ setStoredValue(initialValue);
183
+ if (typeof window !== "undefined") {
184
+ window.localStorage.removeItem(key);
185
+ window.dispatchEvent(new StorageEvent("storage", { key }));
186
+ }
187
+ } catch (error) {
188
+ console.warn(`Error removing localStorage key "${key}":`, error);
189
+ }
190
+ }, [key, initialValue]);
191
+ useEffect(() => {
192
+ const onStorage = (e) => {
193
+ if (e.key === key) setStoredValue(readValue());
194
+ };
195
+ window.addEventListener("storage", onStorage);
196
+ return () => window.removeEventListener("storage", onStorage);
197
+ }, [key, readValue]);
198
+ return [storedValue, setValue, remove];
199
+ }
200
+ function useCopyToClipboard() {
201
+ const [copied, setCopied] = useState(false);
202
+ const copy = useCallback(async (text) => {
203
+ if (typeof navigator === "undefined" || !navigator.clipboard) return false;
204
+ try {
205
+ await navigator.clipboard.writeText(text);
206
+ setCopied(true);
207
+ setTimeout(() => setCopied(false), 2e3);
208
+ return true;
209
+ } catch {
210
+ return false;
211
+ }
212
+ }, []);
213
+ const reset = useCallback(() => setCopied(false), []);
214
+ return { copy, copied, reset };
215
+ }
216
+
217
+ // src/use-dark-mode.ts
218
+ function useDarkMode() {
219
+ return useMediaQuery("(prefers-color-scheme: dark)");
220
+ }
221
+ function useDebounce(value, delay = 300) {
222
+ const [debounced, setDebounced] = useState(value);
223
+ useEffect(() => {
224
+ const id = setTimeout(() => setDebounced(value), delay);
225
+ return () => clearTimeout(id);
226
+ }, [value, delay]);
227
+ return debounced;
228
+ }
229
+ function useThrottle(value, delay = 300) {
230
+ const [throttled, setThrottled] = useState(value);
231
+ const lastRan = useRef(Date.now());
232
+ useEffect(() => {
233
+ const handler = setTimeout(
234
+ () => {
235
+ if (Date.now() - lastRan.current >= delay) {
236
+ setThrottled(value);
237
+ lastRan.current = Date.now();
238
+ }
239
+ },
240
+ delay - (Date.now() - lastRan.current)
241
+ );
242
+ return () => clearTimeout(handler);
243
+ }, [value, delay]);
244
+ return throttled;
245
+ }
246
+ function useId(prefix) {
247
+ const id = useId$1();
248
+ return prefix ? `${prefix}-${id}` : id;
249
+ }
250
+ function useMount(callback) {
251
+ useEffect(() => {
252
+ callback();
253
+ }, []);
254
+ }
255
+ function useUnmount(callback) {
256
+ const cbRef = useLatest(callback);
257
+ useEffect(() => () => cbRef.current?.(), [cbRef]);
258
+ }
259
+ function useUpdateEffect(effect, deps) {
260
+ const isMounted = useRef(false);
261
+ useEffect(() => {
262
+ if (isMounted.current) return effect();
263
+ isMounted.current = true;
264
+ return void 0;
265
+ }, deps);
266
+ }
267
+ function useWindowSize() {
268
+ const [size, setSize] = useState({
269
+ width: typeof window === "undefined" ? 0 : window.innerWidth,
270
+ height: typeof window === "undefined" ? 0 : window.innerHeight
271
+ });
272
+ useEffect(() => {
273
+ if (typeof window === "undefined") return;
274
+ const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight });
275
+ window.addEventListener("resize", handler);
276
+ return () => window.removeEventListener("resize", handler);
277
+ }, []);
278
+ return size;
279
+ }
280
+ function useHotkeys(keys, handler, options = {}) {
281
+ useEffect(() => {
282
+ const { enableOnFormTags = false, preventDefault = true } = options;
283
+ const tokens = keys.toLowerCase().split("+").map((s) => s.trim());
284
+ const targetKey = tokens[tokens.length - 1];
285
+ const listener = (event) => {
286
+ const target = event.target;
287
+ if (!enableOnFormTags && target && ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName)) {
288
+ return;
289
+ }
290
+ const expectsCtrl = tokens.includes("ctrl");
291
+ const expectsMeta = tokens.includes("meta");
292
+ const expectsMod = tokens.includes("mod");
293
+ const expectsShift = tokens.includes("shift");
294
+ const expectsAlt = tokens.includes("alt");
295
+ const isMacLike = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
296
+ const modOk = expectsMod ? isMacLike ? event.metaKey : event.ctrlKey : true;
297
+ if (event.key.toLowerCase() === targetKey && (expectsCtrl ? event.ctrlKey : !expectsMod || !event.ctrlKey || isMacLike) && (expectsMeta ? event.metaKey : !expectsMod || !event.metaKey || !isMacLike) && modOk && (expectsShift ? event.shiftKey : !event.shiftKey || expectsShift) && (expectsAlt ? event.altKey : !event.altKey || expectsAlt)) {
298
+ if (preventDefault) event.preventDefault();
299
+ handler(event);
300
+ }
301
+ };
302
+ document.addEventListener("keydown", listener);
303
+ return () => document.removeEventListener("keydown", listener);
304
+ }, [keys, handler, options]);
305
+ }
306
+
307
+ export { composeRefs, useBoolean, useCallbackRef, useClickOutside, useComposedRefs, useControllableState, useCopyToClipboard, useCounter, useDarkMode, useDebounce, useEventListener, useHotkeys, useId, useIsomorphicLayoutEffect, useKeyPress, useLatest, useLocalStorage, useMediaQuery, useMount, usePrevious, useThrottle, useToggle, useUnmount, useUpdateEffect, useWindowSize };
308
+ //# sourceMappingURL=index.mjs.map
309
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/use-isomorphic-layout-effect.ts","../src/use-callback-ref.ts","../src/use-controllable-state.ts","../src/use-toggle.ts","../src/use-boolean.ts","../src/use-counter.ts","../src/use-previous.ts","../src/use-composed-refs.ts","../src/use-latest.ts","../src/use-click-outside.ts","../src/use-event-listener.ts","../src/use-key-press.ts","../src/use-media-query.ts","../src/use-local-storage.ts","../src/use-copy-to-clipboard.ts","../src/use-dark-mode.ts","../src/use-debounce.ts","../src/use-throttle.ts","../src/use-id.ts","../src/use-mount.ts","../src/use-unmount.ts","../src/use-update-effect.ts","../src/use-window-size.ts","../src/use-hotkeys.ts"],"names":["useRef","useEffect","useState","useCallback","useReactId"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useEffect, useLayoutEffect } from 'react';\n\n/** Uses useLayoutEffect on the client, useEffect on the server. */\nexport const useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n","import { useMemo, useRef } from 'react';\nimport { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';\n\n/**\n * A custom hook that converts a callback to a ref to avoid triggering re-renders\n * when passed as a prop or in dependency arrays.\n */\nexport function useCallbackRef<T extends (...args: never[]) => unknown>(\n callback: T | undefined,\n): T {\n const callbackRef = useRef(callback);\n\n useIsomorphicLayoutEffect(() => {\n callbackRef.current = callback;\n });\n\n return useMemo(\n () => ((...args) => callbackRef.current?.(...args)) as T,\n [],\n );\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { useCallbackRef } from './use-callback-ref';\n\ntype UseControllableStateParams<T> = {\n prop?: T | undefined;\n defaultProp?: T | undefined;\n onChange?: (state: T) => void;\n};\n\ntype SetStateFn<T> = (prevState?: T) => T;\n\n/**\n * Manages state that can be either controlled by parent or uncontrolled internally.\n * The cornerstone hook of accessible component design.\n */\nexport function useControllableState<T>({\n prop,\n defaultProp,\n onChange = () => {},\n}: UseControllableStateParams<T>): [T | undefined, (next: T | SetStateFn<T>) => void] {\n const [uncontrolledProp, setUncontrolledProp] = useUncontrolledState({\n defaultProp,\n onChange,\n });\n const isControlled = prop !== undefined;\n const value = isControlled ? prop : uncontrolledProp;\n const handleChange = useCallbackRef(onChange);\n\n const setValue = useCallback<(next: T | SetStateFn<T>) => void>(\n (nextValue) => {\n if (isControlled) {\n const setter = nextValue as SetStateFn<T>;\n const resolved =\n typeof nextValue === 'function' ? setter(prop) : (nextValue as T);\n if (resolved !== prop) handleChange(resolved);\n } else {\n setUncontrolledProp(nextValue);\n }\n },\n [isControlled, prop, setUncontrolledProp, handleChange],\n );\n\n return [value, setValue];\n}\n\nfunction useUncontrolledState<T>({\n defaultProp,\n onChange,\n}: Omit<UseControllableStateParams<T>, 'prop'>): [T | undefined, (next: T | SetStateFn<T>) => void] {\n const uncontrolledState = useState<T | undefined>(defaultProp);\n const [value] = uncontrolledState;\n const prevValueRef = useRef(value);\n const handleChange = useCallbackRef(onChange);\n\n useEffect(() => {\n if (prevValueRef.current !== value) {\n handleChange(value as T);\n prevValueRef.current = value;\n }\n }, [value, prevValueRef, handleChange]);\n\n return uncontrolledState;\n}\n","import { useCallback, useState } from 'react';\n\nexport function useToggle(initial = false): [boolean, () => void, (value: boolean) => void] {\n const [value, setValue] = useState(initial);\n const toggle = useCallback(() => setValue((v) => !v), []);\n return [value, toggle, setValue];\n}\n","import { useCallback, useState } from 'react';\n\nexport function useBoolean(initial = false) {\n const [value, setValue] = useState(initial);\n const on = useCallback(() => setValue(true), []);\n const off = useCallback(() => setValue(false), []);\n const toggle = useCallback(() => setValue((v) => !v), []);\n return { value, on, off, toggle, set: setValue };\n}\n","import { useCallback, useState } from 'react';\n\nexport function useCounter(initial = 0) {\n const [count, setCount] = useState(initial);\n const increment = useCallback((by = 1) => setCount((c) => c + by), []);\n const decrement = useCallback((by = 1) => setCount((c) => c - by), []);\n const reset = useCallback(() => setCount(initial), [initial]);\n return { count, increment, decrement, reset, set: setCount };\n}\n","import { useEffect, useRef } from 'react';\n\nexport function usePrevious<T>(value: T): T | undefined {\n const ref = useRef<T | undefined>(undefined);\n useEffect(() => {\n ref.current = value;\n }, [value]);\n return ref.current;\n}\n","import { useCallback, type Ref } from 'react';\n\ntype PossibleRef<T> = Ref<T> | undefined | ((value: T | null) => void);\n\nfunction setRef<T>(ref: PossibleRef<T>, value: T | null) {\n if (typeof ref === 'function') ref(value as T);\n else if (ref != null && typeof ref === 'object')\n (ref as { current: T | null }).current = value;\n}\n\nexport function composeRefs<T>(...refs: PossibleRef<T>[]): (node: T | null) => void {\n return (node) => refs.forEach((ref) => setRef(ref, node));\n}\n\nexport function useComposedRefs<T>(...refs: PossibleRef<T>[]): (node: T | null) => void {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return useCallback(composeRefs(...refs), refs);\n}\n","import { useRef } from 'react';\nimport { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';\n\nexport function useLatest<T>(value: T): { readonly current: T } {\n const ref = useRef(value);\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n","import { useEffect, type RefObject } from 'react';\n\nexport function useClickOutside<T extends HTMLElement>(\n ref: RefObject<T | null>,\n handler: (event: MouseEvent | TouchEvent) => void,\n enabled = true,\n): void {\n useEffect(() => {\n if (!enabled) return;\n const listener = (event: MouseEvent | TouchEvent) => {\n const el = ref.current;\n if (!el || el.contains(event.target as Node)) return;\n handler(event);\n };\n document.addEventListener('mousedown', listener);\n document.addEventListener('touchstart', listener);\n return () => {\n document.removeEventListener('mousedown', listener);\n document.removeEventListener('touchstart', listener);\n };\n }, [ref, handler, enabled]);\n}\n","import { useEffect, useRef } from 'react';\n\nexport function useEventListener<K extends keyof WindowEventMap>(\n eventName: K,\n handler: (event: WindowEventMap[K]) => void,\n element?: Window,\n): void;\nexport function useEventListener<K extends keyof DocumentEventMap>(\n eventName: K,\n handler: (event: DocumentEventMap[K]) => void,\n element: Document,\n): void;\nexport function useEventListener<K extends keyof HTMLElementEventMap>(\n eventName: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n element: HTMLElement | null,\n): void;\nexport function useEventListener(\n eventName: string,\n handler: (event: Event) => void,\n element?: Window | Document | HTMLElement | null,\n): void {\n const savedHandler = useRef(handler);\n\n useEffect(() => {\n savedHandler.current = handler;\n }, [handler]);\n\n useEffect(() => {\n const target = element ?? (typeof window !== 'undefined' ? window : null);\n if (!target?.addEventListener) return;\n\n const listener: EventListener = (event) => savedHandler.current(event);\n target.addEventListener(eventName, listener);\n return () => target.removeEventListener(eventName, listener);\n }, [eventName, element]);\n}\n","import { useEventListener } from './use-event-listener';\n\nexport function useKeyPress(targetKey: string, handler: (event: KeyboardEvent) => void): void {\n useEventListener('keydown', (event) => {\n if (event.key === targetKey) handler(event);\n });\n}\n","import { useEffect, useState } from 'react';\n\nexport function useMediaQuery(query: string, defaultValue = false): boolean {\n const [matches, setMatches] = useState(defaultValue);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const mediaQuery = window.matchMedia(query);\n setMatches(mediaQuery.matches);\n const listener = (e: MediaQueryListEvent) => setMatches(e.matches);\n mediaQuery.addEventListener('change', listener);\n return () => mediaQuery.removeEventListener('change', listener);\n }, [query]);\n\n return matches;\n}\n","import { useCallback, useEffect, useState } from 'react';\n\nexport function useLocalStorage<T>(\n key: string,\n initialValue: T,\n): [T, (value: T | ((val: T) => T)) => void, () => void] {\n const readValue = useCallback((): T => {\n if (typeof window === 'undefined') return initialValue;\n try {\n const item = window.localStorage.getItem(key);\n return item ? (JSON.parse(item) as T) : initialValue;\n } catch {\n return initialValue;\n }\n }, [key, initialValue]);\n\n const [storedValue, setStoredValue] = useState<T>(readValue);\n\n const setValue = useCallback(\n (value: T | ((val: T) => T)) => {\n try {\n const newValue =\n value instanceof Function ? (value as (val: T) => T)(storedValue) : value;\n setStoredValue(newValue);\n if (typeof window !== 'undefined') {\n window.localStorage.setItem(key, JSON.stringify(newValue));\n window.dispatchEvent(new StorageEvent('storage', { key }));\n }\n } catch (error) {\n console.warn(`Error setting localStorage key \"${key}\":`, error);\n }\n },\n [key, storedValue],\n );\n\n const remove = useCallback(() => {\n try {\n setStoredValue(initialValue);\n if (typeof window !== 'undefined') {\n window.localStorage.removeItem(key);\n window.dispatchEvent(new StorageEvent('storage', { key }));\n }\n } catch (error) {\n console.warn(`Error removing localStorage key \"${key}\":`, error);\n }\n }, [key, initialValue]);\n\n useEffect(() => {\n const onStorage = (e: StorageEvent) => {\n if (e.key === key) setStoredValue(readValue());\n };\n window.addEventListener('storage', onStorage);\n return () => window.removeEventListener('storage', onStorage);\n }, [key, readValue]);\n\n return [storedValue, setValue, remove];\n}\n","import { useCallback, useState } from 'react';\n\nexport function useCopyToClipboard(): {\n copy: (text: string) => Promise<boolean>;\n copied: boolean;\n reset: () => void;\n} {\n const [copied, setCopied] = useState(false);\n\n const copy = useCallback(async (text: string): Promise<boolean> => {\n if (typeof navigator === 'undefined' || !navigator.clipboard) return false;\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n return true;\n } catch {\n return false;\n }\n }, []);\n\n const reset = useCallback(() => setCopied(false), []);\n\n return { copy, copied, reset };\n}\n","import { useMediaQuery } from './use-media-query';\n\nexport function useDarkMode(): boolean {\n return useMediaQuery('(prefers-color-scheme: dark)');\n}\n","import { useEffect, useState } from 'react';\n\nexport function useDebounce<T>(value: T, delay = 300): T {\n const [debounced, setDebounced] = useState(value);\n useEffect(() => {\n const id = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(id);\n }, [value, delay]);\n return debounced;\n}\n","import { useEffect, useRef, useState } from 'react';\n\nexport function useThrottle<T>(value: T, delay = 300): T {\n const [throttled, setThrottled] = useState(value);\n const lastRan = useRef(Date.now());\n\n useEffect(() => {\n const handler = setTimeout(\n () => {\n if (Date.now() - lastRan.current >= delay) {\n setThrottled(value);\n lastRan.current = Date.now();\n }\n },\n delay - (Date.now() - lastRan.current),\n );\n return () => clearTimeout(handler);\n }, [value, delay]);\n\n return throttled;\n}\n","import { useId as useReactId } from 'react';\n\n/** SSR-safe stable ID. Wraps React.useId with optional prefix. */\nexport function useId(prefix?: string): string {\n const id = useReactId();\n return prefix ? `${prefix}-${id}` : id;\n}\n","import { useEffect } from 'react';\n\nexport function useMount(callback: () => void): void {\n useEffect(() => {\n callback();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n}\n","import { useEffect } from 'react';\nimport { useLatest } from './use-latest';\n\nexport function useUnmount(callback: () => void): void {\n const cbRef = useLatest(callback);\n useEffect(() => () => cbRef.current?.(), [cbRef]);\n}\n","import { useEffect, useRef, type DependencyList, type EffectCallback } from 'react';\n\n/** Like useEffect, but skips the first run (on mount). */\nexport function useUpdateEffect(effect: EffectCallback, deps?: DependencyList): void {\n const isMounted = useRef(false);\n useEffect(() => {\n if (isMounted.current) return effect();\n isMounted.current = true;\n return undefined;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n}\n","import { useEffect, useState } from 'react';\n\nexport function useWindowSize(): { width: number; height: number } {\n const [size, setSize] = useState({\n width: typeof window === 'undefined' ? 0 : window.innerWidth,\n height: typeof window === 'undefined' ? 0 : window.innerHeight,\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight });\n window.addEventListener('resize', handler);\n return () => window.removeEventListener('resize', handler);\n }, []);\n\n return size;\n}\n","import { useEffect } from 'react';\n\ntype Modifier = 'ctrl' | 'meta' | 'shift' | 'alt' | 'mod';\n\nexport function useHotkeys(\n keys: string,\n handler: (event: KeyboardEvent) => void,\n options: { enableOnFormTags?: boolean; preventDefault?: boolean } = {},\n): void {\n useEffect(() => {\n const { enableOnFormTags = false, preventDefault = true } = options;\n const tokens = keys.toLowerCase().split('+').map((s) => s.trim()) as (Modifier | string)[];\n const targetKey = tokens[tokens.length - 1];\n\n const listener = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n if (\n !enableOnFormTags &&\n target &&\n ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName)\n ) {\n return;\n }\n\n const expectsCtrl = tokens.includes('ctrl');\n const expectsMeta = tokens.includes('meta');\n const expectsMod = tokens.includes('mod');\n const expectsShift = tokens.includes('shift');\n const expectsAlt = tokens.includes('alt');\n\n const isMacLike =\n typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform);\n const modOk = expectsMod ? (isMacLike ? event.metaKey : event.ctrlKey) : true;\n\n if (\n event.key.toLowerCase() === targetKey &&\n (expectsCtrl ? event.ctrlKey : !expectsMod || !event.ctrlKey || isMacLike) &&\n (expectsMeta ? event.metaKey : !expectsMod || !event.metaKey || !isMacLike) &&\n modOk &&\n (expectsShift ? event.shiftKey : !event.shiftKey || expectsShift) &&\n (expectsAlt ? event.altKey : !event.altKey || expectsAlt)\n ) {\n if (preventDefault) event.preventDefault();\n handler(event);\n }\n };\n\n document.addEventListener('keydown', listener);\n return () => document.removeEventListener('keydown', listener);\n }, [keys, handler, options]);\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@structyl/hooks",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "SSR-safe, tree-shakeable React hooks for state, refs, DOM, browser APIs, and performance, including useControllableState for headless UI",
8
+ "keywords": [
9
+ "react",
10
+ "typescript",
11
+ "structyl",
12
+ "hooks",
13
+ "react-hooks",
14
+ "ssr",
15
+ "use-controllable-state",
16
+ "use-debounce",
17
+ "use-media-query",
18
+ "use-local-storage",
19
+ "tree-shakeable"
20
+ ],
21
+ "license": "MIT",
22
+ "type": "module",
23
+ "sideEffects": false,
24
+ "main": "./dist/index.cjs",
25
+ "module": "./dist/index.mjs",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.mjs",
31
+ "require": "./dist/index.cjs"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "peerDependencies": {
38
+ "react": "^18.0.0 || ^19.0.0",
39
+ "react-dom": "^18.0.0 || ^19.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/react": "^19.0.0",
43
+ "react": "^19.0.0",
44
+ "tsup": "^8.3.0",
45
+ "typescript": "^5.6.3",
46
+ "vitest": "^2.1.2"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "dev": "tsup --watch",
51
+ "lint": "eslint src",
52
+ "test": "vitest run",
53
+ "test:watch": "vitest",
54
+ "typecheck": "tsc --noEmit",
55
+ "clean": "rm -rf dist .turbo"
56
+ }
57
+ }