react-hook-toolkit 4.0.0 → 5.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/README.md +289 -36
- package/dist/chunk1516/chunk1617.d.ts +190 -0
- package/dist/chunk1516/chunk1617.js +408 -0
- package/dist/chunk1516/chunk1718.d.ts +324 -0
- package/dist/chunk1516/chunk1718.js +783 -0
- package/dist/chunk1516/chunk1819.d.ts +90 -0
- package/dist/chunk1516/chunk1819.js +235 -0
- package/dist/chunk1516/chunk1920.d.ts +104 -0
- package/dist/chunk1516/chunk1920.js +274 -0
- package/dist/chunk1516/chunk2021.d.ts +219 -0
- package/dist/chunk1516/chunk2021.js +406 -0
- package/dist/chunk1516/chunk2122.d.ts +77 -0
- package/dist/chunk1516/chunk2122.js +193 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +7 -1
- package/package.json +1 -1
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Manages client-side pagination state: current page, page size, total pages,
|
|
4
|
+
* and navigation helpers.
|
|
5
|
+
*/
|
|
6
|
+
export function usePagination(options) {
|
|
7
|
+
const { total, pageSize: initialSize = 10, initialPage = 1 } = options;
|
|
8
|
+
const [page, setPage] = useState(initialPage);
|
|
9
|
+
const [pageSize, setPageSizeState] = useState(initialSize);
|
|
10
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
11
|
+
const clamp = (p) => Math.min(Math.max(p, 1), totalPages);
|
|
12
|
+
const next = useCallback(() => setPage(p => clamp(p + 1)), [totalPages]);
|
|
13
|
+
const prev = useCallback(() => setPage(p => clamp(p - 1)), [totalPages]);
|
|
14
|
+
const goTo = useCallback((p) => setPage(clamp(p)), [totalPages]);
|
|
15
|
+
const setPageSize = useCallback((size) => {
|
|
16
|
+
setPageSizeState(size);
|
|
17
|
+
setPage(1);
|
|
18
|
+
}, []);
|
|
19
|
+
// Clamp page when total changes
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
setPage(p => clamp(p));
|
|
22
|
+
}, [total, pageSize]);
|
|
23
|
+
return {
|
|
24
|
+
page,
|
|
25
|
+
pageSize,
|
|
26
|
+
totalPages,
|
|
27
|
+
offset: (page - 1) * pageSize,
|
|
28
|
+
isFirstPage: page === 1,
|
|
29
|
+
isLastPage: page >= totalPages,
|
|
30
|
+
next,
|
|
31
|
+
prev,
|
|
32
|
+
goTo,
|
|
33
|
+
setPageSize,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Repeatedly calls an async function on a fixed interval.
|
|
38
|
+
* Returns the latest data/error and start/stop controls.
|
|
39
|
+
*/
|
|
40
|
+
export function usePolling(fetcher, options = {}) {
|
|
41
|
+
const { interval = 3000, immediate = true, enabled = true } = options;
|
|
42
|
+
const [data, setData] = useState(null);
|
|
43
|
+
const [error, setError] = useState(null);
|
|
44
|
+
const [loading, setLoading] = useState(false);
|
|
45
|
+
const [isPolling, setIsPolling] = useState(false);
|
|
46
|
+
const timerRef = useRef(null);
|
|
47
|
+
const fetcherRef = useRef(fetcher);
|
|
48
|
+
const isMounted = useRef(true);
|
|
49
|
+
useEffect(() => { fetcherRef.current = fetcher; });
|
|
50
|
+
useEffect(() => { isMounted.current = true; return () => { isMounted.current = false; }; }, []);
|
|
51
|
+
const run = useCallback(async () => {
|
|
52
|
+
setLoading(true);
|
|
53
|
+
try {
|
|
54
|
+
const result = await fetcherRef.current();
|
|
55
|
+
if (isMounted.current) {
|
|
56
|
+
setData(result);
|
|
57
|
+
setError(null);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (isMounted.current)
|
|
62
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
if (isMounted.current)
|
|
66
|
+
setLoading(false);
|
|
67
|
+
}
|
|
68
|
+
}, []);
|
|
69
|
+
const start = useCallback(() => {
|
|
70
|
+
if (timerRef.current)
|
|
71
|
+
return;
|
|
72
|
+
setIsPolling(true);
|
|
73
|
+
if (immediate)
|
|
74
|
+
run();
|
|
75
|
+
timerRef.current = setInterval(run, interval);
|
|
76
|
+
}, [interval, immediate, run]);
|
|
77
|
+
const stop = useCallback(() => {
|
|
78
|
+
if (timerRef.current)
|
|
79
|
+
clearInterval(timerRef.current);
|
|
80
|
+
timerRef.current = null;
|
|
81
|
+
setIsPolling(false);
|
|
82
|
+
}, []);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (enabled)
|
|
85
|
+
start();
|
|
86
|
+
else
|
|
87
|
+
stop();
|
|
88
|
+
return stop;
|
|
89
|
+
}, [enabled]);
|
|
90
|
+
return { data, error, loading, start, stop, isPolling };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Fetches a URL with an AbortController so in-flight requests are automatically
|
|
94
|
+
* cancelled on unmount or when the URL changes.
|
|
95
|
+
*/
|
|
96
|
+
export function useAbortFetch(url, options) {
|
|
97
|
+
const [data, setData] = useState(null);
|
|
98
|
+
const [error, setError] = useState(null);
|
|
99
|
+
const [loading, setLoading] = useState(true);
|
|
100
|
+
const [tick, setTick] = useState(0);
|
|
101
|
+
const controllerRef = useRef(null);
|
|
102
|
+
const abort = useCallback(() => {
|
|
103
|
+
var _a;
|
|
104
|
+
(_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.abort();
|
|
105
|
+
setLoading(false);
|
|
106
|
+
}, []);
|
|
107
|
+
const refetch = useCallback(() => setTick(n => n + 1), []);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const controller = new AbortController();
|
|
110
|
+
controllerRef.current = controller;
|
|
111
|
+
setLoading(true);
|
|
112
|
+
fetch(url, Object.assign(Object.assign({}, options), { signal: controller.signal }))
|
|
113
|
+
.then(r => { if (!r.ok)
|
|
114
|
+
throw new Error(`HTTP ${r.status}`); return r.json(); })
|
|
115
|
+
.then(d => { setData(d); setError(null); })
|
|
116
|
+
.catch(err => { if (err.name !== 'AbortError')
|
|
117
|
+
setError(err instanceof Error ? err : new Error(String(err))); })
|
|
118
|
+
.finally(() => setLoading(false));
|
|
119
|
+
return () => controller.abort();
|
|
120
|
+
}, [url, tick]);
|
|
121
|
+
return { data, error, loading, abort, refetch };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A fetch hook that does NOT run on mount. Call the returned fetch() function
|
|
125
|
+
* to trigger a request imperatively (e.g. on button click).
|
|
126
|
+
*/
|
|
127
|
+
export function useLazyFetch() {
|
|
128
|
+
const [data, setData] = useState(null);
|
|
129
|
+
const [error, setError] = useState(null);
|
|
130
|
+
const [loading, setLoading] = useState(false);
|
|
131
|
+
const isMounted = useRef(true);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
isMounted.current = true;
|
|
134
|
+
return () => { isMounted.current = false; };
|
|
135
|
+
}, []);
|
|
136
|
+
const doFetch = useCallback(async (url, options) => {
|
|
137
|
+
if (isMounted.current) {
|
|
138
|
+
setLoading(true);
|
|
139
|
+
setError(null);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const res = await fetch(url, options);
|
|
143
|
+
if (!res.ok)
|
|
144
|
+
throw new Error(`HTTP ${res.status}`);
|
|
145
|
+
const result = await res.json();
|
|
146
|
+
if (isMounted.current)
|
|
147
|
+
setData(result);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
if (isMounted.current)
|
|
151
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
if (isMounted.current)
|
|
155
|
+
setLoading(false);
|
|
156
|
+
}
|
|
157
|
+
}, []);
|
|
158
|
+
const reset = useCallback(() => { setData(null); setError(null); setLoading(false); }, []);
|
|
159
|
+
return { data, error, loading, fetch: doFetch, reset };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Applies an optimistic value immediately while an async action runs.
|
|
163
|
+
* Rolls back to the previous value on error.
|
|
164
|
+
*/
|
|
165
|
+
export function useOptimisticUpdate(initialData) {
|
|
166
|
+
const [data, setData] = useState(initialData);
|
|
167
|
+
const [isPending, setIsPending] = useState(false);
|
|
168
|
+
const [error, setError] = useState(null);
|
|
169
|
+
const previousRef = useRef(initialData);
|
|
170
|
+
const update = useCallback(async (optimisticValue, action) => {
|
|
171
|
+
previousRef.current = data;
|
|
172
|
+
setData(optimisticValue);
|
|
173
|
+
setIsPending(true);
|
|
174
|
+
setError(null);
|
|
175
|
+
try {
|
|
176
|
+
const confirmed = await action();
|
|
177
|
+
setData(confirmed);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
setData(previousRef.current);
|
|
181
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
setIsPending(false);
|
|
185
|
+
}
|
|
186
|
+
}, [data]);
|
|
187
|
+
const rollback = useCallback(() => {
|
|
188
|
+
setData(previousRef.current);
|
|
189
|
+
setError(null);
|
|
190
|
+
}, []);
|
|
191
|
+
return { data, isPending, error, update, rollback };
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* A stopwatch that tracks elapsed milliseconds with start/stop/reset/lap controls.
|
|
195
|
+
*/
|
|
196
|
+
export function useStopwatch() {
|
|
197
|
+
const [elapsedMs, setElapsedMs] = useState(0);
|
|
198
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
199
|
+
const [laps, setLaps] = useState([]);
|
|
200
|
+
const startTimeRef = useRef(0);
|
|
201
|
+
const frameRef = useRef(0);
|
|
202
|
+
const accumulatedRef = useRef(0);
|
|
203
|
+
const tick = useCallback(() => {
|
|
204
|
+
setElapsedMs(accumulatedRef.current + (performance.now() - startTimeRef.current));
|
|
205
|
+
frameRef.current = requestAnimationFrame(tick);
|
|
206
|
+
}, []);
|
|
207
|
+
const start = useCallback(() => {
|
|
208
|
+
if (isRunning)
|
|
209
|
+
return;
|
|
210
|
+
startTimeRef.current = performance.now();
|
|
211
|
+
setIsRunning(true);
|
|
212
|
+
frameRef.current = requestAnimationFrame(tick);
|
|
213
|
+
}, [isRunning, tick]);
|
|
214
|
+
const stop = useCallback(() => {
|
|
215
|
+
cancelAnimationFrame(frameRef.current);
|
|
216
|
+
accumulatedRef.current += performance.now() - startTimeRef.current;
|
|
217
|
+
setIsRunning(false);
|
|
218
|
+
}, []);
|
|
219
|
+
const reset = useCallback(() => {
|
|
220
|
+
cancelAnimationFrame(frameRef.current);
|
|
221
|
+
accumulatedRef.current = 0;
|
|
222
|
+
setElapsedMs(0);
|
|
223
|
+
setIsRunning(false);
|
|
224
|
+
setLaps([]);
|
|
225
|
+
}, []);
|
|
226
|
+
const lap = useCallback(() => {
|
|
227
|
+
const current = accumulatedRef.current + (isRunning ? performance.now() - startTimeRef.current : 0);
|
|
228
|
+
setLaps(prev => [...prev, current]);
|
|
229
|
+
return current;
|
|
230
|
+
}, [isRunning]);
|
|
231
|
+
useEffect(() => () => cancelAnimationFrame(frameRef.current), []);
|
|
232
|
+
return { elapsedMs, isRunning, start, stop, reset, lap, laps };
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Schedules a callback with requestIdleCallback, falling back to setTimeout
|
|
236
|
+
* in environments that don't support it (e.g. Safari).
|
|
237
|
+
*/
|
|
238
|
+
export function useIdleCallback(callback, options = {}) {
|
|
239
|
+
const { timeout = 2000, immediate = true } = options;
|
|
240
|
+
const callbackRef = useRef(callback);
|
|
241
|
+
useEffect(() => { callbackRef.current = callback; });
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
if (!immediate)
|
|
244
|
+
return;
|
|
245
|
+
let id;
|
|
246
|
+
if (typeof requestIdleCallback !== 'undefined') {
|
|
247
|
+
id = requestIdleCallback(() => callbackRef.current(), { timeout });
|
|
248
|
+
return () => cancelIdleCallback(id);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const tid = setTimeout(() => callbackRef.current(), timeout);
|
|
252
|
+
return () => clearTimeout(tid);
|
|
253
|
+
}
|
|
254
|
+
}, [immediate, timeout]);
|
|
255
|
+
}
|
|
256
|
+
// ─── useAsyncEffect ───────────────────────────────────────────────────────────
|
|
257
|
+
/**
|
|
258
|
+
* useEffect wrapper that accepts an async function and handles cleanup safely.
|
|
259
|
+
* The async function receives an isCancelled() check to guard async operations.
|
|
260
|
+
*/
|
|
261
|
+
export function useAsyncEffect(effect, deps) {
|
|
262
|
+
useEffect(() => {
|
|
263
|
+
let cancelled = false;
|
|
264
|
+
let cleanup;
|
|
265
|
+
effect(() => cancelled).then(fn => {
|
|
266
|
+
cleanup = fn;
|
|
267
|
+
});
|
|
268
|
+
return () => {
|
|
269
|
+
cancelled = true;
|
|
270
|
+
cleanup === null || cleanup === void 0 ? void 0 : cleanup();
|
|
271
|
+
};
|
|
272
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
273
|
+
}, deps);
|
|
274
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { MutableRefObject } from 'react';
|
|
2
|
+
interface UseScrollToTopOptions {
|
|
3
|
+
behavior?: ScrollBehavior;
|
|
4
|
+
element?: MutableRefObject<HTMLElement | null>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Returns a function that scrolls the window (or a given element) to the top.
|
|
8
|
+
* Also returns a boolean indicating whether the user has scrolled down enough
|
|
9
|
+
* that a "back to top" button should be shown (default threshold: 300px).
|
|
10
|
+
*/
|
|
11
|
+
export declare function useScrollToTop(threshold?: number, options?: UseScrollToTopOptions): [boolean, () => void];
|
|
12
|
+
interface UseScrollIntoViewReturn {
|
|
13
|
+
ref: MutableRefObject<HTMLElement | null>;
|
|
14
|
+
scrollIntoView: (options?: ScrollIntoViewOptions) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns a ref and a function that scrolls the ref'd element into the viewport.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useScrollIntoView(defaultOptions?: ScrollIntoViewOptions): UseScrollIntoViewReturn;
|
|
20
|
+
interface UseFocusTrapReturn {
|
|
21
|
+
ref: MutableRefObject<HTMLElement | null>;
|
|
22
|
+
activate: () => void;
|
|
23
|
+
deactivate: () => void;
|
|
24
|
+
isActive: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Traps keyboard focus within a container element. Tab/Shift+Tab cycle only
|
|
28
|
+
* through focusable elements inside the container while the trap is active.
|
|
29
|
+
* Essential for accessible modals, drawers, and dialogs.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useFocusTrap(initiallyActive?: boolean): UseFocusTrapReturn;
|
|
32
|
+
interface UseVirtualListOptions {
|
|
33
|
+
itemHeight: number;
|
|
34
|
+
overscan?: number;
|
|
35
|
+
}
|
|
36
|
+
interface UseVirtualListReturn<T> {
|
|
37
|
+
containerRef: MutableRefObject<HTMLElement | null>;
|
|
38
|
+
virtualItems: {
|
|
39
|
+
item: T;
|
|
40
|
+
index: number;
|
|
41
|
+
offsetTop: number;
|
|
42
|
+
}[];
|
|
43
|
+
totalHeight: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Windowed (virtual) list rendering. Only renders items visible in the
|
|
47
|
+
* scroll container plus an overscan buffer. Dramatically reduces DOM nodes for
|
|
48
|
+
* large lists. Requires a fixed itemHeight.
|
|
49
|
+
*/
|
|
50
|
+
export declare function useVirtualList<T>(items: T[], options: UseVirtualListOptions): UseVirtualListReturn<T>;
|
|
51
|
+
type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
52
|
+
/**
|
|
53
|
+
* Returns the current named breakpoint based on window width.
|
|
54
|
+
* Uses Tailwind-compatible breakpoints by default.
|
|
55
|
+
*/
|
|
56
|
+
export declare function useBreakpoint(): Breakpoint;
|
|
57
|
+
type ThemeMode = 'light' | 'dark' | 'system';
|
|
58
|
+
interface UseThemeReturn {
|
|
59
|
+
theme: ThemeMode;
|
|
60
|
+
resolvedTheme: 'light' | 'dark';
|
|
61
|
+
setTheme: (theme: ThemeMode) => void;
|
|
62
|
+
toggleTheme: () => void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Manages a light/dark/system theme with persistence to localStorage.
|
|
66
|
+
* Applies a `data-theme` attribute to `<html>` and resolves the system preference.
|
|
67
|
+
*/
|
|
68
|
+
export declare function useTheme(): UseThemeReturn;
|
|
69
|
+
interface RippleEntry {
|
|
70
|
+
id: number;
|
|
71
|
+
x: number;
|
|
72
|
+
y: number;
|
|
73
|
+
size: number;
|
|
74
|
+
}
|
|
75
|
+
interface UseRippleReturn {
|
|
76
|
+
ripples: RippleEntry[];
|
|
77
|
+
onMouseDown: (e: React.MouseEvent<HTMLElement>) => void;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Material-style ripple effect. Spread the returned props onto a button/div.
|
|
81
|
+
* Render each ripple as an absolutely-positioned circular element using the
|
|
82
|
+
* provided x/y/size values.
|
|
83
|
+
*/
|
|
84
|
+
export declare function useRipple(duration?: number): UseRippleReturn;
|
|
85
|
+
type ToastType = 'info' | 'success' | 'warning' | 'error';
|
|
86
|
+
interface Toast {
|
|
87
|
+
id: number;
|
|
88
|
+
message: string;
|
|
89
|
+
type: ToastType;
|
|
90
|
+
duration: number;
|
|
91
|
+
}
|
|
92
|
+
interface UseToastReturn {
|
|
93
|
+
toasts: Toast[];
|
|
94
|
+
show: (message: string, type?: ToastType, duration?: number) => void;
|
|
95
|
+
dismiss: (id: number) => void;
|
|
96
|
+
clear: () => void;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Manages a list of toast notifications with automatic dismissal after a duration.
|
|
100
|
+
*/
|
|
101
|
+
export declare function useToast(): UseToastReturn;
|
|
102
|
+
interface UseModalReturn {
|
|
103
|
+
isOpen: boolean;
|
|
104
|
+
open: () => void;
|
|
105
|
+
close: () => void;
|
|
106
|
+
toggle: () => void;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Manages modal open/close state. Automatically restores body scroll on close.
|
|
110
|
+
*/
|
|
111
|
+
export declare function useModal(initialOpen?: boolean): UseModalReturn;
|
|
112
|
+
interface ContextMenuState {
|
|
113
|
+
isVisible: boolean;
|
|
114
|
+
x: number;
|
|
115
|
+
y: number;
|
|
116
|
+
}
|
|
117
|
+
interface UseContextMenuReturn {
|
|
118
|
+
state: ContextMenuState;
|
|
119
|
+
targetProps: {
|
|
120
|
+
onContextMenu: (e: React.MouseEvent) => void;
|
|
121
|
+
};
|
|
122
|
+
close: () => void;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Captures right-click position to display a custom context menu at the cursor.
|
|
126
|
+
* Closes on outside click or Escape.
|
|
127
|
+
*/
|
|
128
|
+
export declare function useContextMenu(): UseContextMenuReturn;
|
|
129
|
+
interface UseInputReturn<T = string> {
|
|
130
|
+
value: T;
|
|
131
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void;
|
|
132
|
+
reset: () => void;
|
|
133
|
+
set: (value: T) => void;
|
|
134
|
+
bind: {
|
|
135
|
+
value: T;
|
|
136
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Controlled input hook. Spread `bind` onto any input/textarea/select element.
|
|
141
|
+
* Returns value, reset, and direct setter.
|
|
142
|
+
*/
|
|
143
|
+
export declare function useInput<T extends string | number | boolean = string>(initialValue: T): UseInputReturn<T>;
|
|
144
|
+
type Validator<T> = (value: T) => string | null;
|
|
145
|
+
interface UseValidationReturn<T> {
|
|
146
|
+
error: string | null;
|
|
147
|
+
validate: (value: T) => boolean;
|
|
148
|
+
reset: () => void;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Standalone field validation hook. Pass an array of validator functions;
|
|
152
|
+
* returns the first error message or null.
|
|
153
|
+
*/
|
|
154
|
+
export declare function useValidation<T>(validators: Validator<T>[]): UseValidationReturn<T>;
|
|
155
|
+
interface UseCheckboxReturn {
|
|
156
|
+
checked: boolean;
|
|
157
|
+
toggle: () => void;
|
|
158
|
+
setChecked: (v: boolean) => void;
|
|
159
|
+
bind: {
|
|
160
|
+
checked: boolean;
|
|
161
|
+
onChange: () => void;
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Controlled checkbox state with a toggle function and spread-ready bind props.
|
|
166
|
+
*/
|
|
167
|
+
export declare function useCheckbox(initialChecked?: boolean): UseCheckboxReturn;
|
|
168
|
+
interface UseSearchReturn<T> {
|
|
169
|
+
query: string;
|
|
170
|
+
setQuery: (q: string) => void;
|
|
171
|
+
results: T[];
|
|
172
|
+
clear: () => void;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Filters an array of items using a search query and a key extractor function.
|
|
176
|
+
* Returns the query state and the filtered results.
|
|
177
|
+
*/
|
|
178
|
+
export declare function useSearch<T>(items: T[], getSearchableText: (item: T) => string, debounceMs?: number): UseSearchReturn<T>;
|
|
179
|
+
interface UseFilterReturn<T> {
|
|
180
|
+
filtered: T[];
|
|
181
|
+
setFilter: (fn: (item: T) => boolean) => void;
|
|
182
|
+
clearFilter: () => void;
|
|
183
|
+
isFiltered: boolean;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Filters an array with a predicate function. Returns the filtered list,
|
|
187
|
+
* a setter for the predicate, and a clear function.
|
|
188
|
+
*/
|
|
189
|
+
export declare function useFilter<T>(items: T[]): UseFilterReturn<T>;
|
|
190
|
+
type SortDirection = 'asc' | 'desc';
|
|
191
|
+
interface UseSortReturn<T> {
|
|
192
|
+
sorted: T[];
|
|
193
|
+
sortKey: keyof T | null;
|
|
194
|
+
direction: SortDirection;
|
|
195
|
+
setSort: (key: keyof T) => void;
|
|
196
|
+
clearSort: () => void;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Sorts an array by a key, toggling direction on repeated calls with the same key.
|
|
200
|
+
*/
|
|
201
|
+
export declare function useSort<T extends Record<string, any>>(items: T[]): UseSortReturn<T>;
|
|
202
|
+
interface UseAutocompleteReturn<T> {
|
|
203
|
+
query: string;
|
|
204
|
+
suggestions: T[];
|
|
205
|
+
selectedIndex: number;
|
|
206
|
+
isOpen: boolean;
|
|
207
|
+
inputProps: {
|
|
208
|
+
value: string;
|
|
209
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
210
|
+
onKeyDown: (e: React.KeyboardEvent) => void;
|
|
211
|
+
};
|
|
212
|
+
select: (item: T) => void;
|
|
213
|
+
clear: () => void;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Autocomplete suggestions list with keyboard navigation (Up/Down/Enter/Escape).
|
|
217
|
+
*/
|
|
218
|
+
export declare function useAutocomplete<T>(getSuggestions: (query: string) => T[], onSelect?: (item: T) => void, getLabel?: (item: T) => string): UseAutocompleteReturn<T>;
|
|
219
|
+
export {};
|