airyhooks 0.1.0 → 0.3.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 +174 -53
- package/dist/commands/add.js +70 -22
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/entry.js +27 -0
- package/dist/commands/entry.js.map +1 -0
- package/dist/commands/init.js +31 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.js +1 -1
- package/dist/commands/list.js.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/config.js +7 -3
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/get-file-extension.js +12 -0
- package/dist/utils/get-file-extension.js.map +1 -0
- package/dist/utils/get-hook-filename.js +12 -0
- package/dist/utils/get-hook-filename.js.map +1 -0
- package/dist/utils/get-hook-template.js +9 -1071
- package/dist/utils/get-hook-template.js.map +1 -1
- package/dist/utils/hook-templates.js +3386 -0
- package/dist/utils/hook-templates.js.map +1 -0
- package/dist/utils/registry.js +40 -0
- package/dist/utils/registry.js.map +1 -1
- package/package.json +9 -4
- package/dist/commands/add.d.ts +0 -2
- package/dist/commands/add.d.ts.map +0 -1
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/list.d.ts +0 -2
- package/dist/commands/list.d.ts.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/utils/config.d.ts +0 -6
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/get-hook-template.d.ts +0 -2
- package/dist/utils/get-hook-template.d.ts.map +0 -1
- package/dist/utils/registry.d.ts +0 -7
- package/dist/utils/registry.d.ts.map +0 -1
|
@@ -1,1076 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
// Generated by: pnpm --filter @airyhooks/hooks build:templates
|
|
3
|
-
// Source: packages/hooks/src/*/use*.ts
|
|
4
|
-
const templates = {
|
|
5
|
-
useBoolean: `import { useCallback, useState } from "react";
|
|
6
|
-
|
|
1
|
+
import { templates } from "./hook-templates.js";
|
|
7
2
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @param initialValue - Initial boolean value (default: false)
|
|
11
|
-
* @returns Tuple of [value, { setTrue, setFalse, toggle }]
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* const [isEnabled, handlers] = useBoolean(false);
|
|
15
|
-
*
|
|
16
|
-
* return (
|
|
17
|
-
* <>
|
|
18
|
-
* <button onClick={handlers.toggle}>Toggle</button>
|
|
19
|
-
* <button onClick={handlers.setTrue}>Enable</button>
|
|
20
|
-
* <button onClick={handlers.setFalse}>Disable</button>
|
|
21
|
-
* </>
|
|
22
|
-
* );
|
|
3
|
+
* Get the hook template from the generated template file.
|
|
23
4
|
*/
|
|
24
|
-
export function useBoolean(initialValue = false): [
|
|
25
|
-
boolean,
|
|
26
|
-
{
|
|
27
|
-
setFalse: () => void;
|
|
28
|
-
setTrue: () => void;
|
|
29
|
-
toggle: () => void;
|
|
30
|
-
},
|
|
31
|
-
] {
|
|
32
|
-
const [value, toggle, setValue] = useToggle(initialValue);
|
|
33
|
-
|
|
34
|
-
return [
|
|
35
|
-
value,
|
|
36
|
-
{
|
|
37
|
-
setFalse: useCallback(() => {
|
|
38
|
-
setValue(false);
|
|
39
|
-
}, [setValue]),
|
|
40
|
-
setTrue: useCallback(() => {
|
|
41
|
-
setValue(true);
|
|
42
|
-
}, [setValue]),
|
|
43
|
-
toggle,
|
|
44
|
-
},
|
|
45
|
-
];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Toggle a boolean value with a callback.
|
|
50
|
-
*
|
|
51
|
-
* @param initialValue - Initial boolean value (default: false)
|
|
52
|
-
* @returns Tuple of [value, toggle, setValue]
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* const [isOpen, toggle] = useToggle(false);
|
|
56
|
-
*
|
|
57
|
-
* return (
|
|
58
|
-
* <>
|
|
59
|
-
* <button onClick={toggle}>Toggle</button>
|
|
60
|
-
* {isOpen && <div>Content</div>}
|
|
61
|
-
* </>
|
|
62
|
-
* );
|
|
63
|
-
*/
|
|
64
|
-
export function useToggle(
|
|
65
|
-
initialValue = false,
|
|
66
|
-
): [boolean, () => void, (value: boolean) => void] {
|
|
67
|
-
const [value, setValue] = useState(initialValue);
|
|
68
|
-
|
|
69
|
-
const toggle = useCallback(() => {
|
|
70
|
-
setValue((prev) => !prev);
|
|
71
|
-
}, []);
|
|
72
|
-
|
|
73
|
-
return [value, toggle, setValue];
|
|
74
|
-
}
|
|
75
|
-
`,
|
|
76
|
-
useClickAway: `import { useEffect } from "react";
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Detects clicks outside of a target element.
|
|
80
|
-
*
|
|
81
|
-
* @param ref - React ref to the target element
|
|
82
|
-
* @param callback - Function to call when click outside is detected
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* const ref = useRef<HTMLDivElement>(null);
|
|
86
|
-
*
|
|
87
|
-
* useClickAway(ref, () => {
|
|
88
|
-
* setIsOpen(false);
|
|
89
|
-
* });
|
|
90
|
-
*
|
|
91
|
-
* return <div ref={ref}>Content</div>;
|
|
92
|
-
*/
|
|
93
|
-
export function useClickAway<T extends HTMLElement>(
|
|
94
|
-
ref: React.RefObject<null | T>,
|
|
95
|
-
callback: () => void,
|
|
96
|
-
): void {
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
99
|
-
const element = ref.current;
|
|
100
|
-
if (element && !element.contains(event.target as Node)) {
|
|
101
|
-
callback();
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
106
|
-
return () => {
|
|
107
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
108
|
-
};
|
|
109
|
-
}, [ref, callback]);
|
|
110
|
-
}
|
|
111
|
-
`,
|
|
112
|
-
useCounter: `import { useCallback, useState } from "react";
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Manages numeric state with increment, decrement, reset, and set methods.
|
|
116
|
-
*
|
|
117
|
-
* @param initialValue - Initial numeric value (default: 0)
|
|
118
|
-
* @returns Tuple of [value, { increment, decrement, reset, set }]
|
|
119
|
-
*
|
|
120
|
-
* @example
|
|
121
|
-
* const [count, { increment, decrement, reset }] = useCounter(0);
|
|
122
|
-
*
|
|
123
|
-
* return (
|
|
124
|
-
* <>
|
|
125
|
-
* <p>Count: {count}</p>
|
|
126
|
-
* <button onClick={() => increment()}>+1</button>
|
|
127
|
-
* <button onClick={() => decrement()}>-1</button>
|
|
128
|
-
* <button onClick={() => increment(5)}>+5</button>
|
|
129
|
-
* <button onClick={() => reset()}>Reset</button>
|
|
130
|
-
* </>
|
|
131
|
-
* );
|
|
132
|
-
*/
|
|
133
|
-
export function useCounter(initialValue = 0): [
|
|
134
|
-
number,
|
|
135
|
-
{
|
|
136
|
-
decrement: (amount?: number) => void;
|
|
137
|
-
increment: (amount?: number) => void;
|
|
138
|
-
reset: () => void;
|
|
139
|
-
set: (value: ((prev: number) => number) | number) => void;
|
|
140
|
-
},
|
|
141
|
-
] {
|
|
142
|
-
const [count, setCount] = useState<number>(initialValue);
|
|
143
|
-
|
|
144
|
-
const increment = useCallback((amount = 1) => {
|
|
145
|
-
setCount((prev) => prev + amount);
|
|
146
|
-
}, []);
|
|
147
|
-
|
|
148
|
-
const decrement = useCallback((amount = 1) => {
|
|
149
|
-
setCount((prev) => prev - amount);
|
|
150
|
-
}, []);
|
|
151
|
-
|
|
152
|
-
const reset = useCallback(() => {
|
|
153
|
-
setCount(initialValue);
|
|
154
|
-
}, [initialValue]);
|
|
155
|
-
|
|
156
|
-
const set = useCallback((value: ((prev: number) => number) | number) => {
|
|
157
|
-
setCount(value);
|
|
158
|
-
}, []);
|
|
159
|
-
|
|
160
|
-
return [
|
|
161
|
-
count,
|
|
162
|
-
{
|
|
163
|
-
decrement,
|
|
164
|
-
increment,
|
|
165
|
-
reset,
|
|
166
|
-
set,
|
|
167
|
-
},
|
|
168
|
-
];
|
|
169
|
-
}
|
|
170
|
-
`,
|
|
171
|
-
useDebounce: `import { useEffect, useState } from "react";
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Debounces a value by delaying updates until after the specified delay.
|
|
175
|
-
*
|
|
176
|
-
* @param value - The value to debounce
|
|
177
|
-
* @param delay - The delay in milliseconds (default: 500ms)
|
|
178
|
-
* @returns The debounced value
|
|
179
|
-
*
|
|
180
|
-
* @example
|
|
181
|
-
* const [search, setSearch] = useState("");
|
|
182
|
-
* const debouncedSearch = useDebounce(search, 300);
|
|
183
|
-
*
|
|
184
|
-
* useEffect(() => {
|
|
185
|
-
* // This effect runs 300ms after the user stops typing
|
|
186
|
-
* fetchResults(debouncedSearch);
|
|
187
|
-
* }, [debouncedSearch]);
|
|
188
|
-
*/
|
|
189
|
-
export function useDebounce<T>(value: T, delay = 500): T {
|
|
190
|
-
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
191
|
-
|
|
192
|
-
useEffect(() => {
|
|
193
|
-
const timer = setTimeout(() => {
|
|
194
|
-
setDebouncedValue(value);
|
|
195
|
-
}, delay);
|
|
196
|
-
|
|
197
|
-
return () => {
|
|
198
|
-
clearTimeout(timer);
|
|
199
|
-
};
|
|
200
|
-
}, [value, delay]);
|
|
201
|
-
|
|
202
|
-
return debouncedValue;
|
|
203
|
-
}
|
|
204
|
-
`,
|
|
205
|
-
useHover: `import { useCallback, useRef, useState } from "react";
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Tracks mouse hover state on a DOM element via ref.
|
|
209
|
-
*
|
|
210
|
-
* @returns Tuple of [isHovered, ref]
|
|
211
|
-
*
|
|
212
|
-
* @example
|
|
213
|
-
* const [isHovered, ref] = useHover();
|
|
214
|
-
*
|
|
215
|
-
* return (
|
|
216
|
-
* <div
|
|
217
|
-
* ref={ref}
|
|
218
|
-
* style={{
|
|
219
|
-
* backgroundColor: isHovered ? "blue" : "gray",
|
|
220
|
-
* }}
|
|
221
|
-
* >
|
|
222
|
-
* Hover me!
|
|
223
|
-
* </div>
|
|
224
|
-
* );
|
|
225
|
-
*/
|
|
226
|
-
export function useHover<T extends HTMLElement = HTMLElement>(): [
|
|
227
|
-
boolean,
|
|
228
|
-
React.RefObject<T>,
|
|
229
|
-
] {
|
|
230
|
-
const ref = useRef<T>(null);
|
|
231
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
232
|
-
|
|
233
|
-
const handleMouseEnter = useCallback(() => {
|
|
234
|
-
setIsHovered(true);
|
|
235
|
-
}, []);
|
|
236
|
-
|
|
237
|
-
const handleMouseLeave = useCallback(() => {
|
|
238
|
-
setIsHovered(false);
|
|
239
|
-
}, []);
|
|
240
|
-
|
|
241
|
-
// Attach event listeners to the ref
|
|
242
|
-
const setRef = useCallback(
|
|
243
|
-
(element: null | T) => {
|
|
244
|
-
if (ref.current) {
|
|
245
|
-
ref.current.removeEventListener("mouseenter", handleMouseEnter);
|
|
246
|
-
ref.current.removeEventListener("mouseleave", handleMouseLeave);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (element) {
|
|
250
|
-
element.addEventListener("mouseenter", handleMouseEnter);
|
|
251
|
-
element.addEventListener("mouseleave", handleMouseLeave);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
ref.current = element;
|
|
255
|
-
},
|
|
256
|
-
[handleMouseEnter, handleMouseLeave],
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
// Return a proxy ref that updates the internal ref
|
|
260
|
-
return [
|
|
261
|
-
isHovered,
|
|
262
|
-
{
|
|
263
|
-
get current() {
|
|
264
|
-
return ref.current;
|
|
265
|
-
},
|
|
266
|
-
set current(element: null | T) {
|
|
267
|
-
setRef(element);
|
|
268
|
-
},
|
|
269
|
-
} as React.RefObject<T>,
|
|
270
|
-
];
|
|
271
|
-
}
|
|
272
|
-
`,
|
|
273
|
-
useInterval: `import { useEffect } from "react";
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Re-renders component at specified interval.
|
|
277
|
-
*
|
|
278
|
-
* @param callback - Function to call on each interval
|
|
279
|
-
* @param delay - Interval delay in milliseconds (null to pause)
|
|
280
|
-
*
|
|
281
|
-
* @example
|
|
282
|
-
* useInterval(() => {
|
|
283
|
-
* setTime(new Date());
|
|
284
|
-
* }, 1000);
|
|
285
|
-
*/
|
|
286
|
-
export function useInterval(callback: () => void, delay: null | number): void {
|
|
287
|
-
useEffect(() => {
|
|
288
|
-
if (delay === null) return;
|
|
289
|
-
|
|
290
|
-
const interval = setInterval(callback, delay);
|
|
291
|
-
return () => {
|
|
292
|
-
clearInterval(interval);
|
|
293
|
-
};
|
|
294
|
-
}, [callback, delay]);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Re-renders component after a timeout.
|
|
299
|
-
*
|
|
300
|
-
* @param callback - Function to call after timeout
|
|
301
|
-
* @param delay - Timeout delay in milliseconds
|
|
302
|
-
*
|
|
303
|
-
* @example
|
|
304
|
-
* useTimeout(() => {
|
|
305
|
-
* console.log("Timeout completed");
|
|
306
|
-
* }, 2000);
|
|
307
|
-
*/
|
|
308
|
-
export function useTimeout(callback: () => void, delay: number): void {
|
|
309
|
-
useEffect(() => {
|
|
310
|
-
const timeout = setTimeout(callback, delay);
|
|
311
|
-
return () => {
|
|
312
|
-
clearTimeout(timeout);
|
|
313
|
-
};
|
|
314
|
-
}, [callback, delay]);
|
|
315
|
-
}
|
|
316
|
-
`,
|
|
317
|
-
useKeyPress: `import { useEffect, useState } from "react";
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Detects if a specific keyboard key is currently pressed.
|
|
321
|
-
*
|
|
322
|
-
* @param targetKey - The key to detect (e.g., "Enter", "ArrowUp", " " for space)
|
|
323
|
-
* @returns Whether the key is currently pressed
|
|
324
|
-
*
|
|
325
|
-
* @example
|
|
326
|
-
* const isEnterPressed = useKeyPress("Enter");
|
|
327
|
-
* const isArrowUpPressed = useKeyPress("ArrowUp");
|
|
328
|
-
*
|
|
329
|
-
* return (
|
|
330
|
-
* <div>
|
|
331
|
-
* <p>Enter pressed: {isEnterPressed ? "Yes" : "No"}</p>
|
|
332
|
-
* <p>Arrow Up pressed: {isArrowUpPressed ? "Yes" : "No"}</p>
|
|
333
|
-
* </div>
|
|
334
|
-
* );
|
|
335
|
-
*/
|
|
336
|
-
export function useKeyPress(targetKey: string): boolean {
|
|
337
|
-
const [isKeyPressed, setIsKeyPressed] = useState(false);
|
|
338
|
-
|
|
339
|
-
useEffect(() => {
|
|
340
|
-
const handleKeyDown = (event: KeyboardEvent) => {
|
|
341
|
-
if (event.key === targetKey) {
|
|
342
|
-
setIsKeyPressed(true);
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
const handleKeyUp = (event: KeyboardEvent) => {
|
|
347
|
-
if (event.key === targetKey) {
|
|
348
|
-
setIsKeyPressed(false);
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
353
|
-
window.addEventListener("keyup", handleKeyUp);
|
|
354
|
-
|
|
355
|
-
return () => {
|
|
356
|
-
window.removeEventListener("keydown", handleKeyDown);
|
|
357
|
-
window.removeEventListener("keyup", handleKeyUp);
|
|
358
|
-
};
|
|
359
|
-
}, [targetKey]);
|
|
360
|
-
|
|
361
|
-
return isKeyPressed;
|
|
362
|
-
}
|
|
363
|
-
`,
|
|
364
|
-
useLocalStorage: `import { useCallback, useEffect, useState } from "react";
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Syncs state with localStorage, persisting across browser sessions.
|
|
368
|
-
*
|
|
369
|
-
* @param key - The localStorage key
|
|
370
|
-
* @param initialValue - The initial value (used if no stored value exists)
|
|
371
|
-
* @returns A tuple of [value, setValue, removeValue]
|
|
372
|
-
*
|
|
373
|
-
* @example
|
|
374
|
-
* const [theme, setTheme, removeTheme] = useLocalStorage("theme", "light");
|
|
375
|
-
*
|
|
376
|
-
* // Update the theme (automatically persisted)
|
|
377
|
-
* setTheme("dark");
|
|
378
|
-
*
|
|
379
|
-
* // Remove from localStorage
|
|
380
|
-
* removeTheme();
|
|
381
|
-
*/
|
|
382
|
-
export function useLocalStorage<T>(
|
|
383
|
-
key: string,
|
|
384
|
-
initialValue: T,
|
|
385
|
-
): [T, (value: ((prev: T) => T) | T) => void, () => void] {
|
|
386
|
-
// Get initial value from localStorage or use provided initial value
|
|
387
|
-
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
388
|
-
if (typeof window === "undefined") {
|
|
389
|
-
return initialValue;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
try {
|
|
393
|
-
const item = window.localStorage.getItem(key);
|
|
394
|
-
return item ? (JSON.parse(item) as T) : initialValue;
|
|
395
|
-
} catch (error) {
|
|
396
|
-
console.warn(\`Error reading localStorage key "\${key}":\`, error);
|
|
397
|
-
return initialValue;
|
|
398
|
-
}
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
// Update localStorage when value changes
|
|
402
|
-
const setValue = useCallback(
|
|
403
|
-
(value: ((prev: T) => T) | T) => {
|
|
404
|
-
try {
|
|
405
|
-
setStoredValue((prev) => {
|
|
406
|
-
const valueToStore = value instanceof Function ? value(prev) : value;
|
|
407
|
-
if (typeof window !== "undefined") {
|
|
408
|
-
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
409
|
-
}
|
|
410
|
-
return valueToStore;
|
|
411
|
-
});
|
|
412
|
-
} catch (error) {
|
|
413
|
-
console.warn(\`Error setting localStorage key "\${key}":\`, error);
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
|
-
[key],
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
// Remove from localStorage
|
|
420
|
-
const removeValue = useCallback(() => {
|
|
421
|
-
try {
|
|
422
|
-
if (typeof window !== "undefined") {
|
|
423
|
-
window.localStorage.removeItem(key);
|
|
424
|
-
}
|
|
425
|
-
setStoredValue(initialValue);
|
|
426
|
-
} catch (error) {
|
|
427
|
-
console.warn(\`Error removing localStorage key "\${key}":\`, error);
|
|
428
|
-
}
|
|
429
|
-
}, [key, initialValue]);
|
|
430
|
-
|
|
431
|
-
// Listen for changes in other tabs/windows
|
|
432
|
-
useEffect(() => {
|
|
433
|
-
const handleStorageChange = (event: StorageEvent) => {
|
|
434
|
-
if (event.key === key && event.newValue !== null) {
|
|
435
|
-
try {
|
|
436
|
-
setStoredValue(JSON.parse(event.newValue) as T);
|
|
437
|
-
} catch (error) {
|
|
438
|
-
console.warn(\`Error parsing localStorage key "\${key}":\`, error);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
window.addEventListener("storage", handleStorageChange);
|
|
444
|
-
return () => {
|
|
445
|
-
window.removeEventListener("storage", handleStorageChange);
|
|
446
|
-
};
|
|
447
|
-
}, [key]);
|
|
448
|
-
|
|
449
|
-
return [storedValue, setValue, removeValue];
|
|
450
|
-
}
|
|
451
|
-
`,
|
|
452
|
-
useMedia: `import { useEffect, useState } from "react";
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Reacts to CSS media query changes.
|
|
456
|
-
*
|
|
457
|
-
* @param query - CSS media query string (e.g., "(max-width: 768px)")
|
|
458
|
-
* @returns Whether the media query matches
|
|
459
|
-
*
|
|
460
|
-
* @example
|
|
461
|
-
* const isMobile = useMedia("(max-width: 768px)");
|
|
462
|
-
* const isDarkMode = useMedia("(prefers-color-scheme: dark)");
|
|
463
|
-
*
|
|
464
|
-
* return (
|
|
465
|
-
* <div>
|
|
466
|
-
* <p>Is mobile: {isMobile ? "Yes" : "No"}</p>
|
|
467
|
-
* <p>Dark mode: {isDarkMode ? "Yes" : "No"}</p>
|
|
468
|
-
* </div>
|
|
469
|
-
* );
|
|
470
|
-
*/
|
|
471
|
-
export function useMedia(query: string): boolean {
|
|
472
|
-
const [matches, setMatches] = useState(false);
|
|
473
|
-
|
|
474
|
-
useEffect(() => {
|
|
475
|
-
// Check if window is defined (SSR safety)
|
|
476
|
-
if (typeof window === "undefined") {
|
|
477
|
-
return undefined;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
try {
|
|
481
|
-
const mediaQueryList = window.matchMedia(query);
|
|
482
|
-
|
|
483
|
-
// Set initial value
|
|
484
|
-
setMatches(mediaQueryList.matches);
|
|
485
|
-
|
|
486
|
-
// Create listener function
|
|
487
|
-
const handleChange = (e: MediaQueryListEvent) => {
|
|
488
|
-
setMatches(e.matches);
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
// Modern browsers use addEventListener
|
|
492
|
-
mediaQueryList.addEventListener("change", handleChange);
|
|
493
|
-
return () => {
|
|
494
|
-
mediaQueryList.removeEventListener("change", handleChange);
|
|
495
|
-
};
|
|
496
|
-
} catch (error) {
|
|
497
|
-
console.warn(\`Invalid media query: "\${query}"\`, error);
|
|
498
|
-
return undefined;
|
|
499
|
-
}
|
|
500
|
-
}, [query]);
|
|
501
|
-
|
|
502
|
-
return matches;
|
|
503
|
-
}
|
|
504
|
-
`,
|
|
505
|
-
useMount: `import { useEffect, useRef } from "react";
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* Calls a callback on component mount.
|
|
509
|
-
*
|
|
510
|
-
* @param callback - Function to call on mount
|
|
511
|
-
*
|
|
512
|
-
* @example
|
|
513
|
-
* useMount(() => {
|
|
514
|
-
* console.log("Component mounted");
|
|
515
|
-
* // Initialize resources
|
|
516
|
-
* });
|
|
517
|
-
*/
|
|
518
|
-
export function useMount(callback: () => void): void {
|
|
519
|
-
useEffect(callback, []);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Calls a callback on component unmount.
|
|
524
|
-
*
|
|
525
|
-
* @param callback - Function to call on unmount
|
|
526
|
-
*
|
|
527
|
-
* @example
|
|
528
|
-
* useUnmount(() => {
|
|
529
|
-
* console.log("Component unmounting");
|
|
530
|
-
* // Cleanup resources
|
|
531
|
-
* });
|
|
532
|
-
*/
|
|
533
|
-
export function useUnmount(callback: () => void): void {
|
|
534
|
-
const callbackRef = useRef(callback);
|
|
535
|
-
|
|
536
|
-
useEffect(() => {
|
|
537
|
-
callbackRef.current = callback;
|
|
538
|
-
}, [callback]);
|
|
539
|
-
|
|
540
|
-
useEffect(() => {
|
|
541
|
-
return () => {
|
|
542
|
-
callbackRef.current();
|
|
543
|
-
};
|
|
544
|
-
}, []);
|
|
545
|
-
}
|
|
546
|
-
`,
|
|
547
|
-
usePrevious: `import { useEffect, useRef } from "react";
|
|
548
|
-
|
|
549
|
-
/**
|
|
550
|
-
* Tracks the previous value or prop.
|
|
551
|
-
*
|
|
552
|
-
* @param value - The current value to track
|
|
553
|
-
* @returns The previous value from the last render
|
|
554
|
-
*
|
|
555
|
-
* @example
|
|
556
|
-
* const [count, setCount] = useState(0);
|
|
557
|
-
* const prevCount = usePrevious(count);
|
|
558
|
-
*
|
|
559
|
-
* useEffect(() => {
|
|
560
|
-
* console.log(\`Current: \${count}, Previous: \${prevCount}\`);
|
|
561
|
-
* }, [count, prevCount]);
|
|
562
|
-
*/
|
|
563
|
-
export function usePrevious<T>(value: T): T | undefined {
|
|
564
|
-
const ref = useRef<T | undefined>(undefined);
|
|
565
|
-
|
|
566
|
-
useEffect(() => {
|
|
567
|
-
ref.current = value;
|
|
568
|
-
}, [value]);
|
|
569
|
-
|
|
570
|
-
return ref.current;
|
|
571
|
-
}
|
|
572
|
-
`,
|
|
573
|
-
useScroll: `import { useCallback, useEffect, useRef, useState } from "react";
|
|
574
|
-
|
|
575
|
-
interface ScrollPosition {
|
|
576
|
-
x: number;
|
|
577
|
-
y: number;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* Tracks scroll position of an element or the window.
|
|
582
|
-
*
|
|
583
|
-
* @param ref - Optional ref to an element. If not provided, tracks window scroll
|
|
584
|
-
* @returns Object with x and y scroll positions
|
|
585
|
-
*
|
|
586
|
-
* @example
|
|
587
|
-
* // Track window scroll
|
|
588
|
-
* const windowScroll = useScroll();
|
|
589
|
-
* console.log(windowScroll.x, windowScroll.y);
|
|
590
|
-
*
|
|
591
|
-
* // Track element scroll
|
|
592
|
-
* const [ref, elementScroll] = useScroll<HTMLDivElement>();
|
|
593
|
-
* return <div ref={ref}>Content</div>;
|
|
594
|
-
*/
|
|
595
|
-
export function useScroll(
|
|
596
|
-
ref?: React.RefObject<HTMLElement | null>,
|
|
597
|
-
): ScrollPosition {
|
|
598
|
-
const [scroll, setScroll] = useState<ScrollPosition>({ x: 0, y: 0 });
|
|
599
|
-
|
|
600
|
-
const handleScroll = useCallback(() => {
|
|
601
|
-
if (ref?.current) {
|
|
602
|
-
setScroll({
|
|
603
|
-
x: ref.current.scrollLeft,
|
|
604
|
-
y: ref.current.scrollTop,
|
|
605
|
-
});
|
|
606
|
-
} else if (typeof window !== "undefined") {
|
|
607
|
-
setScroll({
|
|
608
|
-
x: window.scrollX,
|
|
609
|
-
y: window.scrollY,
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
}, [ref]);
|
|
613
|
-
|
|
614
|
-
useEffect(() => {
|
|
615
|
-
// Set initial scroll position
|
|
616
|
-
handleScroll();
|
|
617
|
-
|
|
618
|
-
if (ref?.current) {
|
|
619
|
-
// Listen to element scroll
|
|
620
|
-
const target = ref.current;
|
|
621
|
-
target.addEventListener("scroll", handleScroll);
|
|
622
|
-
return () => {
|
|
623
|
-
target.removeEventListener("scroll", handleScroll);
|
|
624
|
-
};
|
|
625
|
-
} else if (typeof window !== "undefined") {
|
|
626
|
-
// Listen to window scroll
|
|
627
|
-
window.addEventListener("scroll", handleScroll);
|
|
628
|
-
return () => {
|
|
629
|
-
window.removeEventListener("scroll", handleScroll);
|
|
630
|
-
};
|
|
631
|
-
}
|
|
632
|
-
}, [ref, handleScroll]);
|
|
633
|
-
|
|
634
|
-
return scroll;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
/**
|
|
638
|
-
* Tracks scroll position with ref attachment for element scroll.
|
|
639
|
-
*
|
|
640
|
-
* @returns Tuple of [ref, scrollPosition]
|
|
641
|
-
*
|
|
642
|
-
* @example
|
|
643
|
-
* const [ref, scroll] = useScrollElement<HTMLDivElement>();
|
|
644
|
-
*
|
|
645
|
-
* return (
|
|
646
|
-
* <div
|
|
647
|
-
* ref={ref}
|
|
648
|
-
* style={{ height: "200px", overflow: "auto" }}
|
|
649
|
-
* >
|
|
650
|
-
* <p>Scroll position: X: {scroll.x}, Y: {scroll.y}</p>
|
|
651
|
-
* </div>
|
|
652
|
-
* );
|
|
653
|
-
*/
|
|
654
|
-
export function useScrollElement<T extends HTMLElement = HTMLElement>(): [
|
|
655
|
-
React.RefObject<null | T>,
|
|
656
|
-
ScrollPosition,
|
|
657
|
-
] {
|
|
658
|
-
const ref = useRef<T>(null);
|
|
659
|
-
const scroll = useScroll(ref as React.RefObject<HTMLElement | null>);
|
|
660
|
-
|
|
661
|
-
return [ref, scroll];
|
|
662
|
-
}
|
|
663
|
-
`,
|
|
664
|
-
useScrollElement: `import { useCallback, useEffect, useRef, useState } from "react";
|
|
665
|
-
|
|
666
|
-
interface ScrollPosition {
|
|
667
|
-
x: number;
|
|
668
|
-
y: number;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
/**
|
|
672
|
-
* Tracks scroll position of an element or the window.
|
|
673
|
-
*
|
|
674
|
-
* @param ref - Optional ref to an element. If not provided, tracks window scroll
|
|
675
|
-
* @returns Object with x and y scroll positions
|
|
676
|
-
*
|
|
677
|
-
* @example
|
|
678
|
-
* // Track window scroll
|
|
679
|
-
* const windowScroll = useScroll();
|
|
680
|
-
* console.log(windowScroll.x, windowScroll.y);
|
|
681
|
-
*
|
|
682
|
-
* // Track element scroll
|
|
683
|
-
* const [ref, elementScroll] = useScroll<HTMLDivElement>();
|
|
684
|
-
* return <div ref={ref}>Content</div>;
|
|
685
|
-
*/
|
|
686
|
-
export function useScroll(
|
|
687
|
-
ref?: React.RefObject<HTMLElement | null>,
|
|
688
|
-
): ScrollPosition {
|
|
689
|
-
const [scroll, setScroll] = useState<ScrollPosition>({ x: 0, y: 0 });
|
|
690
|
-
|
|
691
|
-
const handleScroll = useCallback(() => {
|
|
692
|
-
if (ref?.current) {
|
|
693
|
-
setScroll({
|
|
694
|
-
x: ref.current.scrollLeft,
|
|
695
|
-
y: ref.current.scrollTop,
|
|
696
|
-
});
|
|
697
|
-
} else if (typeof window !== "undefined") {
|
|
698
|
-
setScroll({
|
|
699
|
-
x: window.scrollX,
|
|
700
|
-
y: window.scrollY,
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
}, [ref]);
|
|
704
|
-
|
|
705
|
-
useEffect(() => {
|
|
706
|
-
// Set initial scroll position
|
|
707
|
-
handleScroll();
|
|
708
|
-
|
|
709
|
-
if (ref?.current) {
|
|
710
|
-
// Listen to element scroll
|
|
711
|
-
const target = ref.current;
|
|
712
|
-
target.addEventListener("scroll", handleScroll);
|
|
713
|
-
return () => {
|
|
714
|
-
target.removeEventListener("scroll", handleScroll);
|
|
715
|
-
};
|
|
716
|
-
} else if (typeof window !== "undefined") {
|
|
717
|
-
// Listen to window scroll
|
|
718
|
-
window.addEventListener("scroll", handleScroll);
|
|
719
|
-
return () => {
|
|
720
|
-
window.removeEventListener("scroll", handleScroll);
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
}, [ref, handleScroll]);
|
|
724
|
-
|
|
725
|
-
return scroll;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
/**
|
|
729
|
-
* Tracks scroll position with ref attachment for element scroll.
|
|
730
|
-
*
|
|
731
|
-
* @returns Tuple of [ref, scrollPosition]
|
|
732
|
-
*
|
|
733
|
-
* @example
|
|
734
|
-
* const [ref, scroll] = useScrollElement<HTMLDivElement>();
|
|
735
|
-
*
|
|
736
|
-
* return (
|
|
737
|
-
* <div
|
|
738
|
-
* ref={ref}
|
|
739
|
-
* style={{ height: "200px", overflow: "auto" }}
|
|
740
|
-
* >
|
|
741
|
-
* <p>Scroll position: X: {scroll.x}, Y: {scroll.y}</p>
|
|
742
|
-
* </div>
|
|
743
|
-
* );
|
|
744
|
-
*/
|
|
745
|
-
export function useScrollElement<T extends HTMLElement = HTMLElement>(): [
|
|
746
|
-
React.RefObject<null | T>,
|
|
747
|
-
ScrollPosition,
|
|
748
|
-
] {
|
|
749
|
-
const ref = useRef<T>(null);
|
|
750
|
-
const scroll = useScroll(ref as React.RefObject<HTMLElement | null>);
|
|
751
|
-
|
|
752
|
-
return [ref, scroll];
|
|
753
|
-
}
|
|
754
|
-
`,
|
|
755
|
-
useSessionStorage: `import { useCallback, useState } from "react";
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* Syncs state with sessionStorage, persisting only for the current session.
|
|
759
|
-
*
|
|
760
|
-
* @param key - The sessionStorage key
|
|
761
|
-
* @param initialValue - The initial value (used if no stored value exists)
|
|
762
|
-
* @returns A tuple of [value, setValue, removeValue]
|
|
763
|
-
*
|
|
764
|
-
* @example
|
|
765
|
-
* const [sessionData, setSessionData, removeSessionData] = useSessionStorage("session", "default");
|
|
766
|
-
*
|
|
767
|
-
* // Update the session data (automatically persisted)
|
|
768
|
-
* setSessionData("newData");
|
|
769
|
-
*
|
|
770
|
-
* // Remove from sessionStorage
|
|
771
|
-
* removeSessionData();
|
|
772
|
-
*/
|
|
773
|
-
export function useSessionStorage<T>(
|
|
774
|
-
key: string,
|
|
775
|
-
initialValue: T,
|
|
776
|
-
): [T, (value: ((prev: T) => T) | T) => void, () => void] {
|
|
777
|
-
// Get initial value from sessionStorage or use provided initial value
|
|
778
|
-
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
779
|
-
if (typeof window === "undefined") {
|
|
780
|
-
return initialValue;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
try {
|
|
784
|
-
const item = window.sessionStorage.getItem(key);
|
|
785
|
-
return item ? (JSON.parse(item) as T) : initialValue;
|
|
786
|
-
} catch (error) {
|
|
787
|
-
console.warn(\`Error reading sessionStorage key "\${key}":\`, error);
|
|
788
|
-
return initialValue;
|
|
789
|
-
}
|
|
790
|
-
});
|
|
791
|
-
|
|
792
|
-
// Update sessionStorage when value changes
|
|
793
|
-
const setValue = useCallback(
|
|
794
|
-
(value: ((prev: T) => T) | T) => {
|
|
795
|
-
try {
|
|
796
|
-
setStoredValue((prev) => {
|
|
797
|
-
const valueToStore = value instanceof Function ? value(prev) : value;
|
|
798
|
-
if (typeof window !== "undefined") {
|
|
799
|
-
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
800
|
-
}
|
|
801
|
-
return valueToStore;
|
|
802
|
-
});
|
|
803
|
-
} catch (error) {
|
|
804
|
-
console.warn(\`Error setting sessionStorage key "\${key}":\`, error);
|
|
805
|
-
}
|
|
806
|
-
},
|
|
807
|
-
[key],
|
|
808
|
-
);
|
|
809
|
-
|
|
810
|
-
// Remove from sessionStorage
|
|
811
|
-
const removeValue = useCallback(() => {
|
|
812
|
-
try {
|
|
813
|
-
if (typeof window !== "undefined") {
|
|
814
|
-
window.sessionStorage.removeItem(key);
|
|
815
|
-
}
|
|
816
|
-
setStoredValue(initialValue);
|
|
817
|
-
} catch (error) {
|
|
818
|
-
console.warn(\`Error removing sessionStorage key "\${key}":\`, error);
|
|
819
|
-
}
|
|
820
|
-
}, [key, initialValue]);
|
|
821
|
-
|
|
822
|
-
return [storedValue, setValue, removeValue];
|
|
823
|
-
}
|
|
824
|
-
`,
|
|
825
|
-
useThrottle: `import { useEffect, useRef, useState } from "react";
|
|
826
|
-
|
|
827
|
-
/**
|
|
828
|
-
* Throttles a value to update at most once per specified interval.
|
|
829
|
-
*
|
|
830
|
-
* @param value - The value to throttle
|
|
831
|
-
* @param interval - The throttle interval in milliseconds (default: 500ms)
|
|
832
|
-
* @returns The throttled value
|
|
833
|
-
*
|
|
834
|
-
* @example
|
|
835
|
-
* const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
836
|
-
* const throttledPosition = useThrottle(position, 100);
|
|
837
|
-
*
|
|
838
|
-
* useEffect(() => {
|
|
839
|
-
* // This effect runs at most every 100ms
|
|
840
|
-
* updateCursor(throttledPosition);
|
|
841
|
-
* }, [throttledPosition]);
|
|
842
|
-
*/
|
|
843
|
-
export function useThrottle<T>(value: T, interval = 500): T {
|
|
844
|
-
const [throttledValue, setThrottledValue] = useState<T>(value);
|
|
845
|
-
const lastUpdated = useRef<number>(Date.now());
|
|
846
|
-
|
|
847
|
-
useEffect(() => {
|
|
848
|
-
const now = Date.now();
|
|
849
|
-
const elapsed = now - lastUpdated.current;
|
|
850
|
-
|
|
851
|
-
if (elapsed >= interval) {
|
|
852
|
-
lastUpdated.current = now;
|
|
853
|
-
setThrottledValue(value);
|
|
854
|
-
} else {
|
|
855
|
-
const timer = setTimeout(() => {
|
|
856
|
-
lastUpdated.current = Date.now();
|
|
857
|
-
setThrottledValue(value);
|
|
858
|
-
}, interval - elapsed);
|
|
859
|
-
|
|
860
|
-
return () => {
|
|
861
|
-
clearTimeout(timer);
|
|
862
|
-
};
|
|
863
|
-
}
|
|
864
|
-
}, [value, interval]);
|
|
865
|
-
|
|
866
|
-
return throttledValue;
|
|
867
|
-
}
|
|
868
|
-
`,
|
|
869
|
-
useTimeout: `import { useEffect } from "react";
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Re-renders component at specified interval.
|
|
873
|
-
*
|
|
874
|
-
* @param callback - Function to call on each interval
|
|
875
|
-
* @param delay - Interval delay in milliseconds (null to pause)
|
|
876
|
-
*
|
|
877
|
-
* @example
|
|
878
|
-
* useInterval(() => {
|
|
879
|
-
* setTime(new Date());
|
|
880
|
-
* }, 1000);
|
|
881
|
-
*/
|
|
882
|
-
export function useInterval(callback: () => void, delay: null | number): void {
|
|
883
|
-
useEffect(() => {
|
|
884
|
-
if (delay === null) return;
|
|
885
|
-
|
|
886
|
-
const interval = setInterval(callback, delay);
|
|
887
|
-
return () => {
|
|
888
|
-
clearInterval(interval);
|
|
889
|
-
};
|
|
890
|
-
}, [callback, delay]);
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
/**
|
|
894
|
-
* Re-renders component after a timeout.
|
|
895
|
-
*
|
|
896
|
-
* @param callback - Function to call after timeout
|
|
897
|
-
* @param delay - Timeout delay in milliseconds
|
|
898
|
-
*
|
|
899
|
-
* @example
|
|
900
|
-
* useTimeout(() => {
|
|
901
|
-
* console.log("Timeout completed");
|
|
902
|
-
* }, 2000);
|
|
903
|
-
*/
|
|
904
|
-
export function useTimeout(callback: () => void, delay: number): void {
|
|
905
|
-
useEffect(() => {
|
|
906
|
-
const timeout = setTimeout(callback, delay);
|
|
907
|
-
return () => {
|
|
908
|
-
clearTimeout(timeout);
|
|
909
|
-
};
|
|
910
|
-
}, [callback, delay]);
|
|
911
|
-
}
|
|
912
|
-
`,
|
|
913
|
-
useToggle: `import { useCallback, useState } from "react";
|
|
914
|
-
|
|
915
|
-
/**
|
|
916
|
-
* Alias for useToggle with boolean semantics.
|
|
917
|
-
*
|
|
918
|
-
* @param initialValue - Initial boolean value (default: false)
|
|
919
|
-
* @returns Tuple of [value, { setTrue, setFalse, toggle }]
|
|
920
|
-
*
|
|
921
|
-
* @example
|
|
922
|
-
* const [isEnabled, handlers] = useBoolean(false);
|
|
923
|
-
*
|
|
924
|
-
* return (
|
|
925
|
-
* <>
|
|
926
|
-
* <button onClick={handlers.toggle}>Toggle</button>
|
|
927
|
-
* <button onClick={handlers.setTrue}>Enable</button>
|
|
928
|
-
* <button onClick={handlers.setFalse}>Disable</button>
|
|
929
|
-
* </>
|
|
930
|
-
* );
|
|
931
|
-
*/
|
|
932
|
-
export function useBoolean(initialValue = false): [
|
|
933
|
-
boolean,
|
|
934
|
-
{
|
|
935
|
-
setFalse: () => void;
|
|
936
|
-
setTrue: () => void;
|
|
937
|
-
toggle: () => void;
|
|
938
|
-
},
|
|
939
|
-
] {
|
|
940
|
-
const [value, toggle, setValue] = useToggle(initialValue);
|
|
941
|
-
|
|
942
|
-
return [
|
|
943
|
-
value,
|
|
944
|
-
{
|
|
945
|
-
setFalse: useCallback(() => {
|
|
946
|
-
setValue(false);
|
|
947
|
-
}, [setValue]),
|
|
948
|
-
setTrue: useCallback(() => {
|
|
949
|
-
setValue(true);
|
|
950
|
-
}, [setValue]),
|
|
951
|
-
toggle,
|
|
952
|
-
},
|
|
953
|
-
];
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
/**
|
|
957
|
-
* Toggle a boolean value with a callback.
|
|
958
|
-
*
|
|
959
|
-
* @param initialValue - Initial boolean value (default: false)
|
|
960
|
-
* @returns Tuple of [value, toggle, setValue]
|
|
961
|
-
*
|
|
962
|
-
* @example
|
|
963
|
-
* const [isOpen, toggle] = useToggle(false);
|
|
964
|
-
*
|
|
965
|
-
* return (
|
|
966
|
-
* <>
|
|
967
|
-
* <button onClick={toggle}>Toggle</button>
|
|
968
|
-
* {isOpen && <div>Content</div>}
|
|
969
|
-
* </>
|
|
970
|
-
* );
|
|
971
|
-
*/
|
|
972
|
-
export function useToggle(
|
|
973
|
-
initialValue = false,
|
|
974
|
-
): [boolean, () => void, (value: boolean) => void] {
|
|
975
|
-
const [value, setValue] = useState(initialValue);
|
|
976
|
-
|
|
977
|
-
const toggle = useCallback(() => {
|
|
978
|
-
setValue((prev) => !prev);
|
|
979
|
-
}, []);
|
|
980
|
-
|
|
981
|
-
return [value, toggle, setValue];
|
|
982
|
-
}
|
|
983
|
-
`,
|
|
984
|
-
useUnmount: `import { useEffect, useRef } from "react";
|
|
985
|
-
|
|
986
|
-
/**
|
|
987
|
-
* Calls a callback on component mount.
|
|
988
|
-
*
|
|
989
|
-
* @param callback - Function to call on mount
|
|
990
|
-
*
|
|
991
|
-
* @example
|
|
992
|
-
* useMount(() => {
|
|
993
|
-
* console.log("Component mounted");
|
|
994
|
-
* // Initialize resources
|
|
995
|
-
* });
|
|
996
|
-
*/
|
|
997
|
-
export function useMount(callback: () => void): void {
|
|
998
|
-
useEffect(callback, []);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
/**
|
|
1002
|
-
* Calls a callback on component unmount.
|
|
1003
|
-
*
|
|
1004
|
-
* @param callback - Function to call on unmount
|
|
1005
|
-
*
|
|
1006
|
-
* @example
|
|
1007
|
-
* useUnmount(() => {
|
|
1008
|
-
* console.log("Component unmounting");
|
|
1009
|
-
* // Cleanup resources
|
|
1010
|
-
* });
|
|
1011
|
-
*/
|
|
1012
|
-
export function useUnmount(callback: () => void): void {
|
|
1013
|
-
const callbackRef = useRef(callback);
|
|
1014
|
-
|
|
1015
|
-
useEffect(() => {
|
|
1016
|
-
callbackRef.current = callback;
|
|
1017
|
-
}, [callback]);
|
|
1018
|
-
|
|
1019
|
-
useEffect(() => {
|
|
1020
|
-
return () => {
|
|
1021
|
-
callbackRef.current();
|
|
1022
|
-
};
|
|
1023
|
-
}, []);
|
|
1024
|
-
}
|
|
1025
|
-
`,
|
|
1026
|
-
useWindowSize: `import { useEffect, useState } from "react";
|
|
1027
|
-
|
|
1028
|
-
interface WindowSize {
|
|
1029
|
-
height: number | undefined;
|
|
1030
|
-
width: number | undefined;
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
/**
|
|
1034
|
-
* Tracks window dimensions.
|
|
1035
|
-
*
|
|
1036
|
-
* @returns Object with width and height of the window
|
|
1037
|
-
*
|
|
1038
|
-
* @example
|
|
1039
|
-
* const { width, height } = useWindowSize();
|
|
1040
|
-
*
|
|
1041
|
-
* return (
|
|
1042
|
-
* <div>
|
|
1043
|
-
* Window size: {width}x{height}
|
|
1044
|
-
* </div>
|
|
1045
|
-
* );
|
|
1046
|
-
*/
|
|
1047
|
-
export function useWindowSize(): WindowSize {
|
|
1048
|
-
const [windowSize, setWindowSize] = useState<WindowSize>({
|
|
1049
|
-
height: undefined,
|
|
1050
|
-
width: undefined,
|
|
1051
|
-
});
|
|
1052
|
-
|
|
1053
|
-
useEffect(() => {
|
|
1054
|
-
const handleResize = () => {
|
|
1055
|
-
setWindowSize({
|
|
1056
|
-
height: window.innerHeight,
|
|
1057
|
-
width: window.innerWidth,
|
|
1058
|
-
});
|
|
1059
|
-
};
|
|
1060
|
-
|
|
1061
|
-
// Call once on mount
|
|
1062
|
-
handleResize();
|
|
1063
|
-
|
|
1064
|
-
window.addEventListener("resize", handleResize);
|
|
1065
|
-
return () => {
|
|
1066
|
-
window.removeEventListener("resize", handleResize);
|
|
1067
|
-
};
|
|
1068
|
-
}, []);
|
|
1069
|
-
|
|
1070
|
-
return windowSize;
|
|
1071
|
-
}
|
|
1072
|
-
`,
|
|
1073
|
-
};
|
|
1074
5
|
export function getHookTemplate(hookName) {
|
|
1075
6
|
const template = templates[hookName];
|
|
1076
7
|
if (!template) {
|
|
@@ -1078,4 +9,11 @@ export function getHookTemplate(hookName) {
|
|
|
1078
9
|
}
|
|
1079
10
|
return template;
|
|
1080
11
|
}
|
|
12
|
+
export function getHookTestTemplate(hookName) {
|
|
13
|
+
const template = templates[`${hookName}_test`];
|
|
14
|
+
if (!template) {
|
|
15
|
+
throw new Error(`Test template for hook "${hookName}" not found`);
|
|
16
|
+
}
|
|
17
|
+
return template;
|
|
18
|
+
}
|
|
1081
19
|
//# sourceMappingURL=get-hook-template.js.map
|