@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 +21 -0
- package/README.md +135 -0
- package/dist/index.cjs +335 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +97 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.mjs +309 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +57 -0
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
|
+

|
|
6
|
+

|
|
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"]}
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|