@ug666/ui-react 0.1.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/dist/chunk-MGXIF756.js +313 -0
- package/dist/chunk-MGXIF756.js.map +1 -0
- package/dist/hooks/index.cjs +351 -0
- package/dist/hooks/index.cjs.map +1 -0
- package/dist/hooks/index.d.cts +232 -0
- package/dist/hooks/index.d.ts +232 -0
- package/dist/hooks/index.js +31 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.cjs +3799 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1363 -0
- package/dist/index.d.ts +1363 -0
- package/dist/index.js +3383 -0
- package/dist/index.js.map +1 -0
- package/dist/tokens.css +9 -0
- package/package.json +53 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// src/hooks/use-debounce.ts
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
function useDebounce(value, delay = 300) {
|
|
4
|
+
const [debounced, setDebounced] = useState(value);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const timer = setTimeout(() => setDebounced(value), delay);
|
|
7
|
+
return () => clearTimeout(timer);
|
|
8
|
+
}, [value, delay]);
|
|
9
|
+
return debounced;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/hooks/use-local-storage.ts
|
|
13
|
+
import { useState as useState2, useCallback } from "react";
|
|
14
|
+
function useLocalStorage(key, initialValue) {
|
|
15
|
+
const [storedValue, setStoredValue] = useState2(() => {
|
|
16
|
+
if (typeof window === "undefined") return initialValue;
|
|
17
|
+
try {
|
|
18
|
+
const item = window.localStorage.getItem(key);
|
|
19
|
+
return item ? JSON.parse(item) : initialValue;
|
|
20
|
+
} catch {
|
|
21
|
+
return initialValue;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const setValue = useCallback((value) => {
|
|
25
|
+
setStoredValue((prev) => {
|
|
26
|
+
const next = value instanceof Function ? value(prev) : value;
|
|
27
|
+
if (typeof window !== "undefined") {
|
|
28
|
+
window.localStorage.setItem(key, JSON.stringify(next));
|
|
29
|
+
}
|
|
30
|
+
return next;
|
|
31
|
+
});
|
|
32
|
+
}, [key]);
|
|
33
|
+
const removeValue = useCallback(() => {
|
|
34
|
+
setStoredValue(initialValue);
|
|
35
|
+
if (typeof window !== "undefined") {
|
|
36
|
+
window.localStorage.removeItem(key);
|
|
37
|
+
}
|
|
38
|
+
}, [key, initialValue]);
|
|
39
|
+
return [storedValue, setValue, removeValue];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/hooks/use-media-query.ts
|
|
43
|
+
import { useState as useState3, useEffect as useEffect2 } from "react";
|
|
44
|
+
function useMediaQuery(query) {
|
|
45
|
+
const [matches, setMatches] = useState3(() => {
|
|
46
|
+
if (typeof window === "undefined") return false;
|
|
47
|
+
return window.matchMedia(query).matches;
|
|
48
|
+
});
|
|
49
|
+
useEffect2(() => {
|
|
50
|
+
if (typeof window === "undefined") return;
|
|
51
|
+
const mediaQueryList = window.matchMedia(query);
|
|
52
|
+
setMatches(mediaQueryList.matches);
|
|
53
|
+
const handleChange = (event) => {
|
|
54
|
+
setMatches(event.matches);
|
|
55
|
+
};
|
|
56
|
+
mediaQueryList.addEventListener("change", handleChange);
|
|
57
|
+
return () => mediaQueryList.removeEventListener("change", handleChange);
|
|
58
|
+
}, [query]);
|
|
59
|
+
return matches;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/hooks/use-click-outside.ts
|
|
63
|
+
import { useEffect as useEffect3 } from "react";
|
|
64
|
+
function useClickOutside(ref, handler) {
|
|
65
|
+
useEffect3(() => {
|
|
66
|
+
const handleMouseDown = (event) => {
|
|
67
|
+
if (ref.current && !ref.current.contains(event.target)) {
|
|
68
|
+
handler();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const handleTouchStart = (event) => {
|
|
72
|
+
if (ref.current && !ref.current.contains(event.target)) {
|
|
73
|
+
handler();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
document.addEventListener("mousedown", handleMouseDown);
|
|
77
|
+
document.addEventListener("touchstart", handleTouchStart);
|
|
78
|
+
return () => {
|
|
79
|
+
document.removeEventListener("mousedown", handleMouseDown);
|
|
80
|
+
document.removeEventListener("touchstart", handleTouchStart);
|
|
81
|
+
};
|
|
82
|
+
}, [ref, handler]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/hooks/use-copy-to-clipboard.ts
|
|
86
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useRef, useState as useState4 } from "react";
|
|
87
|
+
function useCopyToClipboard() {
|
|
88
|
+
const [copied, setCopied] = useState4(false);
|
|
89
|
+
const timerRef = useRef(null);
|
|
90
|
+
useEffect4(() => {
|
|
91
|
+
return () => {
|
|
92
|
+
if (timerRef.current) {
|
|
93
|
+
clearTimeout(timerRef.current);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}, []);
|
|
97
|
+
const copy = useCallback2(async (text) => {
|
|
98
|
+
if (typeof navigator === "undefined" || !navigator.clipboard) {
|
|
99
|
+
console.error("\u526A\u8D34\u677F API \u4E0D\u53EF\u7528");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
await navigator.clipboard.writeText(text);
|
|
104
|
+
setCopied(true);
|
|
105
|
+
if (timerRef.current) {
|
|
106
|
+
clearTimeout(timerRef.current);
|
|
107
|
+
}
|
|
108
|
+
timerRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error("\u590D\u5236\u5230\u526A\u8D34\u677F\u5931\u8D25:", error);
|
|
111
|
+
}
|
|
112
|
+
}, []);
|
|
113
|
+
return { copied, copy };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/hooks/use-toggle.ts
|
|
117
|
+
import { useState as useState5, useCallback as useCallback3 } from "react";
|
|
118
|
+
function useToggle(initial = false) {
|
|
119
|
+
const [value, setValue] = useState5(initial);
|
|
120
|
+
const toggle = useCallback3(() => {
|
|
121
|
+
setValue((prev) => !prev);
|
|
122
|
+
}, []);
|
|
123
|
+
return [value, toggle, setValue];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/hooks/use-pagination.ts
|
|
127
|
+
import { useCallback as useCallback4, useEffect as useEffect5, useMemo, useState as useState6 } from "react";
|
|
128
|
+
function usePagination({
|
|
129
|
+
total,
|
|
130
|
+
pageSize = 10,
|
|
131
|
+
initialPage = 1
|
|
132
|
+
}) {
|
|
133
|
+
const totalPages = useMemo(
|
|
134
|
+
() => Math.max(1, Math.ceil(total / pageSize)),
|
|
135
|
+
[total, pageSize]
|
|
136
|
+
);
|
|
137
|
+
const [page, setPageState] = useState6(
|
|
138
|
+
() => Math.min(Math.max(1, initialPage), Math.max(1, Math.ceil(total / pageSize)))
|
|
139
|
+
);
|
|
140
|
+
useEffect5(() => {
|
|
141
|
+
setPageState((prev) => Math.min(prev, totalPages));
|
|
142
|
+
}, [totalPages]);
|
|
143
|
+
const setPage = useCallback4(
|
|
144
|
+
(p) => {
|
|
145
|
+
setPageState(Math.min(Math.max(1, p), totalPages));
|
|
146
|
+
},
|
|
147
|
+
[totalPages]
|
|
148
|
+
);
|
|
149
|
+
const nextPage = useCallback4(() => {
|
|
150
|
+
setPageState((prev) => Math.min(prev + 1, totalPages));
|
|
151
|
+
}, [totalPages]);
|
|
152
|
+
const prevPage = useCallback4(() => {
|
|
153
|
+
setPageState((prev) => Math.max(prev - 1, 1));
|
|
154
|
+
}, []);
|
|
155
|
+
const startIndex = (page - 1) * pageSize;
|
|
156
|
+
const endIndex = Math.min(startIndex + pageSize, total);
|
|
157
|
+
return {
|
|
158
|
+
page,
|
|
159
|
+
totalPages,
|
|
160
|
+
setPage,
|
|
161
|
+
nextPage,
|
|
162
|
+
prevPage,
|
|
163
|
+
hasNext: page < totalPages,
|
|
164
|
+
hasPrev: page > 1,
|
|
165
|
+
startIndex,
|
|
166
|
+
endIndex
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/hooks/use-form.ts
|
|
171
|
+
import { useState as useState7, useCallback as useCallback5, useMemo as useMemo2 } from "react";
|
|
172
|
+
function useForm(initialValues) {
|
|
173
|
+
const [values, setValues] = useState7(initialValues);
|
|
174
|
+
const [errors, setErrors] = useState7({});
|
|
175
|
+
const isDirty = useMemo2(
|
|
176
|
+
() => Object.keys(initialValues).some(
|
|
177
|
+
(key) => values[key] !== initialValues[key]
|
|
178
|
+
),
|
|
179
|
+
[values, initialValues]
|
|
180
|
+
);
|
|
181
|
+
const handleChange = useCallback5((name, value) => {
|
|
182
|
+
setValues((prev) => ({ ...prev, [name]: value }));
|
|
183
|
+
setErrors((prev) => {
|
|
184
|
+
if (!prev[name]) return prev;
|
|
185
|
+
const next = { ...prev };
|
|
186
|
+
delete next[name];
|
|
187
|
+
return next;
|
|
188
|
+
});
|
|
189
|
+
}, []);
|
|
190
|
+
const setError = useCallback5((name, message) => {
|
|
191
|
+
setErrors((prev) => ({ ...prev, [name]: message }));
|
|
192
|
+
}, []);
|
|
193
|
+
const clearErrors = useCallback5(() => {
|
|
194
|
+
setErrors({});
|
|
195
|
+
}, []);
|
|
196
|
+
const reset = useCallback5(() => {
|
|
197
|
+
setValues(initialValues);
|
|
198
|
+
setErrors({});
|
|
199
|
+
}, [initialValues]);
|
|
200
|
+
return { values, errors, handleChange, setError, clearErrors, reset, isDirty };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/hooks/use-mounted.ts
|
|
204
|
+
import { useState as useState8, useEffect as useEffect6 } from "react";
|
|
205
|
+
function useMounted() {
|
|
206
|
+
const [mounted, setMounted] = useState8(false);
|
|
207
|
+
useEffect6(() => {
|
|
208
|
+
setMounted(true);
|
|
209
|
+
}, []);
|
|
210
|
+
return mounted;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/hooks/use-interval.ts
|
|
214
|
+
import { useEffect as useEffect7, useRef as useRef2 } from "react";
|
|
215
|
+
function useInterval(callback, delay) {
|
|
216
|
+
const savedCallback = useRef2(callback);
|
|
217
|
+
useEffect7(() => {
|
|
218
|
+
savedCallback.current = callback;
|
|
219
|
+
}, [callback]);
|
|
220
|
+
useEffect7(() => {
|
|
221
|
+
if (delay === null) return;
|
|
222
|
+
const id = setInterval(() => savedCallback.current(), delay);
|
|
223
|
+
return () => clearInterval(id);
|
|
224
|
+
}, [delay]);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/hooks/use-timeout.ts
|
|
228
|
+
import { useEffect as useEffect8, useRef as useRef3 } from "react";
|
|
229
|
+
function useTimeout(callback, delay) {
|
|
230
|
+
const savedCallback = useRef3(callback);
|
|
231
|
+
useEffect8(() => {
|
|
232
|
+
savedCallback.current = callback;
|
|
233
|
+
}, [callback]);
|
|
234
|
+
useEffect8(() => {
|
|
235
|
+
if (delay === null) return;
|
|
236
|
+
const id = setTimeout(() => savedCallback.current(), delay);
|
|
237
|
+
return () => clearTimeout(id);
|
|
238
|
+
}, [delay]);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/hooks/use-hotkeys.ts
|
|
242
|
+
import { useEffect as useEffect9, useRef as useRef4 } from "react";
|
|
243
|
+
function parseHotkey(keys) {
|
|
244
|
+
const parts = keys.toLowerCase().split("+").map((s) => s.trim());
|
|
245
|
+
const ctrl = parts.includes("ctrl");
|
|
246
|
+
const meta = parts.includes("cmd") || parts.includes("meta");
|
|
247
|
+
const alt = parts.includes("alt");
|
|
248
|
+
const shift = parts.includes("shift");
|
|
249
|
+
const key = parts.find(
|
|
250
|
+
(p) => !["ctrl", "cmd", "meta", "alt", "shift"].includes(p)
|
|
251
|
+
) ?? "";
|
|
252
|
+
return { ctrl, meta, alt, shift, key };
|
|
253
|
+
}
|
|
254
|
+
function useHotkeys(keys, callback, options = {}) {
|
|
255
|
+
const { preventDefault = false, target = null } = options;
|
|
256
|
+
const savedCallback = useRef4(callback);
|
|
257
|
+
useEffect9(() => {
|
|
258
|
+
savedCallback.current = callback;
|
|
259
|
+
}, [callback]);
|
|
260
|
+
useEffect9(() => {
|
|
261
|
+
const parsed = parseHotkey(keys);
|
|
262
|
+
const eventTarget = target ?? window;
|
|
263
|
+
function handler(event) {
|
|
264
|
+
const eventKey = event.key.toLowerCase();
|
|
265
|
+
const match = eventKey === parsed.key && event.ctrlKey === parsed.ctrl && event.metaKey === parsed.meta && event.altKey === parsed.alt && event.shiftKey === parsed.shift;
|
|
266
|
+
if (match) {
|
|
267
|
+
if (preventDefault) event.preventDefault();
|
|
268
|
+
savedCallback.current(event);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
eventTarget.addEventListener("keydown", handler);
|
|
272
|
+
return () => {
|
|
273
|
+
eventTarget.removeEventListener("keydown", handler);
|
|
274
|
+
};
|
|
275
|
+
}, [keys, preventDefault, target]);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/hooks/use-event-listener.ts
|
|
279
|
+
import { useEffect as useEffect10, useRef as useRef5 } from "react";
|
|
280
|
+
function useEventListener(event, handler, target) {
|
|
281
|
+
const savedHandler = useRef5(handler);
|
|
282
|
+
useEffect10(() => {
|
|
283
|
+
savedHandler.current = handler;
|
|
284
|
+
}, [handler]);
|
|
285
|
+
useEffect10(() => {
|
|
286
|
+
if (target === null) return;
|
|
287
|
+
const eventTarget = target ?? window;
|
|
288
|
+
function listener(event2) {
|
|
289
|
+
savedHandler.current(event2);
|
|
290
|
+
}
|
|
291
|
+
eventTarget.addEventListener(event, listener);
|
|
292
|
+
return () => {
|
|
293
|
+
eventTarget.removeEventListener(event, listener);
|
|
294
|
+
};
|
|
295
|
+
}, [event, target]);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export {
|
|
299
|
+
useDebounce,
|
|
300
|
+
useLocalStorage,
|
|
301
|
+
useMediaQuery,
|
|
302
|
+
useClickOutside,
|
|
303
|
+
useCopyToClipboard,
|
|
304
|
+
useToggle,
|
|
305
|
+
usePagination,
|
|
306
|
+
useForm,
|
|
307
|
+
useMounted,
|
|
308
|
+
useInterval,
|
|
309
|
+
useTimeout,
|
|
310
|
+
useHotkeys,
|
|
311
|
+
useEventListener
|
|
312
|
+
};
|
|
313
|
+
//# sourceMappingURL=chunk-MGXIF756.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-debounce.ts","../src/hooks/use-local-storage.ts","../src/hooks/use-media-query.ts","../src/hooks/use-click-outside.ts","../src/hooks/use-copy-to-clipboard.ts","../src/hooks/use-toggle.ts","../src/hooks/use-pagination.ts","../src/hooks/use-form.ts","../src/hooks/use-mounted.ts","../src/hooks/use-interval.ts","../src/hooks/use-timeout.ts","../src/hooks/use-hotkeys.ts","../src/hooks/use-event-listener.ts"],"sourcesContent":["/**\n * @description: 防抖 hook\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-15\n */\nimport { useState, useEffect } from 'react'\n\n/**\n * 防抖值,延迟 delay 毫秒后才更新\n * @example\n * const keyword = useDebounce(inputValue, 300)\n */\nexport function useDebounce<T>(value: T, delay = 300): T {\n const [debounced, setDebounced] = useState(value)\n\n useEffect(() => {\n const timer = setTimeout(() => setDebounced(value), delay)\n return () => clearTimeout(timer)\n }, [value, delay])\n\n return debounced\n}\n","/**\n * @description: localStorage 持久化 hook\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-15\n */\nimport { useState, useCallback } from 'react'\n\nexport type SetLocalStorageValue<T> = (value: T | ((prev: T) => T)) => void\nexport type UseLocalStorageReturn<T> = [T, SetLocalStorageValue<T>, () => void]\n\n/**\n * 自动读写 localStorage 的 useState\n * @example\n * const [token, setToken, removeToken] = useLocalStorage('token', '')\n */\nexport function useLocalStorage<T>(key: string, initialValue: T): UseLocalStorageReturn<T> {\n const [storedValue, setStoredValue] = useState<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 })\n\n const setValue = useCallback((value: T | ((prev: T) => T)) => {\n setStoredValue(prev => {\n const next = value instanceof Function ? value(prev) : value\n if (typeof window !== 'undefined') {\n window.localStorage.setItem(key, JSON.stringify(next))\n }\n return next\n })\n }, [key])\n\n const removeValue = useCallback(() => {\n setStoredValue(initialValue)\n if (typeof window !== 'undefined') {\n window.localStorage.removeItem(key)\n }\n }, [key, initialValue])\n\n return [storedValue, setValue, removeValue]\n}\n","/**\n * @description: CSS media query 监听 hook\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-15\n */\nimport { useState, useEffect } from 'react'\n\n/**\n * 监听 CSS media query 变化,返回当前是否匹配\n * @example\n * const isMobile = useMediaQuery('(max-width: 768px)')\n * const prefersDark = useMediaQuery('(prefers-color-scheme: dark)')\n */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState<boolean>(() => {\n if (typeof window === 'undefined') return false\n return window.matchMedia(query).matches\n })\n\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n const mediaQueryList = window.matchMedia(query)\n setMatches(mediaQueryList.matches)\n\n const handleChange = (event: MediaQueryListEvent) => {\n setMatches(event.matches)\n }\n\n mediaQueryList.addEventListener('change', handleChange)\n return () => mediaQueryList.removeEventListener('change', handleChange)\n }, [query])\n\n return matches\n}\n","/**\n * @description: 点击元素外部触发回调的 hook\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-15\n */\nimport { useEffect } from 'react'\nimport type { RefObject } from 'react'\n\n/**\n * 点击 ref 元素外部时触发 handler\n * @example\n * const ref = useRef<HTMLDivElement>(null)\n * useClickOutside(ref, () => setOpen(false))\n */\nexport function useClickOutside<T extends HTMLElement>(\n ref: RefObject<T | null>,\n handler: () => void,\n): void {\n useEffect(() => {\n const handleMouseDown = (event: MouseEvent) => {\n if (ref.current && !ref.current.contains(event.target as Node)) {\n handler()\n }\n }\n\n const handleTouchStart = (event: TouchEvent) => {\n if (ref.current && !ref.current.contains(event.target as Node)) {\n handler()\n }\n }\n\n document.addEventListener('mousedown', handleMouseDown)\n document.addEventListener('touchstart', handleTouchStart)\n\n return () => {\n document.removeEventListener('mousedown', handleMouseDown)\n document.removeEventListener('touchstart', handleTouchStart)\n }\n }, [ref, handler])\n}\n","/**\n * @description: 复制文本到剪贴板的 hook\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-15\n */\nimport { useCallback, useEffect, useRef, useState } from 'react'\n\nexport interface UseCopyToClipboardReturn {\n copied: boolean\n copy: (text: string) => Promise<void>\n}\n\n/**\n * 复制文本到剪贴板,copied 状态 2 秒后自动重置\n * @example\n * const { copied, copy } = useCopyToClipboard()\n * <button onClick={() => copy('hello')}>{copied ? '已复制' : '复制'}</button>\n */\nexport function useCopyToClipboard(): UseCopyToClipboardReturn {\n const [copied, setCopied] = useState(false)\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n useEffect(() => {\n return () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current)\n }\n }\n }, [])\n\n const copy = useCallback(async (text: string) => {\n if (typeof navigator === 'undefined' || !navigator.clipboard) {\n console.error('剪贴板 API 不可用')\n return\n }\n\n try {\n await navigator.clipboard.writeText(text)\n setCopied(true)\n if (timerRef.current) {\n clearTimeout(timerRef.current)\n }\n // 2 秒后自动重置状态\n timerRef.current = setTimeout(() => setCopied(false), 2000)\n } catch (error) {\n console.error('复制到剪贴板失败:', error)\n }\n }, [])\n\n return { copied, copy }\n}\n","/**\n * @description: 布尔值切换 hook\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-15\n */\nimport { useState, useCallback } from 'react'\n\nexport type UseToggleReturn = [boolean, () => void, (value: boolean) => void]\n\n/**\n * 简单的布尔值切换,返回 [value, toggle, setValue]\n * @example\n * const [isOpen, toggleOpen, setIsOpen] = useToggle(false)\n * <button onClick={toggleOpen}>切换</button>\n */\nexport function useToggle(\n initial = false,\n): UseToggleReturn {\n const [value, setValue] = useState(initial)\n\n const toggle = useCallback(() => {\n setValue(prev => !prev)\n }, [])\n\n return [value, toggle, setValue]\n}\n","/**\n * @description: 分页状态管理 hook\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-15\n */\nimport { useCallback, useEffect, useMemo, useState } from 'react'\n\nexport interface UsePaginationOptions {\n /** 数据总条数 */\n total: number\n /** 每页条数,默认 10 */\n pageSize?: number\n /** 初始页码,默认 1 */\n initialPage?: number\n}\n\nexport interface UsePaginationReturn {\n /** 当前页码 (从 1 开始) */\n page: number\n /** 总页数 */\n totalPages: number\n /** 跳转到指定页 */\n setPage: (p: number) => void\n /** 下一页 */\n nextPage: () => void\n /** 上一页 */\n prevPage: () => void\n /** 是否有下一页 */\n hasNext: boolean\n /** 是否有上一页 */\n hasPrev: boolean\n /** 当前页起始索引 (0-based,用于切片) */\n startIndex: number\n /** 当前页结束索引 (exclusive,用于切片) */\n endIndex: number\n}\n\n/**\n * 分页状态管理,提供页码、边界判断和数据切片索引\n * @example\n * const { page, totalPages, nextPage, prevPage, startIndex, endIndex } = usePagination({ total: 100, pageSize: 10 })\n * const pageData = data.slice(startIndex, endIndex)\n */\nexport function usePagination({\n total,\n pageSize = 10,\n initialPage = 1,\n}: UsePaginationOptions): UsePaginationReturn {\n const totalPages = useMemo(\n () => Math.max(1, Math.ceil(total / pageSize)),\n [total, pageSize],\n )\n\n const [page, setPageState] = useState(() =>\n Math.min(Math.max(1, initialPage), Math.max(1, Math.ceil(total / pageSize))),\n )\n\n useEffect(() => {\n setPageState(prev => Math.min(prev, totalPages))\n }, [totalPages])\n\n const setPage = useCallback(\n (p: number) => {\n setPageState(Math.min(Math.max(1, p), totalPages))\n },\n [totalPages],\n )\n\n const nextPage = useCallback(() => {\n setPageState(prev => Math.min(prev + 1, totalPages))\n }, [totalPages])\n\n const prevPage = useCallback(() => {\n setPageState(prev => Math.max(prev - 1, 1))\n }, [])\n\n const startIndex = (page - 1) * pageSize\n const endIndex = Math.min(startIndex + pageSize, total)\n\n return {\n page,\n totalPages,\n setPage,\n nextPage,\n prevPage,\n hasNext: page < totalPages,\n hasPrev: page > 1,\n startIndex,\n endIndex,\n }\n}\n","/**\n * @description: 极简表单状态管理 hook,不依赖第三方库\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-15\n */\nimport { useState, useCallback, useMemo } from 'react'\n\nexport type UseFormErrors<T extends Record<string, unknown>> = Partial<Record<keyof T, string>>\n\nexport interface UseFormReturn<T extends Record<string, unknown>> {\n /** 当前表单值 */\n values: T\n /** 字段错误信息 */\n errors: UseFormErrors<T>\n /** 更新单个字段值 */\n handleChange: <K extends keyof T>(name: K, value: T[K]) => void\n /** 设置字段错误 */\n setError: (name: keyof T, message: string) => void\n /** 清除所有错误 */\n clearErrors: () => void\n /** 重置表单到初始值 */\n reset: () => void\n /** 表单是否被修改过 */\n isDirty: boolean\n}\n\n/**\n * 极简表单状态管理,提供值管理、错误处理和脏状态检测\n * @example\n * const form = useForm({ username: '', password: '' })\n * <input value={form.values.username} onChange={e => form.handleChange('username', e.target.value)} />\n * {form.errors.username && <span>{form.errors.username}</span>}\n */\nexport function useForm<T extends Record<string, unknown>>(\n initialValues: T,\n): UseFormReturn<T> {\n const [values, setValues] = useState<T>(initialValues)\n const [errors, setErrors] = useState<UseFormErrors<T>>({})\n\n const isDirty = useMemo(\n () =>\n (Object.keys(initialValues) as (keyof T)[]).some(\n key => values[key] !== initialValues[key],\n ),\n [values, initialValues],\n )\n\n const handleChange = useCallback(<K extends keyof T>(name: K, value: T[K]) => {\n setValues(prev => ({ ...prev, [name]: value }))\n // 修改字段时清除该字段的错误\n setErrors(prev => {\n if (!prev[name]) return prev\n const next = { ...prev }\n delete next[name]\n return next\n })\n }, [])\n\n const setError = useCallback((name: keyof T, message: string) => {\n setErrors(prev => ({ ...prev, [name]: message }))\n }, [])\n\n const clearErrors = useCallback(() => {\n setErrors({})\n }, [])\n\n const reset = useCallback(() => {\n setValues(initialValues)\n setErrors({})\n }, [initialValues])\n\n return { values, errors, handleChange, setError, clearErrors, reset, isDirty }\n}\n","/**\n * @description: 组件挂载状态 hook — 用于避免 SSR 环境下的 hydration 报错\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-17\n */\nimport { useState, useEffect } from 'react'\n\n/**\n * 返回组件是否已在客户端挂载完成。\n *\n * 在 SSR 场景中,服务端渲染时始终返回 false,\n * 客户端 hydration 完成后变为 true,可用于条件渲染仅客户端内容。\n *\n * @returns 组件是否已挂载\n *\n * @example\n * ```tsx\n * const isMounted = useMounted()\n *\n * if (!isMounted) return null // 服务端不渲染\n * return <ClientOnlyComponent />\n * ```\n */\nexport function useMounted(): boolean {\n const [mounted, setMounted] = useState(false)\n\n useEffect(() => {\n setMounted(true)\n }, [])\n\n return mounted\n}\n","/**\n * @description: 定时间隔 hook — delay 为 null 时暂停,支持动态调整间隔\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-17\n */\nimport { useEffect, useRef } from 'react'\n\n/**\n * 以指定间隔重复调用回调函数。\n *\n * 当 delay 为 null 时定时器自动暂停,可动态修改 delay 或 callback。\n * 参考 Dan Abramov 的 useInterval 实现,使用 ref 保持 callback 最新引用。\n *\n * @param callback 每次触发时调用的函数\n * @param delay 间隔毫秒数;传入 null 则暂停定时器\n *\n * @example\n * ```tsx\n * const [count, setCount] = useState(0)\n * const [running, setRunning] = useState(true)\n *\n * useInterval(() => setCount(c => c + 1), running ? 1000 : null)\n * ```\n */\nexport function useInterval(callback: () => void, delay: number | null): void {\n const savedCallback = useRef<() => void>(callback)\n\n // 每次渲染都更新 ref,确保 callback 始终最新\n useEffect(() => {\n savedCallback.current = callback\n }, [callback])\n\n useEffect(() => {\n if (delay === null) return\n const id = setInterval(() => savedCallback.current(), delay)\n return () => clearInterval(id)\n }, [delay])\n}\n","/**\n * @description: 延迟执行 hook — delay 为 null 时暂停,组件卸载自动清理\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-17\n */\nimport { useEffect, useRef } from 'react'\n\n/**\n * 在指定延迟后执行一次回调函数。\n *\n * 当 delay 为 null 时定时器不启动(暂停模式)。\n * 每次 delay 变化都会重置定时器;组件卸载时自动清理。\n *\n * @param callback 延迟结束后调用的函数\n * @param delay 延迟毫秒数;传入 null 则不执行\n *\n * @example\n * ```tsx\n * const [show, setShow] = useState(true)\n *\n * // 3 秒后自动隐藏提示\n * useTimeout(() => setShow(false), show ? 3000 : null)\n * ```\n */\nexport function useTimeout(callback: () => void, delay: number | null): void {\n const savedCallback = useRef<() => void>(callback)\n\n // 每次渲染都更新 ref,确保 callback 始终最新\n useEffect(() => {\n savedCallback.current = callback\n }, [callback])\n\n useEffect(() => {\n if (delay === null) return\n const id = setTimeout(() => savedCallback.current(), delay)\n return () => clearTimeout(id)\n }, [delay])\n}\n","/**\n * @description: 快捷键绑定 hook — 支持 \"ctrl+k\"、\"cmd+s\"、\"escape\" 等组合键\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-17\n */\nimport { useEffect, useRef } from 'react'\n\nexport interface UseHotkeysOptions {\n /** 是否阻止默认浏览器行为,默认 false */\n preventDefault?: boolean\n /** 监听目标,默认 window */\n target?: HTMLElement | null\n}\n\n/**\n * 解析快捷键字符串,返回规范化的修饰键 + 主键结构。\n * 支持 ctrl/cmd/alt/shift + 任意按键,键名不区分大小写。\n * \"cmd\" 在 Mac 映射为 Meta,Windows 同样可响应 Meta 键。\n */\nfunction parseHotkey(keys: string): {\n ctrl: boolean\n meta: boolean\n alt: boolean\n shift: boolean\n key: string\n} {\n const parts = keys.toLowerCase().split('+').map((s) => s.trim())\n const ctrl = parts.includes('ctrl')\n const meta = parts.includes('cmd') || parts.includes('meta')\n const alt = parts.includes('alt')\n const shift = parts.includes('shift')\n const key = parts.find(\n (p) => !['ctrl', 'cmd', 'meta', 'alt', 'shift'].includes(p),\n ) ?? ''\n\n return { ctrl, meta, alt, shift, key }\n}\n\n/**\n * 将快捷键字符串绑定到回调函数。\n *\n * 支持修饰键组合:ctrl、cmd(Meta)、alt、shift,加主键(不区分大小写)。\n * 组件卸载时自动解绑。\n *\n * @param keys 快捷键字符串,如 \"ctrl+k\"、\"cmd+s\"、\"escape\"\n * @param callback 触发时调用的函数\n * @param options 额外配置项\n *\n * @example\n * ```tsx\n * // 全局搜索快捷键\n * useHotkeys('ctrl+k', () => setSearchOpen(true), { preventDefault: true })\n *\n * // ESC 关闭弹窗\n * useHotkeys('escape', () => setOpen(false))\n *\n * // 仅在特定元素内响应\n * useHotkeys('ctrl+enter', handleSubmit, { target: formRef.current })\n * ```\n */\nexport function useHotkeys(\n keys: string,\n callback: (event: KeyboardEvent) => void,\n options: UseHotkeysOptions = {},\n): void {\n const { preventDefault = false, target = null } = options\n const savedCallback = useRef<(event: KeyboardEvent) => void>(callback)\n\n useEffect(() => {\n savedCallback.current = callback\n }, [callback])\n\n useEffect(() => {\n const parsed = parseHotkey(keys)\n const eventTarget: EventTarget = target ?? window\n\n function handler(event: KeyboardEvent): void {\n const eventKey = event.key.toLowerCase()\n const match =\n eventKey === parsed.key &&\n event.ctrlKey === parsed.ctrl &&\n event.metaKey === parsed.meta &&\n event.altKey === parsed.alt &&\n event.shiftKey === parsed.shift\n\n if (match) {\n if (preventDefault) event.preventDefault()\n savedCallback.current(event)\n }\n }\n\n eventTarget.addEventListener('keydown', handler as EventListener)\n return () => {\n eventTarget.removeEventListener('keydown', handler as EventListener)\n }\n }, [keys, preventDefault, target])\n}\n","/**\n * @description: 事件监听 hook — 自动管理事件绑定与清理,支持 Window/Document/HTMLElement\n * @author: UG - 一个斗码大陆苦逼的三段码之气的少年,并没有神秘戒指中码老的帮助,但总有一天,我会成为斗码大陆中码帝一样的存在。三十年河东,三十年河西,莫欺少年穷。\n * @date: 2026-04-17\n */\nimport { useEffect, useRef } from 'react'\n\n/**\n * 在指定目标上绑定事件监听器,组件卸载时自动清理。\n *\n * 支持三种目标类型:\n * - 不传 target → 默认监听 window\n * - 传入 HTMLElement → 监听该元素\n * - 传入 null → 跳过绑定(适合 ref 未初始化的场景)\n *\n * @typeParam K WindowEventMap 中的事件名称\n * @param event 事件名称,如 \"scroll\"、\"resize\"、\"click\"\n * @param handler 事件处理函数\n * @param target 监听目标;默认 window;传 null 跳过绑定\n *\n * @example\n * ```tsx\n * // 监听全局滚动\n * useEventListener('scroll', () => setScrollY(window.scrollY))\n *\n * // 监听特定元素的点击\n * const divRef = useRef<HTMLDivElement>(null)\n * useEventListener('click', handleClick, divRef.current)\n *\n * // 传入 null 跳过(ref 尚未挂载时)\n * useEventListener('mouseenter', onEnter, ref.current)\n * ```\n */\nexport function useEventListener<K extends keyof WindowEventMap>(\n event: K,\n handler: (event: WindowEventMap[K]) => void,\n target?: EventTarget | null,\n): void {\n const savedHandler = useRef<(event: WindowEventMap[K]) => void>(handler)\n\n useEffect(() => {\n savedHandler.current = handler\n }, [handler])\n\n useEffect(() => {\n // target 显式传入 null 时跳过绑定\n if (target === null) return\n const eventTarget: EventTarget = target ?? window\n\n function listener(event: Event): void {\n savedHandler.current(event as WindowEventMap[K])\n }\n\n eventTarget.addEventListener(event, listener)\n return () => {\n eventTarget.removeEventListener(event, listener)\n }\n }, [event, target])\n}\n"],"mappings":"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","names":["useState","useState","useEffect","useEffect","useCallback","useEffect","useState","useState","useCallback","useCallback","useEffect","useState","useState","useCallback","useMemo","useState","useEffect","useEffect","useRef","useEffect","useRef","useEffect","useRef","useEffect","useRef","event"]}
|