@ug666/ui-react 0.1.0 → 0.2.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.
Files changed (54) hide show
  1. package/dist/blocks/index.cjs +238 -0
  2. package/dist/blocks/index.cjs.map +1 -0
  3. package/dist/blocks/index.d.cts +86 -0
  4. package/dist/blocks/index.d.ts +86 -0
  5. package/dist/blocks/index.js +153 -0
  6. package/dist/blocks/index.js.map +1 -0
  7. package/dist/button-CaLZig8j.d.cts +22 -0
  8. package/dist/button-CaLZig8j.d.ts +22 -0
  9. package/dist/chunk-2IVRUJKO.js +377 -0
  10. package/dist/chunk-2IVRUJKO.js.map +1 -0
  11. package/dist/chunk-73WQAE3E.js +3003 -0
  12. package/dist/chunk-73WQAE3E.js.map +1 -0
  13. package/dist/chunk-RUDEZA5Q.js +62 -0
  14. package/dist/chunk-RUDEZA5Q.js.map +1 -0
  15. package/dist/chunk-S45GP6IB.js +254 -0
  16. package/dist/chunk-S45GP6IB.js.map +1 -0
  17. package/dist/components/index.cjs +3993 -0
  18. package/dist/components/index.cjs.map +1 -0
  19. package/dist/components/index.d.cts +1097 -0
  20. package/dist/components/index.d.ts +1097 -0
  21. package/dist/components/index.js +330 -0
  22. package/dist/components/index.js.map +1 -0
  23. package/dist/hooks/index.cjs +1 -0
  24. package/dist/hooks/index.cjs.map +1 -1
  25. package/dist/hooks/index.js +1 -0
  26. package/dist/index.cjs +1410 -710
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +274 -1340
  29. package/dist/index.d.ts +274 -1340
  30. package/dist/index.js +385 -3229
  31. package/dist/index.js.map +1 -1
  32. package/dist/labs/index.cjs +34 -0
  33. package/dist/labs/index.cjs.map +1 -0
  34. package/dist/labs/index.d.cts +12 -0
  35. package/dist/labs/index.d.ts +12 -0
  36. package/dist/labs/index.js +9 -0
  37. package/dist/labs/index.js.map +1 -0
  38. package/dist/patterns/index.cjs +758 -0
  39. package/dist/patterns/index.cjs.map +1 -0
  40. package/dist/patterns/index.d.cts +158 -0
  41. package/dist/patterns/index.d.ts +158 -0
  42. package/dist/patterns/index.js +320 -0
  43. package/dist/patterns/index.js.map +1 -0
  44. package/dist/primitives/index.cjs +384 -0
  45. package/dist/primitives/index.cjs.map +1 -0
  46. package/dist/primitives/index.d.cts +137 -0
  47. package/dist/primitives/index.d.ts +137 -0
  48. package/dist/primitives/index.js +56 -0
  49. package/dist/primitives/index.js.map +1 -0
  50. package/dist/sidebar-vl00Z2o-.d.cts +93 -0
  51. package/dist/sidebar-vl00Z2o-.d.ts +93 -0
  52. package/dist/styles.css +2499 -0
  53. package/dist/tokens.css +86 -9
  54. package/package.json +36 -6
@@ -0,0 +1,3003 @@
1
+ import {
2
+ useEscapeKey,
3
+ usePortalContainer
4
+ } from "./chunk-2IVRUJKO.js";
5
+ import {
6
+ Label
7
+ } from "./chunk-S45GP6IB.js";
8
+ import {
9
+ cn
10
+ } from "./chunk-RUDEZA5Q.js";
11
+
12
+ // src/components/dialog.tsx
13
+ import { forwardRef, useCallback } from "react";
14
+ import { createPortal } from "react-dom";
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ var CONFIRM_VARIANT_CLASSES = {
17
+ default: "bg-primary text-primary-fg hover:bg-primary-hover focus-visible:ring-ring",
18
+ destructive: "bg-danger text-danger-fg hover:bg-danger-hover focus-visible:ring-danger"
19
+ };
20
+ var Dialog = forwardRef(
21
+ ({
22
+ open,
23
+ onOpenChange,
24
+ title,
25
+ description,
26
+ variant = "default",
27
+ cancelText = "\u53D6\u6D88",
28
+ confirmText = "\u786E\u8BA4",
29
+ onConfirm,
30
+ onCancel,
31
+ className
32
+ }, ref) => {
33
+ const portalContainer = usePortalContainer();
34
+ const handleClose = useCallback(() => {
35
+ onOpenChange(false);
36
+ }, [onOpenChange]);
37
+ const handleCancel = useCallback(() => {
38
+ onCancel?.();
39
+ handleClose();
40
+ }, [onCancel, handleClose]);
41
+ const handleConfirm = useCallback(() => {
42
+ onConfirm?.();
43
+ handleClose();
44
+ }, [onConfirm, handleClose]);
45
+ useEscapeKey(open, handleCancel);
46
+ if (!open || !portalContainer) return null;
47
+ return createPortal(
48
+ /* @__PURE__ */ jsxs(
49
+ "div",
50
+ {
51
+ ref,
52
+ className: cn(
53
+ "fixed inset-0 z-[90] flex items-center justify-center p-4",
54
+ className
55
+ ),
56
+ role: "alertdialog",
57
+ "aria-modal": "true",
58
+ "aria-labelledby": "dialog-title",
59
+ "aria-describedby": description ? "dialog-description" : void 0,
60
+ children: [
61
+ /* @__PURE__ */ jsx(
62
+ "div",
63
+ {
64
+ className: "absolute inset-0 bg-overlay",
65
+ onClick: handleCancel,
66
+ "aria-hidden": "true"
67
+ }
68
+ ),
69
+ /* @__PURE__ */ jsxs("div", { className: "relative z-10 w-full max-w-sm rounded-lg border border-border-base bg-surface-1 shadow-xl", children: [
70
+ /* @__PURE__ */ jsxs("div", { className: "px-6 pt-6 pb-4", children: [
71
+ /* @__PURE__ */ jsx(
72
+ "h2",
73
+ {
74
+ id: "dialog-title",
75
+ className: "text-base font-bold text-text-primary",
76
+ children: title
77
+ }
78
+ ),
79
+ description && /* @__PURE__ */ jsx(
80
+ "p",
81
+ {
82
+ id: "dialog-description",
83
+ className: "mt-2 text-sm text-text-secondary",
84
+ children: description
85
+ }
86
+ )
87
+ ] }),
88
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2 border-t border-border-subtle px-6 py-4", children: [
89
+ /* @__PURE__ */ jsx(
90
+ "button",
91
+ {
92
+ type: "button",
93
+ onClick: handleCancel,
94
+ className: cn(
95
+ "inline-flex h-9 items-center justify-center rounded-md border border-border-strong bg-surface-1 px-4 text-sm font-medium text-text-secondary transition-colors",
96
+ "hover:bg-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-ring/30"
97
+ ),
98
+ children: cancelText
99
+ }
100
+ ),
101
+ /* @__PURE__ */ jsx(
102
+ "button",
103
+ {
104
+ type: "button",
105
+ onClick: handleConfirm,
106
+ className: cn(
107
+ "inline-flex h-9 items-center justify-center rounded-md px-4 text-sm font-medium transition-colors",
108
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
109
+ CONFIRM_VARIANT_CLASSES[variant]
110
+ ),
111
+ children: confirmText
112
+ }
113
+ )
114
+ ] })
115
+ ] })
116
+ ]
117
+ }
118
+ ),
119
+ portalContainer
120
+ );
121
+ }
122
+ );
123
+ Dialog.displayName = "Dialog";
124
+
125
+ // src/components/drawer.tsx
126
+ import { createContext, forwardRef as forwardRef2, useContext } from "react";
127
+ import { createPortal as createPortal2 } from "react-dom";
128
+ import { X } from "lucide-react";
129
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
130
+ var DrawerContext = createContext(null);
131
+ function useDrawerContext() {
132
+ const ctx = useContext(DrawerContext);
133
+ if (!ctx) throw new Error("Drawer \u5B50\u7EC4\u4EF6\u5FC5\u987B\u5728 <Drawer> \u5185\u4F7F\u7528");
134
+ return ctx;
135
+ }
136
+ var SIDE_CLASSES = {
137
+ right: "inset-y-0 right-0 h-full w-80 max-w-full translate-x-0",
138
+ left: "inset-y-0 left-0 h-full w-80 max-w-full translate-x-0",
139
+ top: "inset-x-0 top-0 w-full h-auto max-h-[80vh] translate-y-0",
140
+ bottom: "inset-x-0 bottom-0 w-full h-auto max-h-[80vh] translate-y-0"
141
+ };
142
+ var Drawer = ({ open, onOpenChange, side = "right", children }) => {
143
+ useEscapeKey(open, () => onOpenChange(false));
144
+ return /* @__PURE__ */ jsx2(DrawerContext.Provider, { value: { open, onOpenChange, side }, children });
145
+ };
146
+ Drawer.displayName = "Drawer";
147
+ var DrawerContent = forwardRef2(({ className, children, ...props }, ref) => {
148
+ const { open, onOpenChange, side } = useDrawerContext();
149
+ const portalContainer = usePortalContainer();
150
+ if (!open || !portalContainer) return null;
151
+ return createPortal2(
152
+ /* @__PURE__ */ jsxs2("div", { className: "fixed inset-0 z-50", role: "dialog", "aria-modal": "true", children: [
153
+ /* @__PURE__ */ jsx2(
154
+ "div",
155
+ {
156
+ className: "absolute inset-0 bg-overlay transition-opacity",
157
+ onClick: () => onOpenChange(false),
158
+ "aria-hidden": "true"
159
+ }
160
+ ),
161
+ /* @__PURE__ */ jsx2(
162
+ "div",
163
+ {
164
+ ref,
165
+ className: cn(
166
+ "absolute flex flex-col bg-surface-1 shadow-xl transition-transform duration-300 ease-in-out",
167
+ SIDE_CLASSES[side],
168
+ className
169
+ ),
170
+ onClick: (e) => e.stopPropagation(),
171
+ ...props,
172
+ children
173
+ }
174
+ )
175
+ ] }),
176
+ portalContainer
177
+ );
178
+ });
179
+ DrawerContent.displayName = "DrawerContent";
180
+ var DrawerHeader = forwardRef2(({ className, ...props }, ref) => {
181
+ return /* @__PURE__ */ jsx2(
182
+ "div",
183
+ {
184
+ ref,
185
+ className: cn("flex flex-col gap-1.5 border-b border-border-base px-6 py-4", className),
186
+ ...props
187
+ }
188
+ );
189
+ });
190
+ DrawerHeader.displayName = "DrawerHeader";
191
+ var DrawerTitle = forwardRef2(({ className, ...props }, ref) => {
192
+ return /* @__PURE__ */ jsx2("h2", { ref, className: cn("text-lg font-semibold text-text-primary", className), ...props });
193
+ });
194
+ DrawerTitle.displayName = "DrawerTitle";
195
+ var DrawerDescription = forwardRef2(({ className, ...props }, ref) => {
196
+ return /* @__PURE__ */ jsx2("p", { ref, className: cn("text-sm text-text-secondary", className), ...props });
197
+ });
198
+ DrawerDescription.displayName = "DrawerDescription";
199
+ var DrawerFooter = forwardRef2(({ className, ...props }, ref) => {
200
+ return /* @__PURE__ */ jsx2(
201
+ "div",
202
+ {
203
+ ref,
204
+ className: cn("mt-auto flex items-center justify-end gap-2 border-t border-border-base px-6 py-4", className),
205
+ ...props
206
+ }
207
+ );
208
+ });
209
+ DrawerFooter.displayName = "DrawerFooter";
210
+ var DrawerClose = forwardRef2(({ className, children, type = "button", ...props }, ref) => {
211
+ const { onOpenChange } = useDrawerContext();
212
+ return /* @__PURE__ */ jsx2(
213
+ "button",
214
+ {
215
+ ref,
216
+ type,
217
+ onClick: () => onOpenChange(false),
218
+ className: cn(
219
+ "inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium text-text-secondary transition-colors hover:bg-surface-3 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30",
220
+ className
221
+ ),
222
+ ...props,
223
+ children: children ?? /* @__PURE__ */ jsx2(X, { size: 18 })
224
+ }
225
+ );
226
+ });
227
+ DrawerClose.displayName = "DrawerClose";
228
+
229
+ // src/components/table.tsx
230
+ import { forwardRef as forwardRef3 } from "react";
231
+ import { jsx as jsx3 } from "react/jsx-runtime";
232
+ var Table = forwardRef3(({ className, ...props }, ref) => {
233
+ return /* @__PURE__ */ jsx3("div", { className: "w-full overflow-auto", children: /* @__PURE__ */ jsx3(
234
+ "table",
235
+ {
236
+ ref,
237
+ className: cn("w-full caption-bottom text-sm", className),
238
+ ...props
239
+ }
240
+ ) });
241
+ });
242
+ var TableHeader = forwardRef3(({ className, ...props }, ref) => {
243
+ return /* @__PURE__ */ jsx3("thead", { ref, className: cn("[&_tr]:border-b border-border-base", className), ...props });
244
+ });
245
+ var TableBody = forwardRef3(({ className, ...props }, ref) => {
246
+ return /* @__PURE__ */ jsx3("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props });
247
+ });
248
+ var TableRow = forwardRef3(({ className, ...props }, ref) => {
249
+ return /* @__PURE__ */ jsx3("tr", { ref, className: cn("border-b border-border-base transition-colors hover:bg-surface-2", className), ...props });
250
+ });
251
+ var TableHead = forwardRef3(({ className, ...props }, ref) => {
252
+ return /* @__PURE__ */ jsx3("th", { ref, className: cn("h-10 px-4 text-left align-middle font-medium text-text-secondary [&:has([role=checkbox])]:pr-0", className), ...props });
253
+ });
254
+ var TableCell = forwardRef3(({ className, ...props }, ref) => {
255
+ return /* @__PURE__ */ jsx3("td", { ref, className: cn("px-4 py-3 align-middle text-text-primary [&:has([role=checkbox])]:pr-0", className), ...props });
256
+ });
257
+ Table.displayName = "Table";
258
+ TableHeader.displayName = "TableHeader";
259
+ TableBody.displayName = "TableBody";
260
+ TableRow.displayName = "TableRow";
261
+ TableHead.displayName = "TableHead";
262
+ TableCell.displayName = "TableCell";
263
+
264
+ // src/components/toast.tsx
265
+ import { forwardRef as forwardRef4, useCallback as useCallback2, useEffect, useRef, useState } from "react";
266
+ import { createPortal as createPortal3 } from "react-dom";
267
+ import { X as X2 } from "lucide-react";
268
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
269
+ var listeners = /* @__PURE__ */ new Set();
270
+ var toastItems = [];
271
+ var toastTimers = /* @__PURE__ */ new Map();
272
+ function notify() {
273
+ listeners.forEach((fn) => fn([...toastItems]));
274
+ }
275
+ function addToast(message, type) {
276
+ const id = Math.random().toString(36).slice(2);
277
+ toastItems = [...toastItems, { id, message, type }];
278
+ notify();
279
+ const timer = setTimeout(() => removeToast(id), 3e3);
280
+ toastTimers.set(id, timer);
281
+ }
282
+ function removeToast(id) {
283
+ const timer = toastTimers.get(id);
284
+ if (timer) {
285
+ clearTimeout(timer);
286
+ toastTimers.delete(id);
287
+ }
288
+ toastItems = toastItems.filter((t) => t.id !== id);
289
+ notify();
290
+ }
291
+ var toast = {
292
+ success: (message) => addToast(message, "success"),
293
+ error: (message) => addToast(message, "error"),
294
+ info: (message) => addToast(message, "info")
295
+ };
296
+ var typeStyles = {
297
+ success: "bg-success-soft border-success/30 text-success-soft-fg",
298
+ error: "bg-danger-soft border-danger/30 text-danger-soft-fg",
299
+ info: "bg-info-soft border-info/30 text-info-soft-fg"
300
+ };
301
+ var typeLabel = {
302
+ success: "\u6210\u529F",
303
+ error: "\u9519\u8BEF",
304
+ info: "\u63D0\u793A"
305
+ };
306
+ var Toaster = forwardRef4(({ className, ...props }, ref) => {
307
+ const [items, setItems] = useState([]);
308
+ const mountedRef = useRef(true);
309
+ const portalContainer = usePortalContainer();
310
+ const handleUpdate = useCallback2((next) => {
311
+ if (mountedRef.current) {
312
+ setItems(next);
313
+ }
314
+ }, []);
315
+ useEffect(() => {
316
+ listeners.add(handleUpdate);
317
+ return () => {
318
+ mountedRef.current = false;
319
+ listeners.delete(handleUpdate);
320
+ };
321
+ }, [handleUpdate]);
322
+ if (items.length === 0 || !portalContainer) return null;
323
+ return createPortal3(
324
+ /* @__PURE__ */ jsx4(
325
+ "div",
326
+ {
327
+ ref,
328
+ className: cn("fixed top-4 right-4 z-[9999] flex w-80 flex-col gap-2", className),
329
+ role: "region",
330
+ "aria-label": "\u901A\u77E5",
331
+ ...props,
332
+ children: items.map((item) => /* @__PURE__ */ jsxs3(
333
+ "div",
334
+ {
335
+ className: cn(
336
+ "flex items-start justify-between gap-2 rounded-md border px-4 py-3 text-sm shadow-md",
337
+ typeStyles[item.type]
338
+ ),
339
+ role: "alert",
340
+ children: [
341
+ /* @__PURE__ */ jsxs3("div", { children: [
342
+ /* @__PURE__ */ jsxs3("span", { className: "font-semibold", children: [
343
+ typeLabel[item.type],
344
+ "\uFF1A"
345
+ ] }),
346
+ item.message
347
+ ] }),
348
+ /* @__PURE__ */ jsx4(
349
+ "button",
350
+ {
351
+ type: "button",
352
+ onClick: () => removeToast(item.id),
353
+ className: "mt-0.5 shrink-0 opacity-60 hover:opacity-100 transition-opacity",
354
+ "aria-label": "\u5173\u95ED\u901A\u77E5",
355
+ children: /* @__PURE__ */ jsx4(X2, { size: 14 })
356
+ }
357
+ )
358
+ ]
359
+ },
360
+ item.id
361
+ ))
362
+ }
363
+ ),
364
+ portalContainer
365
+ );
366
+ });
367
+ Toaster.displayName = "Toaster";
368
+
369
+ // src/components/pagination.tsx
370
+ import { forwardRef as forwardRef5 } from "react";
371
+ import { ChevronLeft, ChevronRight } from "lucide-react";
372
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
373
+ function getPageNumbers(page, totalPages) {
374
+ if (totalPages <= 5) {
375
+ return Array.from({ length: totalPages }, (_, i) => i + 1);
376
+ }
377
+ if (page <= 3) {
378
+ return [1, 2, 3, "...", totalPages];
379
+ }
380
+ if (page >= totalPages - 2) {
381
+ return [1, "...", totalPages - 2, totalPages - 1, totalPages];
382
+ }
383
+ return [1, "...", page, "...", totalPages];
384
+ }
385
+ var Pagination = forwardRef5(({ page, totalPages, onPageChange, className }, ref) => {
386
+ const pageNumbers = getPageNumbers(page, totalPages);
387
+ const btnBase = "inline-flex h-8 min-w-8 shrink-0 items-center justify-center rounded-lg px-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:pointer-events-none disabled:opacity-40";
388
+ const btnGhost = "border border-border-strong bg-surface-1 text-text-primary hover:border-primary/60 hover:bg-surface-2";
389
+ return /* @__PURE__ */ jsxs4("div", { ref, className: cn("flex max-w-full flex-wrap items-center gap-1", className), children: [
390
+ /* @__PURE__ */ jsx5(
391
+ "button",
392
+ {
393
+ type: "button",
394
+ className: cn(btnBase, btnGhost),
395
+ disabled: page <= 1,
396
+ onClick: () => onPageChange(page - 1),
397
+ "aria-label": "\u4E0A\u4E00\u9875",
398
+ children: /* @__PURE__ */ jsx5(ChevronLeft, { size: 16 })
399
+ }
400
+ ),
401
+ pageNumbers.map(
402
+ (p, idx) => p === "..." ? /* @__PURE__ */ jsx5("span", { className: "inline-flex h-8 min-w-6 shrink-0 items-center justify-center text-text-tertiary select-none", children: "\u2026" }, `ellipsis-${idx}`) : /* @__PURE__ */ jsx5(
403
+ "button",
404
+ {
405
+ type: "button",
406
+ className: cn(
407
+ btnBase,
408
+ p === page ? "bg-primary text-primary-fg shadow-sm" : btnGhost
409
+ ),
410
+ onClick: () => onPageChange(p),
411
+ "aria-current": p === page ? "page" : void 0,
412
+ children: p
413
+ },
414
+ p
415
+ )
416
+ ),
417
+ /* @__PURE__ */ jsx5(
418
+ "button",
419
+ {
420
+ type: "button",
421
+ className: cn(btnBase, btnGhost),
422
+ disabled: page >= totalPages,
423
+ onClick: () => onPageChange(page + 1),
424
+ "aria-label": "\u4E0B\u4E00\u9875",
425
+ children: /* @__PURE__ */ jsx5(ChevronRight, { size: 16 })
426
+ }
427
+ )
428
+ ] });
429
+ });
430
+ Pagination.displayName = "Pagination";
431
+
432
+ // src/components/select.tsx
433
+ import { forwardRef as forwardRef6, useEffect as useEffect2, useId, useMemo, useRef as useRef2, useState as useState2 } from "react";
434
+ import { ChevronDown } from "lucide-react";
435
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
436
+ var Select = forwardRef6(
437
+ ({
438
+ className,
439
+ label,
440
+ error,
441
+ helperText,
442
+ options,
443
+ placeholder,
444
+ id,
445
+ value,
446
+ defaultValue,
447
+ onChange,
448
+ disabled,
449
+ name,
450
+ required,
451
+ ...props
452
+ }, ref) => {
453
+ const generatedId = useId();
454
+ const selectId = id ?? generatedId;
455
+ const listboxId = `${selectId}-listbox`;
456
+ const rootRef = useRef2(null);
457
+ const hiddenSelectRef = useRef2(null);
458
+ const isControlled = value !== void 0;
459
+ const [open, setOpen] = useState2(false);
460
+ const [internalValue, setInternalValue] = useState2(() => String(defaultValue ?? ""));
461
+ const selectedValue = String(isControlled ? value : internalValue);
462
+ const selectedOption = useMemo(
463
+ () => options.find((option) => String(option.value) === selectedValue),
464
+ [options, selectedValue]
465
+ );
466
+ function setSelectRef(node) {
467
+ hiddenSelectRef.current = node;
468
+ if (typeof ref === "function") {
469
+ ref(node);
470
+ } else if (ref) {
471
+ ref.current = node;
472
+ }
473
+ }
474
+ function handleSelect(nextValue) {
475
+ if (disabled) return;
476
+ if (!isControlled) {
477
+ setInternalValue(nextValue);
478
+ }
479
+ if (hiddenSelectRef.current) {
480
+ hiddenSelectRef.current.value = nextValue;
481
+ onChange?.({ target: hiddenSelectRef.current, currentTarget: hiddenSelectRef.current });
482
+ }
483
+ setOpen(false);
484
+ }
485
+ function toggleOpen() {
486
+ if (disabled) return;
487
+ setOpen((current) => !current);
488
+ }
489
+ function handleTriggerKeyDown(event) {
490
+ if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
491
+ event.preventDefault();
492
+ setOpen(true);
493
+ return;
494
+ }
495
+ if (event.key === "Escape") {
496
+ event.preventDefault();
497
+ setOpen(false);
498
+ }
499
+ }
500
+ useEffect2(() => {
501
+ if (!open) return;
502
+ function handlePointerDown(event) {
503
+ if (!rootRef.current?.contains(event.target)) {
504
+ setOpen(false);
505
+ }
506
+ }
507
+ function handleKeyDown(event) {
508
+ if (event.key === "Escape") {
509
+ setOpen(false);
510
+ }
511
+ }
512
+ document.addEventListener("pointerdown", handlePointerDown);
513
+ document.addEventListener("keydown", handleKeyDown);
514
+ return () => {
515
+ document.removeEventListener("pointerdown", handlePointerDown);
516
+ document.removeEventListener("keydown", handleKeyDown);
517
+ };
518
+ }, [open]);
519
+ return /* @__PURE__ */ jsxs5("div", { ref: rootRef, className: "relative flex flex-col gap-1.5", children: [
520
+ label && /* @__PURE__ */ jsx6("label", { htmlFor: selectId, className: "text-sm font-medium text-text-primary", children: label }),
521
+ /* @__PURE__ */ jsxs5(
522
+ "select",
523
+ {
524
+ ref: setSelectRef,
525
+ id: `${selectId}-native`,
526
+ name,
527
+ value: selectedValue,
528
+ required,
529
+ disabled,
530
+ tabIndex: -1,
531
+ "aria-hidden": "true",
532
+ className: "sr-only",
533
+ onChange,
534
+ ...props,
535
+ children: [
536
+ placeholder && /* @__PURE__ */ jsx6("option", { value: "", children: placeholder }),
537
+ options.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt.value, children: opt.label }, opt.value))
538
+ ]
539
+ }
540
+ ),
541
+ /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
542
+ /* @__PURE__ */ jsxs5(
543
+ "button",
544
+ {
545
+ type: "button",
546
+ id: selectId,
547
+ disabled,
548
+ "aria-haspopup": "listbox",
549
+ "aria-expanded": open,
550
+ "aria-controls": listboxId,
551
+ className: cn(
552
+ "flex h-10 w-full items-center justify-between rounded-lg border bg-surface-1 px-3 py-2 text-left text-sm leading-5 text-text-primary shadow-sm",
553
+ "transition-colors hover:border-primary/60",
554
+ "focus-visible:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/25 focus-visible:ring-offset-1 focus-visible:ring-offset-surface-0",
555
+ "disabled:cursor-not-allowed disabled:bg-surface-2 disabled:text-text-tertiary disabled:opacity-70",
556
+ error ? "border-danger focus-visible:ring-danger/25" : "border-border-strong",
557
+ className
558
+ ),
559
+ onClick: toggleOpen,
560
+ onKeyDown: handleTriggerKeyDown,
561
+ children: [
562
+ /* @__PURE__ */ jsx6("span", { className: cn("truncate", !selectedOption && "text-text-tertiary"), children: selectedOption?.label ?? placeholder ?? "\u8BF7\u9009\u62E9" }),
563
+ /* @__PURE__ */ jsx6(
564
+ ChevronDown,
565
+ {
566
+ "aria-hidden": "true",
567
+ className: cn("h-4 w-4 shrink-0 text-text-tertiary transition-transform", open && "rotate-180")
568
+ }
569
+ )
570
+ ]
571
+ }
572
+ ),
573
+ open && /* @__PURE__ */ jsxs5(
574
+ "div",
575
+ {
576
+ id: listboxId,
577
+ role: "listbox",
578
+ className: "absolute left-0 right-0 top-full z-[70] mt-1 max-h-60 overflow-auto rounded-lg border border-border-strong bg-surface-1 p-1 shadow-xl",
579
+ children: [
580
+ placeholder && /* @__PURE__ */ jsx6(
581
+ "button",
582
+ {
583
+ type: "button",
584
+ role: "option",
585
+ "aria-selected": selectedValue === "",
586
+ className: cn(
587
+ "flex w-full items-center rounded-md px-2.5 py-2 text-left text-sm text-text-tertiary transition-colors hover:bg-surface-2",
588
+ selectedValue === "" && "bg-surface-2 text-text-primary"
589
+ ),
590
+ onClick: () => handleSelect(""),
591
+ children: placeholder
592
+ }
593
+ ),
594
+ options.map((option) => {
595
+ const optionValue = String(option.value);
596
+ const selected = optionValue === selectedValue;
597
+ return /* @__PURE__ */ jsx6(
598
+ "button",
599
+ {
600
+ type: "button",
601
+ role: "option",
602
+ "aria-selected": selected,
603
+ className: cn(
604
+ "flex w-full items-center rounded-md px-2.5 py-2 text-left text-sm text-text-primary transition-colors hover:bg-surface-2",
605
+ selected && "bg-primary-soft text-primary-soft-fg"
606
+ ),
607
+ onClick: () => handleSelect(optionValue),
608
+ children: option.label
609
+ },
610
+ option.value
611
+ );
612
+ })
613
+ ]
614
+ }
615
+ )
616
+ ] }),
617
+ error && /* @__PURE__ */ jsx6("p", { className: "text-xs text-danger", children: error }),
618
+ !error && helperText && /* @__PURE__ */ jsx6("p", { className: "text-xs text-text-secondary", children: helperText })
619
+ ] });
620
+ }
621
+ );
622
+ Select.displayName = "Select";
623
+
624
+ // src/components/checkbox.tsx
625
+ import { forwardRef as forwardRef7, useEffect as useEffect3, useId as useId2, useRef as useRef3 } from "react";
626
+ import { Check, Minus } from "lucide-react";
627
+ import { cva } from "class-variance-authority";
628
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
629
+ var checkboxVariants = cva(
630
+ "peer inline-flex shrink-0 items-center justify-center rounded-md border shadow-sm transition-colors peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring/35 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-surface-0 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
631
+ {
632
+ variants: {
633
+ size: {
634
+ sm: "h-4 w-4",
635
+ default: "h-[18px] w-[18px]",
636
+ lg: "h-5 w-5"
637
+ },
638
+ state: {
639
+ unchecked: "border-border-strong bg-surface-1 text-transparent hover:border-primary/70 hover:bg-surface-2",
640
+ checked: "border-primary bg-primary text-primary-fg",
641
+ indeterminate: "border-primary bg-primary text-primary-fg"
642
+ }
643
+ },
644
+ defaultVariants: {
645
+ size: "default",
646
+ state: "unchecked"
647
+ }
648
+ }
649
+ );
650
+ var Checkbox = forwardRef7(
651
+ ({
652
+ className,
653
+ size,
654
+ checked,
655
+ indeterminate = false,
656
+ disabled,
657
+ label,
658
+ labelClassName,
659
+ onCheckedChange,
660
+ id,
661
+ ...props
662
+ }, ref) => {
663
+ const innerRef = useRef3(null);
664
+ const generatedId = useId2();
665
+ const inputId = id ?? generatedId;
666
+ useEffect3(() => {
667
+ if (innerRef.current) {
668
+ innerRef.current.indeterminate = indeterminate;
669
+ }
670
+ }, [indeterminate]);
671
+ const state = indeterminate ? "indeterminate" : checked ? "checked" : "unchecked";
672
+ const iconSize = size === "sm" ? 11 : size === "lg" ? 14 : 12;
673
+ const control = /* @__PURE__ */ jsxs6("span", { className: "relative inline-flex", children: [
674
+ /* @__PURE__ */ jsx7(
675
+ "input",
676
+ {
677
+ ref: (node) => {
678
+ innerRef.current = node;
679
+ if (typeof ref === "function") ref(node);
680
+ else if (ref) ref.current = node;
681
+ },
682
+ id: inputId,
683
+ type: "checkbox",
684
+ className: "peer sr-only",
685
+ checked: checked ?? false,
686
+ disabled,
687
+ onChange: (e) => onCheckedChange?.(e.target.checked),
688
+ ...props
689
+ }
690
+ ),
691
+ /* @__PURE__ */ jsx7(
692
+ "span",
693
+ {
694
+ "aria-hidden": "true",
695
+ className: cn(checkboxVariants({ size, state }), className),
696
+ children: indeterminate ? /* @__PURE__ */ jsx7(Minus, { size: iconSize }) : checked ? /* @__PURE__ */ jsx7(Check, { size: iconSize }) : null
697
+ }
698
+ )
699
+ ] });
700
+ if (label === void 0 || label === null) return control;
701
+ return /* @__PURE__ */ jsxs6(
702
+ "label",
703
+ {
704
+ htmlFor: inputId,
705
+ className: cn(
706
+ "inline-flex min-h-5 cursor-pointer items-center gap-2.5 text-sm leading-5 text-text-primary select-none",
707
+ disabled && "cursor-not-allowed opacity-60",
708
+ labelClassName
709
+ ),
710
+ children: [
711
+ control,
712
+ /* @__PURE__ */ jsx7("span", { className: "pt-px", children: label })
713
+ ]
714
+ }
715
+ );
716
+ }
717
+ );
718
+ Checkbox.displayName = "Checkbox";
719
+
720
+ // src/components/radio.tsx
721
+ import { createContext as createContext2, forwardRef as forwardRef8, useContext as useContext2, useId as useId3, useState as useState3 } from "react";
722
+ import { cva as cva2 } from "class-variance-authority";
723
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
724
+ var RadioGroupContext = createContext2(null);
725
+ function useRadioGroupContext() {
726
+ return useContext2(RadioGroupContext);
727
+ }
728
+ var radioVariants = cva2(
729
+ "relative inline-flex shrink-0 items-center justify-center rounded-full border shadow-sm transition-colors peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring/35 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-surface-0 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
730
+ {
731
+ variants: {
732
+ size: {
733
+ sm: "h-4 w-4",
734
+ default: "h-[18px] w-[18px]",
735
+ lg: "h-5 w-5"
736
+ },
737
+ checked: {
738
+ true: "border-primary bg-surface-1",
739
+ false: "border-border-strong bg-surface-1 hover:border-primary/70 hover:bg-surface-2"
740
+ }
741
+ },
742
+ defaultVariants: {
743
+ size: "default",
744
+ checked: false
745
+ }
746
+ }
747
+ );
748
+ var Radio = forwardRef8(
749
+ ({
750
+ className,
751
+ size,
752
+ value,
753
+ checked: checkedProp,
754
+ disabled: disabledProp,
755
+ label,
756
+ name: nameProp,
757
+ onChange,
758
+ id,
759
+ ...props
760
+ }, ref) => {
761
+ const group = useRadioGroupContext();
762
+ const generatedId = useId3();
763
+ const inputId = id ?? generatedId;
764
+ const isChecked = group ? group.value === value : checkedProp ?? false;
765
+ const isDisabled = group ? group.disabled || (disabledProp ?? false) : disabledProp ?? false;
766
+ const inputName = group ? group.name : nameProp;
767
+ function handleChange() {
768
+ if (group) {
769
+ group.onValueChange(value);
770
+ } else {
771
+ onChange?.(value);
772
+ }
773
+ }
774
+ const dotSize = size === "sm" ? "h-1.5 w-1.5" : size === "lg" ? "h-2.5 w-2.5" : "h-2 w-2";
775
+ const control = /* @__PURE__ */ jsxs7("span", { className: "relative inline-flex", children: [
776
+ /* @__PURE__ */ jsx8(
777
+ "input",
778
+ {
779
+ ref,
780
+ id: inputId,
781
+ type: "radio",
782
+ className: "peer sr-only",
783
+ value,
784
+ checked: isChecked,
785
+ disabled: isDisabled,
786
+ name: inputName,
787
+ onChange: handleChange,
788
+ ...props
789
+ }
790
+ ),
791
+ /* @__PURE__ */ jsx8(
792
+ "span",
793
+ {
794
+ "aria-hidden": "true",
795
+ className: cn(radioVariants({ size, checked: isChecked }), className),
796
+ children: isChecked && /* @__PURE__ */ jsx8("span", { className: cn("rounded-full bg-primary", dotSize) })
797
+ }
798
+ )
799
+ ] });
800
+ if (label === void 0 || label === null) return control;
801
+ return /* @__PURE__ */ jsxs7(
802
+ "label",
803
+ {
804
+ htmlFor: inputId,
805
+ className: cn(
806
+ "inline-flex min-h-5 cursor-pointer items-center gap-2.5 text-sm leading-5 text-text-primary select-none",
807
+ isDisabled && "cursor-not-allowed opacity-60"
808
+ ),
809
+ children: [
810
+ control,
811
+ /* @__PURE__ */ jsx8("span", { className: "pt-px", children: label })
812
+ ]
813
+ }
814
+ );
815
+ }
816
+ );
817
+ Radio.displayName = "Radio";
818
+ var RadioGroup = forwardRef8(
819
+ ({
820
+ className,
821
+ value: valueProp,
822
+ defaultValue,
823
+ onValueChange,
824
+ disabled = false,
825
+ name,
826
+ orientation = "vertical",
827
+ children,
828
+ ...props
829
+ }, ref) => {
830
+ const [uncontrolledValue, setUncontrolledValue] = useState3(defaultValue);
831
+ const isControlled = valueProp !== void 0;
832
+ const currentValue = isControlled ? valueProp : uncontrolledValue;
833
+ function handleValueChange(newValue) {
834
+ if (!isControlled) {
835
+ setUncontrolledValue(newValue);
836
+ }
837
+ onValueChange?.(newValue);
838
+ }
839
+ return /* @__PURE__ */ jsx8(
840
+ RadioGroupContext.Provider,
841
+ {
842
+ value: {
843
+ value: currentValue,
844
+ onValueChange: handleValueChange,
845
+ name,
846
+ disabled
847
+ },
848
+ children: /* @__PURE__ */ jsx8(
849
+ "div",
850
+ {
851
+ ref,
852
+ role: "radiogroup",
853
+ className: cn(
854
+ "flex",
855
+ orientation === "vertical" ? "flex-col gap-2.5" : "flex-row flex-wrap gap-x-5 gap-y-2.5",
856
+ className
857
+ ),
858
+ ...props,
859
+ children
860
+ }
861
+ )
862
+ }
863
+ );
864
+ }
865
+ );
866
+ RadioGroup.displayName = "RadioGroup";
867
+
868
+ // src/components/slider.tsx
869
+ import { forwardRef as forwardRef9, useState as useState4 } from "react";
870
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
871
+ var Slider = forwardRef9(
872
+ ({
873
+ className,
874
+ value: valueProp,
875
+ defaultValue = 0,
876
+ min = 0,
877
+ max = 100,
878
+ step = 1,
879
+ disabled = false,
880
+ showValue = false,
881
+ onValueChange,
882
+ ...props
883
+ }, ref) => {
884
+ const isControlled = valueProp !== void 0;
885
+ const [uncontrolledValue, setUncontrolledValue] = useState4(defaultValue);
886
+ const currentValue = isControlled ? valueProp : uncontrolledValue;
887
+ const fillPercent = max === min ? 0 : (currentValue - min) / (max - min) * 100;
888
+ function updateValue(nextValue) {
889
+ if (!isControlled) {
890
+ setUncontrolledValue(nextValue);
891
+ }
892
+ onValueChange?.(nextValue);
893
+ }
894
+ function handleInput(e) {
895
+ updateValue(Number(e.currentTarget.value));
896
+ }
897
+ function handleChange(e) {
898
+ const newValue = Number(e.target.value);
899
+ if (newValue === currentValue) {
900
+ return;
901
+ }
902
+ updateValue(newValue);
903
+ }
904
+ return /* @__PURE__ */ jsxs8("div", { className: cn("flex flex-col gap-1.5", className), children: [
905
+ showValue && /* @__PURE__ */ jsx9("span", { className: "self-start text-xs font-semibold leading-none text-text-primary", children: currentValue }),
906
+ /* @__PURE__ */ jsxs8("div", { className: "relative flex h-6 w-full items-center", children: [
907
+ /* @__PURE__ */ jsx9("div", { className: "pointer-events-none absolute h-2 w-full overflow-hidden rounded-full bg-surface-3", children: /* @__PURE__ */ jsx9(
908
+ "div",
909
+ {
910
+ className: "h-full rounded-full bg-primary transition-all",
911
+ style: { width: `${fillPercent}%` }
912
+ }
913
+ ) }),
914
+ /* @__PURE__ */ jsx9(
915
+ "input",
916
+ {
917
+ ref,
918
+ type: "range",
919
+ min,
920
+ max,
921
+ step,
922
+ value: currentValue,
923
+ disabled,
924
+ onInput: handleInput,
925
+ onChange: handleChange,
926
+ className: cn(
927
+ "relative h-6 w-full cursor-pointer appearance-none bg-transparent",
928
+ // thumb 样式
929
+ "[&::-webkit-slider-thumb]:h-[18px] [&::-webkit-slider-thumb]:w-[18px] [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-surface-1 [&::-webkit-slider-thumb]:shadow-md [&::-webkit-slider-thumb]:transition-transform [&::-webkit-slider-thumb]:hover:scale-110",
930
+ "[&::-moz-range-thumb]:h-[18px] [&::-moz-range-thumb]:w-[18px] [&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary [&::-moz-range-thumb]:bg-surface-1 [&::-moz-range-thumb]:shadow-md",
931
+ // track 透明(用自定义 div 代替)
932
+ "[&::-webkit-slider-runnable-track]:bg-transparent",
933
+ "[&::-moz-range-track]:bg-transparent",
934
+ // focus
935
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0",
936
+ // disabled
937
+ "disabled:cursor-not-allowed disabled:opacity-50"
938
+ ),
939
+ ...props
940
+ }
941
+ )
942
+ ] })
943
+ ] });
944
+ }
945
+ );
946
+ Slider.displayName = "Slider";
947
+
948
+ // src/components/number-input.tsx
949
+ import { forwardRef as forwardRef10, useState as useState5 } from "react";
950
+ import { Minus as Minus2, Plus } from "lucide-react";
951
+ import { cva as cva3 } from "class-variance-authority";
952
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
953
+ var numberInputVariants = cva3(
954
+ "inline-flex items-center overflow-hidden rounded-lg border border-border-strong bg-surface-1 shadow-sm transition-colors focus-within:border-primary focus-within:ring-2 focus-within:ring-ring/25 has-[:disabled]:pointer-events-none has-[:disabled]:opacity-60",
955
+ {
956
+ variants: {
957
+ size: {
958
+ sm: "h-8 text-xs",
959
+ default: "h-9 text-sm",
960
+ lg: "h-11 text-base"
961
+ }
962
+ },
963
+ defaultVariants: {
964
+ size: "default"
965
+ }
966
+ }
967
+ );
968
+ var NumberInput = forwardRef10(
969
+ ({
970
+ className,
971
+ size,
972
+ value: valueProp,
973
+ defaultValue = 0,
974
+ min,
975
+ max,
976
+ step = 1,
977
+ precision = 0,
978
+ disabled = false,
979
+ placeholder,
980
+ onValueChange,
981
+ ...props
982
+ }, ref) => {
983
+ const isControlled = valueProp !== void 0;
984
+ const [uncontrolledValue, setUncontrolledValue] = useState5(defaultValue);
985
+ const currentValue = isControlled ? valueProp : uncontrolledValue;
986
+ function clamp(val) {
987
+ let result = val;
988
+ if (min !== void 0) result = Math.max(min, result);
989
+ if (max !== void 0) result = Math.min(max, result);
990
+ return result;
991
+ }
992
+ function format(val) {
993
+ return val.toFixed(precision);
994
+ }
995
+ function commit(newVal) {
996
+ const clamped = clamp(Number(newVal.toFixed(precision)));
997
+ if (!isControlled) {
998
+ setUncontrolledValue(clamped);
999
+ }
1000
+ onValueChange?.(clamped);
1001
+ }
1002
+ function handleDecrement() {
1003
+ commit(currentValue - step);
1004
+ }
1005
+ function handleIncrement() {
1006
+ commit(currentValue + step);
1007
+ }
1008
+ function handleInputChange(e) {
1009
+ const parsed = parseFloat(e.target.value);
1010
+ if (!Number.isNaN(parsed)) {
1011
+ commit(parsed);
1012
+ }
1013
+ }
1014
+ function handleBlur(e) {
1015
+ const parsed = parseFloat(e.target.value);
1016
+ if (!Number.isNaN(parsed)) {
1017
+ commit(parsed);
1018
+ } else {
1019
+ if (!isControlled) {
1020
+ setUncontrolledValue(uncontrolledValue);
1021
+ }
1022
+ }
1023
+ }
1024
+ const atMin = min !== void 0 && currentValue <= min;
1025
+ const atMax = max !== void 0 && currentValue >= max;
1026
+ const btnBase = "inline-flex shrink-0 items-center justify-center bg-surface-2 text-text-secondary transition-colors hover:bg-surface-3 hover:text-text-primary disabled:pointer-events-none disabled:opacity-40";
1027
+ const btnSizeCls = size === "sm" ? "h-8 w-7" : size === "lg" ? "h-11 w-10" : "h-9 w-9";
1028
+ const iconSize = size === "sm" ? 12 : size === "lg" ? 16 : 14;
1029
+ return /* @__PURE__ */ jsxs9("div", { className: cn(numberInputVariants({ size }), className), children: [
1030
+ /* @__PURE__ */ jsx10(
1031
+ "button",
1032
+ {
1033
+ type: "button",
1034
+ "aria-label": "\u51CF\u5C11\u6570\u503C",
1035
+ disabled: disabled || atMin,
1036
+ onClick: handleDecrement,
1037
+ className: cn(btnBase, btnSizeCls, "border-r border-border-base"),
1038
+ tabIndex: -1,
1039
+ children: /* @__PURE__ */ jsx10(Minus2, { size: iconSize })
1040
+ }
1041
+ ),
1042
+ /* @__PURE__ */ jsx10(
1043
+ "input",
1044
+ {
1045
+ ref,
1046
+ type: "number",
1047
+ className: cn(
1048
+ "min-w-0 flex-1 bg-transparent px-2 text-center font-medium text-text-primary outline-none",
1049
+ "placeholder:text-text-tertiary",
1050
+ // 隐藏原生 spinner
1051
+ "[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
1052
+ ),
1053
+ value: format(currentValue),
1054
+ disabled,
1055
+ placeholder,
1056
+ min,
1057
+ max,
1058
+ step,
1059
+ onChange: handleInputChange,
1060
+ onBlur: handleBlur,
1061
+ ...props
1062
+ }
1063
+ ),
1064
+ /* @__PURE__ */ jsx10(
1065
+ "button",
1066
+ {
1067
+ type: "button",
1068
+ "aria-label": "\u589E\u52A0\u6570\u503C",
1069
+ disabled: disabled || atMax,
1070
+ onClick: handleIncrement,
1071
+ className: cn(btnBase, btnSizeCls, "border-l border-border-base"),
1072
+ tabIndex: -1,
1073
+ children: /* @__PURE__ */ jsx10(Plus, { size: iconSize })
1074
+ }
1075
+ )
1076
+ ] });
1077
+ }
1078
+ );
1079
+ NumberInput.displayName = "NumberInput";
1080
+
1081
+ // src/components/otp-input.tsx
1082
+ import { forwardRef as forwardRef11, useRef as useRef4, useCallback as useCallback3 } from "react";
1083
+ import { jsx as jsx11 } from "react/jsx-runtime";
1084
+ var OTPInput = forwardRef11(
1085
+ ({
1086
+ value,
1087
+ onValueChange,
1088
+ length = 6,
1089
+ disabled = false,
1090
+ onComplete,
1091
+ className
1092
+ }, ref) => {
1093
+ const inputRefs = useRef4([]);
1094
+ const focusAt = useCallback3((index) => {
1095
+ const el = inputRefs.current[index];
1096
+ if (el) {
1097
+ el.focus();
1098
+ el.setSelectionRange(el.value.length, el.value.length);
1099
+ }
1100
+ }, []);
1101
+ const updateValueAt = useCallback3(
1102
+ (index, char) => {
1103
+ const chars = value.padEnd(length, "").split("").slice(0, length);
1104
+ chars[index] = char;
1105
+ const next = chars.join("");
1106
+ onValueChange(next);
1107
+ if (char && next.replace(/\s/g, "").length === length && !next.includes(" ")) {
1108
+ onComplete?.(next);
1109
+ }
1110
+ },
1111
+ [value, length, onValueChange, onComplete]
1112
+ );
1113
+ const handleChange = useCallback3(
1114
+ (index, e) => {
1115
+ const raw = e.target.value;
1116
+ const digit = raw.replace(/\D/g, "").slice(-1);
1117
+ updateValueAt(index, digit);
1118
+ if (digit && index < length - 1) {
1119
+ focusAt(index + 1);
1120
+ }
1121
+ },
1122
+ [length, updateValueAt, focusAt]
1123
+ );
1124
+ const handleKeyDown = useCallback3(
1125
+ (index, e) => {
1126
+ if (e.key === "ArrowLeft") {
1127
+ e.preventDefault();
1128
+ if (index > 0) focusAt(index - 1);
1129
+ } else if (e.key === "ArrowRight") {
1130
+ e.preventDefault();
1131
+ if (index < length - 1) focusAt(index + 1);
1132
+ } else if (e.key === "Backspace") {
1133
+ e.preventDefault();
1134
+ const current = value[index] ?? "";
1135
+ if (current) {
1136
+ updateValueAt(index, "");
1137
+ } else if (index > 0) {
1138
+ updateValueAt(index - 1, "");
1139
+ focusAt(index - 1);
1140
+ }
1141
+ }
1142
+ },
1143
+ [value, length, focusAt, updateValueAt]
1144
+ );
1145
+ const handlePaste = useCallback3(
1146
+ (e) => {
1147
+ e.preventDefault();
1148
+ const pasted = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, length);
1149
+ if (!pasted) return;
1150
+ const chars = pasted.padEnd(length, "").split("").slice(0, length);
1151
+ const next = chars.join("");
1152
+ onValueChange(next);
1153
+ const nextFocus = Math.min(pasted.length, length - 1);
1154
+ focusAt(nextFocus);
1155
+ if (pasted.length === length) {
1156
+ onComplete?.(next);
1157
+ }
1158
+ },
1159
+ [length, onValueChange, onComplete, focusAt]
1160
+ );
1161
+ return /* @__PURE__ */ jsx11(
1162
+ "div",
1163
+ {
1164
+ ref,
1165
+ className: cn("flex items-center gap-2", className),
1166
+ role: "group",
1167
+ "aria-label": "\u9A8C\u8BC1\u7801\u8F93\u5165",
1168
+ children: Array.from({ length }, (_, i) => {
1169
+ const char = value[i] ?? "";
1170
+ const isFocused = false;
1171
+ return /* @__PURE__ */ jsx11(
1172
+ "input",
1173
+ {
1174
+ ref: (el) => {
1175
+ inputRefs.current[i] = el;
1176
+ },
1177
+ type: "text",
1178
+ inputMode: "numeric",
1179
+ pattern: "[0-9]",
1180
+ maxLength: 1,
1181
+ value: char,
1182
+ disabled,
1183
+ "aria-label": `\u9A8C\u8BC1\u7801\u7B2C ${i + 1} \u4F4D`,
1184
+ className: cn(
1185
+ "h-10 w-10 rounded-md border text-center text-sm font-medium text-text-primary transition-colors",
1186
+ "outline-none focus:border-primary focus:ring-2 focus:ring-ring/20",
1187
+ "disabled:cursor-not-allowed disabled:opacity-50",
1188
+ char ? "border-border-strong" : "border-border-base"
1189
+ ),
1190
+ onChange: (e) => handleChange(i, e),
1191
+ onKeyDown: (e) => handleKeyDown(i, e),
1192
+ onPaste: handlePaste,
1193
+ onClick: (e) => e.target.select()
1194
+ },
1195
+ i
1196
+ );
1197
+ })
1198
+ }
1199
+ );
1200
+ }
1201
+ );
1202
+ OTPInput.displayName = "OTPInput";
1203
+
1204
+ // src/components/form.tsx
1205
+ import {
1206
+ createContext as createContext3,
1207
+ forwardRef as forwardRef12,
1208
+ useContext as useContext3,
1209
+ useId as useId4,
1210
+ cloneElement
1211
+ } from "react";
1212
+ import { jsx as jsx12 } from "react/jsx-runtime";
1213
+ var FormFieldContext = createContext3(null);
1214
+ function useFormFieldContext() {
1215
+ const ctx = useContext3(FormFieldContext);
1216
+ if (!ctx) {
1217
+ throw new Error("FormField \u5B50\u7EC4\u4EF6\u5FC5\u987B\u5728 <FormField> \u5185\u4F7F\u7528");
1218
+ }
1219
+ return ctx;
1220
+ }
1221
+ var Form = forwardRef12(
1222
+ ({ className, onSubmit, children, ...props }, ref) => {
1223
+ function handleSubmit(event) {
1224
+ event.preventDefault();
1225
+ onSubmit?.(event);
1226
+ }
1227
+ return /* @__PURE__ */ jsx12(
1228
+ "form",
1229
+ {
1230
+ ref,
1231
+ className: cn(className),
1232
+ onSubmit: handleSubmit,
1233
+ ...props,
1234
+ children
1235
+ }
1236
+ );
1237
+ }
1238
+ );
1239
+ Form.displayName = "Form";
1240
+ var FormField = forwardRef12(
1241
+ ({ name, error, className, children, ...props }, ref) => {
1242
+ const generatedId = useId4();
1243
+ return /* @__PURE__ */ jsx12(FormFieldContext.Provider, { value: { name, error, id: generatedId }, children: /* @__PURE__ */ jsx12("div", { ref, className: cn(className), ...props, children }) });
1244
+ }
1245
+ );
1246
+ FormField.displayName = "FormField";
1247
+ var FormItem = forwardRef12(
1248
+ ({ className, children, ...props }, ref) => {
1249
+ return /* @__PURE__ */ jsx12(
1250
+ "div",
1251
+ {
1252
+ ref,
1253
+ className: cn("flex flex-col gap-1.5", className),
1254
+ ...props,
1255
+ children
1256
+ }
1257
+ );
1258
+ }
1259
+ );
1260
+ FormItem.displayName = "FormItem";
1261
+ var FormLabel = forwardRef12(
1262
+ ({ className, required, children, htmlFor, ...props }, ref) => {
1263
+ const ctx = useFormFieldContext();
1264
+ return /* @__PURE__ */ jsx12(
1265
+ Label,
1266
+ {
1267
+ ref,
1268
+ htmlFor: htmlFor ?? ctx.id,
1269
+ required,
1270
+ className: cn(className),
1271
+ ...props,
1272
+ children
1273
+ }
1274
+ );
1275
+ }
1276
+ );
1277
+ FormLabel.displayName = "FormLabel";
1278
+ function FormControl({ children }) {
1279
+ const ctx = useFormFieldContext();
1280
+ const descriptionId = `${ctx.id}-description`;
1281
+ const messageId = `${ctx.id}-message`;
1282
+ return cloneElement(children, {
1283
+ id: ctx.id,
1284
+ "aria-describedby": ctx.error ? `${descriptionId} ${messageId}` : descriptionId,
1285
+ "aria-invalid": ctx.error ? true : void 0
1286
+ });
1287
+ }
1288
+ FormControl.displayName = "FormControl";
1289
+ var FormDescription = forwardRef12(
1290
+ ({ className, children, ...props }, ref) => {
1291
+ const ctx = useFormFieldContext();
1292
+ return /* @__PURE__ */ jsx12(
1293
+ "p",
1294
+ {
1295
+ ref,
1296
+ id: `${ctx.id}-description`,
1297
+ className: cn("text-xs text-text-secondary", className),
1298
+ ...props,
1299
+ children
1300
+ }
1301
+ );
1302
+ }
1303
+ );
1304
+ FormDescription.displayName = "FormDescription";
1305
+ var FormMessage = forwardRef12(
1306
+ ({ className, children, ...props }, ref) => {
1307
+ const ctx = useFormFieldContext();
1308
+ const message = ctx.error ?? (typeof children === "string" ? children : void 0);
1309
+ if (!message) return null;
1310
+ return /* @__PURE__ */ jsx12(
1311
+ "p",
1312
+ {
1313
+ ref,
1314
+ id: `${ctx.id}-message`,
1315
+ className: cn("text-xs text-danger-soft-fg", className),
1316
+ role: "alert",
1317
+ ...props,
1318
+ children: message
1319
+ }
1320
+ );
1321
+ }
1322
+ );
1323
+ FormMessage.displayName = "FormMessage";
1324
+
1325
+ // src/components/tabs.tsx
1326
+ import { createContext as createContext4, forwardRef as forwardRef13, useContext as useContext4 } from "react";
1327
+ import { jsx as jsx13 } from "react/jsx-runtime";
1328
+ var TabsContext = createContext4(null);
1329
+ function useTabsContext() {
1330
+ const ctx = useContext4(TabsContext);
1331
+ if (!ctx) throw new Error("Tabs \u5B50\u7EC4\u4EF6\u5FC5\u987B\u5728 <Tabs> \u5185\u4F7F\u7528");
1332
+ return ctx;
1333
+ }
1334
+ var Tabs = forwardRef13(({ value, onValueChange, children, className }, ref) => {
1335
+ return /* @__PURE__ */ jsx13(TabsContext.Provider, { value: { value, onValueChange }, children: /* @__PURE__ */ jsx13("div", { ref, className: cn("w-full", className), children }) });
1336
+ });
1337
+ var TabsList = forwardRef13(({ className, children, ...props }, ref) => {
1338
+ return /* @__PURE__ */ jsx13(
1339
+ "div",
1340
+ {
1341
+ ref,
1342
+ role: "tablist",
1343
+ className: cn("inline-flex w-full items-center gap-0 border-b border-border-base", className),
1344
+ ...props,
1345
+ children
1346
+ }
1347
+ );
1348
+ });
1349
+ var TabsTrigger = forwardRef13(({ value, className, children, ...props }, ref) => {
1350
+ const { value: activeValue, onValueChange } = useTabsContext();
1351
+ const isActive = activeValue === value;
1352
+ return /* @__PURE__ */ jsx13(
1353
+ "button",
1354
+ {
1355
+ ref,
1356
+ type: "button",
1357
+ role: "tab",
1358
+ "aria-selected": isActive,
1359
+ tabIndex: isActive ? 0 : -1,
1360
+ onClick: () => onValueChange(value),
1361
+ className: cn(
1362
+ "relative px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:ring-offset-1",
1363
+ "after:absolute after:bottom-0 after:left-0 after:h-0.5 after:w-full after:transition-colors",
1364
+ isActive ? "text-text-primary after:bg-primary" : "text-text-secondary hover:text-text-primary after:bg-transparent",
1365
+ className
1366
+ ),
1367
+ ...props,
1368
+ children
1369
+ }
1370
+ );
1371
+ });
1372
+ var TabsContent = forwardRef13(({ value, className, children, ...props }, ref) => {
1373
+ const { value: activeValue } = useTabsContext();
1374
+ if (activeValue !== value) return null;
1375
+ return /* @__PURE__ */ jsx13(
1376
+ "div",
1377
+ {
1378
+ ref,
1379
+ role: "tabpanel",
1380
+ tabIndex: 0,
1381
+ className: cn("mt-4 focus-visible:outline-none", className),
1382
+ ...props,
1383
+ children
1384
+ }
1385
+ );
1386
+ });
1387
+ Tabs.displayName = "Tabs";
1388
+ TabsList.displayName = "TabsList";
1389
+ TabsTrigger.displayName = "TabsTrigger";
1390
+ TabsContent.displayName = "TabsContent";
1391
+
1392
+ // src/components/accordion.tsx
1393
+ import { createContext as createContext5, forwardRef as forwardRef14, useContext as useContext5, useState as useState6 } from "react";
1394
+ import { ChevronDown as ChevronDown2 } from "lucide-react";
1395
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
1396
+ var AccordionContext = createContext5(null);
1397
+ var AccordionItemContext = createContext5(null);
1398
+ function useAccordionContext() {
1399
+ const ctx = useContext5(AccordionContext);
1400
+ if (!ctx) throw new Error("Accordion \u5B50\u7EC4\u4EF6\u5FC5\u987B\u5728 <Accordion> \u5185\u4F7F\u7528");
1401
+ return ctx;
1402
+ }
1403
+ function useAccordionItemContext() {
1404
+ const ctx = useContext5(AccordionItemContext);
1405
+ if (!ctx) throw new Error("AccordionTrigger/AccordionContent \u5FC5\u987B\u5728 <AccordionItem> \u5185\u4F7F\u7528");
1406
+ return ctx;
1407
+ }
1408
+ var Accordion = forwardRef14(
1409
+ (props, ref) => {
1410
+ const { type, children, className, ...rest } = props;
1411
+ const isSingle = type === "single";
1412
+ const singleProps = isSingle ? props : null;
1413
+ const multiProps = !isSingle ? props : null;
1414
+ const [internalSingle, setInternalSingle] = useState6(singleProps?.defaultValue ?? "");
1415
+ const [internalMulti, setInternalMulti] = useState6(multiProps?.defaultValue ?? []);
1416
+ const isControlledSingle = isSingle && singleProps?.value !== void 0;
1417
+ const isControlledMulti = !isSingle && multiProps?.value !== void 0;
1418
+ const value = isSingle ? isControlledSingle ? singleProps.value ?? "" : internalSingle : isControlledMulti ? multiProps.value ?? [] : internalMulti;
1419
+ const collapsible = isSingle ? singleProps?.collapsible ?? false : false;
1420
+ function handleValueChange(itemValue) {
1421
+ if (isSingle) {
1422
+ const current = value;
1423
+ const next = current === itemValue && collapsible ? "" : itemValue;
1424
+ if (!isControlledSingle) setInternalSingle(next);
1425
+ singleProps?.onValueChange?.(next);
1426
+ } else {
1427
+ const current = value;
1428
+ const next = current.includes(itemValue) ? current.filter((v) => v !== itemValue) : [...current, itemValue];
1429
+ if (!isControlledMulti) setInternalMulti(next);
1430
+ multiProps?.onValueChange?.(next);
1431
+ }
1432
+ }
1433
+ const { defaultValue: _dv, onValueChange: _ov, collapsible: _col, ...domRest } = rest;
1434
+ void _dv;
1435
+ void _ov;
1436
+ void _col;
1437
+ return /* @__PURE__ */ jsx14(AccordionContext.Provider, { value: { type, value, onValueChange: handleValueChange, collapsible }, children: /* @__PURE__ */ jsx14("div", { ref, className: cn("w-full divide-y divide-border-base rounded-md border border-border-base", className), ...domRest, children }) });
1438
+ }
1439
+ );
1440
+ Accordion.displayName = "Accordion";
1441
+ var AccordionItem = forwardRef14(({ value, className, children, ...props }, ref) => {
1442
+ const { value: activeValue, type } = useAccordionContext();
1443
+ const isOpen = type === "single" ? activeValue === value : activeValue.includes(value);
1444
+ return /* @__PURE__ */ jsx14(AccordionItemContext.Provider, { value: { value, isOpen }, children: /* @__PURE__ */ jsx14("div", { ref, className: cn("", className), ...props, children }) });
1445
+ });
1446
+ AccordionItem.displayName = "AccordionItem";
1447
+ var AccordionTrigger = forwardRef14(({ className, children, ...props }, ref) => {
1448
+ const { onValueChange } = useAccordionContext();
1449
+ const { value, isOpen } = useAccordionItemContext();
1450
+ return /* @__PURE__ */ jsxs10(
1451
+ "button",
1452
+ {
1453
+ ref,
1454
+ type: "button",
1455
+ "aria-expanded": isOpen,
1456
+ onClick: () => onValueChange(value),
1457
+ className: cn(
1458
+ "flex w-full items-center justify-between px-4 py-3 text-left text-sm font-medium text-text-primary transition-colors hover:bg-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:ring-inset",
1459
+ className
1460
+ ),
1461
+ ...props,
1462
+ children: [
1463
+ children,
1464
+ /* @__PURE__ */ jsx14(
1465
+ ChevronDown2,
1466
+ {
1467
+ size: 16,
1468
+ className: cn("shrink-0 text-text-secondary transition-transform duration-200", isOpen && "rotate-180")
1469
+ }
1470
+ )
1471
+ ]
1472
+ }
1473
+ );
1474
+ });
1475
+ AccordionTrigger.displayName = "AccordionTrigger";
1476
+ var AccordionContent = forwardRef14(({ className, children, ...props }, ref) => {
1477
+ const { isOpen } = useAccordionItemContext();
1478
+ if (!isOpen) return null;
1479
+ return /* @__PURE__ */ jsx14(
1480
+ "div",
1481
+ {
1482
+ ref,
1483
+ className: cn("px-4 pb-4 pt-1 text-sm text-text-secondary", className),
1484
+ ...props,
1485
+ children
1486
+ }
1487
+ );
1488
+ });
1489
+ AccordionContent.displayName = "AccordionContent";
1490
+
1491
+ // src/components/steps.tsx
1492
+ import { forwardRef as forwardRef15 } from "react";
1493
+ import { Check as Check2 } from "lucide-react";
1494
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
1495
+ var Steps = forwardRef15(
1496
+ ({ current, items, direction = "horizontal", status = "process", className, ...props }, ref) => {
1497
+ const isHorizontal = direction === "horizontal";
1498
+ return /* @__PURE__ */ jsx15(
1499
+ "div",
1500
+ {
1501
+ ref,
1502
+ className: cn(
1503
+ isHorizontal ? "flex items-start" : "flex flex-col gap-1",
1504
+ className
1505
+ ),
1506
+ ...props,
1507
+ children: items.map((item, index) => {
1508
+ const isFinished = index < current || index === current && status === "finish";
1509
+ const isCurrent = index === current && status !== "finish";
1510
+ const isError = index === current && status === "error";
1511
+ const isLast = index === items.length - 1;
1512
+ return /* @__PURE__ */ jsxs11(
1513
+ "div",
1514
+ {
1515
+ className: cn(
1516
+ isHorizontal ? "flex flex-1 items-start" : "flex items-start"
1517
+ ),
1518
+ children: [
1519
+ /* @__PURE__ */ jsxs11("div", { className: cn("flex", isHorizontal ? "flex-col items-center" : "items-start"), children: [
1520
+ /* @__PURE__ */ jsx15(
1521
+ "div",
1522
+ {
1523
+ className: cn(
1524
+ "flex h-9 w-9 shrink-0 items-center justify-center rounded-full border-2 text-sm font-semibold shadow-sm transition-colors",
1525
+ isFinished && "border-primary bg-primary text-primary-fg shadow-primary/20",
1526
+ isCurrent && !isError && "border-primary bg-primary-soft text-primary-soft-fg shadow-primary/10",
1527
+ isError && "border-danger bg-danger-soft text-danger-soft-fg shadow-danger/10",
1528
+ !isFinished && !isCurrent && !isError && "border-border-strong bg-surface-1 text-text-tertiary"
1529
+ ),
1530
+ children: isFinished ? /* @__PURE__ */ jsx15(Check2, { size: 16, strokeWidth: 2.5 }) : item.icon ?? /* @__PURE__ */ jsx15("span", { children: index + 1 })
1531
+ }
1532
+ ),
1533
+ !isLast && !isHorizontal && /* @__PURE__ */ jsx15(
1534
+ "div",
1535
+ {
1536
+ className: cn(
1537
+ "ml-[17px] mt-1 w-0.5 flex-1 self-stretch rounded-full",
1538
+ isFinished ? "bg-primary" : "bg-border-strong"
1539
+ ),
1540
+ style: { minHeight: "26px" }
1541
+ }
1542
+ )
1543
+ ] }),
1544
+ /* @__PURE__ */ jsxs11(
1545
+ "div",
1546
+ {
1547
+ className: cn(
1548
+ isHorizontal ? "mt-2 text-center" : "ml-3.5 pb-5",
1549
+ isLast && !isHorizontal && "pb-0"
1550
+ ),
1551
+ children: [
1552
+ /* @__PURE__ */ jsx15(
1553
+ "p",
1554
+ {
1555
+ className: cn(
1556
+ "text-sm font-semibold leading-5",
1557
+ isFinished && "text-text-primary",
1558
+ isCurrent && !isError && "text-primary",
1559
+ isError && "text-danger",
1560
+ !isFinished && !isCurrent && !isError && "text-text-tertiary"
1561
+ ),
1562
+ children: item.title
1563
+ }
1564
+ ),
1565
+ item.description && /* @__PURE__ */ jsx15(
1566
+ "p",
1567
+ {
1568
+ className: cn(
1569
+ "mt-0.5 text-xs leading-5",
1570
+ isError ? "text-danger" : "text-text-secondary"
1571
+ ),
1572
+ children: item.description
1573
+ }
1574
+ )
1575
+ ]
1576
+ }
1577
+ ),
1578
+ !isLast && isHorizontal && /* @__PURE__ */ jsx15(
1579
+ "div",
1580
+ {
1581
+ className: cn(
1582
+ "mx-2 mt-4 h-0.5 flex-1",
1583
+ isFinished ? "bg-primary" : "bg-border-base"
1584
+ )
1585
+ }
1586
+ )
1587
+ ]
1588
+ },
1589
+ index
1590
+ );
1591
+ })
1592
+ }
1593
+ );
1594
+ }
1595
+ );
1596
+ Steps.displayName = "Steps";
1597
+
1598
+ // src/components/dropdown.tsx
1599
+ import { createContext as createContext6, forwardRef as forwardRef16, useCallback as useCallback4, useContext as useContext6, useEffect as useEffect4, useRef as useRef5, useState as useState7 } from "react";
1600
+ import { createPortal as createPortal4 } from "react-dom";
1601
+ import { jsx as jsx16 } from "react/jsx-runtime";
1602
+ var DropdownContext = createContext6(null);
1603
+ function assignRef(ref, value) {
1604
+ if (typeof ref === "function") {
1605
+ ref(value);
1606
+ return;
1607
+ }
1608
+ if (ref) {
1609
+ ref.current = value;
1610
+ }
1611
+ }
1612
+ function useDropdownContext() {
1613
+ const ctx = useContext6(DropdownContext);
1614
+ if (!ctx) throw new Error("Dropdown \u5B50\u7EC4\u4EF6\u5FC5\u987B\u5728 <DropdownMenu> \u5185\u4F7F\u7528");
1615
+ return ctx;
1616
+ }
1617
+ var DropdownMenu = forwardRef16(({ children, className }, ref) => {
1618
+ const [open, setOpen] = useState7(false);
1619
+ const triggerRef = useRef5(null);
1620
+ return /* @__PURE__ */ jsx16(DropdownContext.Provider, { value: { open, setOpen, triggerRef }, children: /* @__PURE__ */ jsx16("div", { ref, className: cn("relative inline-block", className), children }) });
1621
+ });
1622
+ var DropdownTrigger = forwardRef16(({ children, className }, ref) => {
1623
+ const { setOpen, open, triggerRef } = useDropdownContext();
1624
+ const handleRef = useCallback4(
1625
+ (node) => {
1626
+ triggerRef.current = node;
1627
+ assignRef(ref, node);
1628
+ },
1629
+ [ref, triggerRef]
1630
+ );
1631
+ return /* @__PURE__ */ jsx16(
1632
+ "div",
1633
+ {
1634
+ ref: handleRef,
1635
+ className: cn("inline-flex cursor-pointer", className),
1636
+ onClick: () => setOpen(!open),
1637
+ children
1638
+ }
1639
+ );
1640
+ });
1641
+ var DropdownContent = forwardRef16(({ children, className, align = "start" }, ref) => {
1642
+ const { open, setOpen, triggerRef } = useDropdownContext();
1643
+ const contentRef = useRef5(null);
1644
+ const portalContainer = usePortalContainer();
1645
+ const [position, setPosition] = useState7({ top: 0, left: 0 });
1646
+ const handleRef = useCallback4(
1647
+ (node) => {
1648
+ contentRef.current = node;
1649
+ assignRef(ref, node);
1650
+ },
1651
+ [ref]
1652
+ );
1653
+ const updatePosition = useCallback4(() => {
1654
+ if (!triggerRef.current) return;
1655
+ const rect = triggerRef.current.getBoundingClientRect();
1656
+ const top = rect.bottom + window.scrollY + 4;
1657
+ const left = align === "end" ? rect.right + window.scrollX - (contentRef.current?.offsetWidth ?? 0) : rect.left + window.scrollX;
1658
+ setPosition({ top, left });
1659
+ }, [align, triggerRef]);
1660
+ useEffect4(() => {
1661
+ if (!open) return;
1662
+ updatePosition();
1663
+ }, [open, updatePosition]);
1664
+ useEffect4(() => {
1665
+ if (!open) return;
1666
+ function handleClickOutside(e) {
1667
+ if (contentRef.current && !contentRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
1668
+ setOpen(false);
1669
+ }
1670
+ }
1671
+ document.addEventListener("mousedown", handleClickOutside);
1672
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1673
+ }, [open, setOpen, triggerRef]);
1674
+ if (!open || !portalContainer) return null;
1675
+ return createPortal4(
1676
+ /* @__PURE__ */ jsx16(
1677
+ "div",
1678
+ {
1679
+ ref: handleRef,
1680
+ style: { top: position.top, left: position.left },
1681
+ className: cn(
1682
+ "absolute z-50 min-w-44 overflow-hidden rounded-lg border border-border-base bg-surface-1 p-1 text-text-primary shadow-xl",
1683
+ className
1684
+ ),
1685
+ children
1686
+ }
1687
+ ),
1688
+ portalContainer
1689
+ );
1690
+ });
1691
+ var DropdownItem = forwardRef16(({ children, className, destructive = false, onClick, ...props }, ref) => {
1692
+ const { setOpen } = useDropdownContext();
1693
+ function handleClick(e) {
1694
+ setOpen(false);
1695
+ onClick?.(e);
1696
+ }
1697
+ return /* @__PURE__ */ jsx16(
1698
+ "button",
1699
+ {
1700
+ ref,
1701
+ type: "button",
1702
+ className: cn(
1703
+ "flex min-h-8 w-full items-center gap-2 rounded-md px-2.5 py-2 text-left text-sm leading-none transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50",
1704
+ destructive ? "text-danger hover:bg-danger-soft focus-visible:bg-danger-soft" : "text-text-primary hover:bg-surface-2 focus-visible:bg-surface-2",
1705
+ className
1706
+ ),
1707
+ onClick: handleClick,
1708
+ ...props,
1709
+ children
1710
+ }
1711
+ );
1712
+ });
1713
+ var DropdownSeparator = forwardRef16(({ className, ...props }, ref) => {
1714
+ return /* @__PURE__ */ jsx16("div", { ref, className: cn("-mx-1 my-1 h-px bg-border-base", className), ...props });
1715
+ });
1716
+ DropdownMenu.displayName = "DropdownMenu";
1717
+ DropdownTrigger.displayName = "DropdownTrigger";
1718
+ DropdownContent.displayName = "DropdownContent";
1719
+ DropdownItem.displayName = "DropdownItem";
1720
+ DropdownSeparator.displayName = "DropdownSeparator";
1721
+
1722
+ // src/components/popover.tsx
1723
+ import { createContext as createContext7, forwardRef as forwardRef17, useCallback as useCallback5, useContext as useContext7, useEffect as useEffect5, useRef as useRef6, useState as useState8 } from "react";
1724
+ import { createPortal as createPortal5 } from "react-dom";
1725
+ import { jsx as jsx17 } from "react/jsx-runtime";
1726
+ var PopoverContext = createContext7(null);
1727
+ function usePopoverContext() {
1728
+ const ctx = useContext7(PopoverContext);
1729
+ if (!ctx) throw new Error("Popover \u5B50\u7EC4\u4EF6\u5FC5\u987B\u5728 <Popover> \u5185\u4F7F\u7528");
1730
+ return ctx;
1731
+ }
1732
+ function assignRef2(ref, value) {
1733
+ if (typeof ref === "function") {
1734
+ ref(value);
1735
+ return;
1736
+ }
1737
+ if (ref) {
1738
+ ref.current = value;
1739
+ }
1740
+ }
1741
+ var Popover = ({ open: controlledOpen, defaultOpen = false, onOpenChange, children }) => {
1742
+ const [internalOpen, setInternalOpen] = useState8(defaultOpen);
1743
+ const triggerRef = useRef6(null);
1744
+ const isControlled = controlledOpen !== void 0;
1745
+ const open = isControlled ? controlledOpen : internalOpen;
1746
+ const setOpen = useCallback5(
1747
+ (value) => {
1748
+ if (!isControlled) setInternalOpen(value);
1749
+ onOpenChange?.(value);
1750
+ },
1751
+ [isControlled, onOpenChange]
1752
+ );
1753
+ return /* @__PURE__ */ jsx17(PopoverContext.Provider, { value: { open, setOpen, triggerRef }, children });
1754
+ };
1755
+ Popover.displayName = "Popover";
1756
+ var PopoverTrigger = forwardRef17(({ children, className }, ref) => {
1757
+ const { setOpen, open, triggerRef } = usePopoverContext();
1758
+ const handleRef = useCallback5(
1759
+ (node) => {
1760
+ triggerRef.current = node;
1761
+ assignRef2(ref, node);
1762
+ },
1763
+ [ref, triggerRef]
1764
+ );
1765
+ return /* @__PURE__ */ jsx17(
1766
+ "div",
1767
+ {
1768
+ ref: handleRef,
1769
+ className: cn("inline-flex", className),
1770
+ onClick: () => setOpen(!open),
1771
+ children
1772
+ }
1773
+ );
1774
+ });
1775
+ PopoverTrigger.displayName = "PopoverTrigger";
1776
+ function calcPosition(triggerRect, contentEl, side, align, sideOffset) {
1777
+ const cw = contentEl.offsetWidth;
1778
+ const ch = contentEl.offsetHeight;
1779
+ let top = 0;
1780
+ let left = 0;
1781
+ if (side === "bottom") {
1782
+ top = triggerRect.bottom + window.scrollY + sideOffset;
1783
+ } else if (side === "top") {
1784
+ top = triggerRect.top + window.scrollY - ch - sideOffset;
1785
+ } else if (side === "left") {
1786
+ top = triggerRect.top + window.scrollY + triggerRect.height / 2 - ch / 2;
1787
+ left = triggerRect.left + window.scrollX - cw - sideOffset;
1788
+ return { top, left };
1789
+ } else {
1790
+ top = triggerRect.top + window.scrollY + triggerRect.height / 2 - ch / 2;
1791
+ left = triggerRect.right + window.scrollX + sideOffset;
1792
+ return { top, left };
1793
+ }
1794
+ if (align === "start") {
1795
+ left = triggerRect.left + window.scrollX;
1796
+ } else if (align === "end") {
1797
+ left = triggerRect.right + window.scrollX - cw;
1798
+ } else {
1799
+ left = triggerRect.left + window.scrollX + triggerRect.width / 2 - cw / 2;
1800
+ }
1801
+ return { top, left };
1802
+ }
1803
+ var PopoverContent = forwardRef17(
1804
+ ({ children, className, side = "bottom", align = "center", sideOffset = 4, ...props }, ref) => {
1805
+ const { open, setOpen, triggerRef } = usePopoverContext();
1806
+ const contentRef = useRef6(null);
1807
+ const portalContainer = usePortalContainer();
1808
+ const [pos, setPos] = useState8({ top: 0, left: 0 });
1809
+ const handleRef = useCallback5(
1810
+ (node) => {
1811
+ contentRef.current = node;
1812
+ assignRef2(ref, node);
1813
+ },
1814
+ [ref]
1815
+ );
1816
+ const updatePos = useCallback5(() => {
1817
+ if (!triggerRef.current || !contentRef.current) return;
1818
+ const rect = triggerRef.current.getBoundingClientRect();
1819
+ setPos(calcPosition(rect, contentRef.current, side, align, sideOffset));
1820
+ }, [side, align, sideOffset, triggerRef]);
1821
+ useEffect5(() => {
1822
+ if (open) updatePos();
1823
+ }, [open, updatePos]);
1824
+ useEffect5(() => {
1825
+ if (!open) return;
1826
+ function handleMouseDown(e) {
1827
+ if (contentRef.current && !contentRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
1828
+ setOpen(false);
1829
+ }
1830
+ }
1831
+ document.addEventListener("mousedown", handleMouseDown);
1832
+ return () => document.removeEventListener("mousedown", handleMouseDown);
1833
+ }, [open, setOpen, triggerRef]);
1834
+ if (!open || !portalContainer) return null;
1835
+ return createPortal5(
1836
+ /* @__PURE__ */ jsx17(
1837
+ "div",
1838
+ {
1839
+ ref: handleRef,
1840
+ style: { top: pos.top, left: pos.left },
1841
+ className: cn(
1842
+ "absolute z-50 min-w-40 rounded-md border border-border-base bg-surface-1 p-3 shadow-lg",
1843
+ className
1844
+ ),
1845
+ ...props,
1846
+ children
1847
+ }
1848
+ ),
1849
+ portalContainer
1850
+ );
1851
+ }
1852
+ );
1853
+ PopoverContent.displayName = "PopoverContent";
1854
+
1855
+ // src/components/context-menu.tsx
1856
+ import { createContext as createContext8, forwardRef as forwardRef18, useCallback as useCallback6, useContext as useContext8, useEffect as useEffect6, useRef as useRef7, useState as useState9 } from "react";
1857
+ import { createPortal as createPortal6 } from "react-dom";
1858
+ import { jsx as jsx18 } from "react/jsx-runtime";
1859
+ var ContextMenuContext = createContext8(null);
1860
+ function useContextMenuContext() {
1861
+ const ctx = useContext8(ContextMenuContext);
1862
+ if (!ctx) throw new Error("ContextMenu \u5B50\u7EC4\u4EF6\u5FC5\u987B\u5728 <ContextMenu> \u5185\u4F7F\u7528");
1863
+ return ctx;
1864
+ }
1865
+ function assignRef3(ref, value) {
1866
+ if (typeof ref === "function") {
1867
+ ref(value);
1868
+ return;
1869
+ }
1870
+ if (ref) {
1871
+ ref.current = value;
1872
+ }
1873
+ }
1874
+ var ContextMenu = forwardRef18(
1875
+ ({ children, className }, ref) => {
1876
+ const [open, setOpen] = useState9(false);
1877
+ const [position, setPosition] = useState9({ x: 0, y: 0 });
1878
+ const openAt = useCallback6((pos) => {
1879
+ setPosition(pos);
1880
+ setOpen(true);
1881
+ }, []);
1882
+ return /* @__PURE__ */ jsx18(ContextMenuContext.Provider, { value: { open, position, setOpen, openAt }, children: /* @__PURE__ */ jsx18("div", { ref, className: cn("relative", className), children }) });
1883
+ }
1884
+ );
1885
+ var ContextMenuTrigger = forwardRef18(
1886
+ ({ children, className, ...props }, ref) => {
1887
+ const { openAt } = useContextMenuContext();
1888
+ function handleContextMenu(e) {
1889
+ e.preventDefault();
1890
+ openAt({ x: e.clientX, y: e.clientY });
1891
+ }
1892
+ return /* @__PURE__ */ jsx18(
1893
+ "div",
1894
+ {
1895
+ ref,
1896
+ className: cn(className),
1897
+ onContextMenu: handleContextMenu,
1898
+ ...props,
1899
+ children
1900
+ }
1901
+ );
1902
+ }
1903
+ );
1904
+ var ContextMenuContent = forwardRef18(
1905
+ ({ children, className, ...props }, ref) => {
1906
+ const { open, position, setOpen } = useContextMenuContext();
1907
+ const contentRef = useRef7(null);
1908
+ const portalContainer = usePortalContainer();
1909
+ const handleRef = useCallback6(
1910
+ (node) => {
1911
+ contentRef.current = node;
1912
+ assignRef3(ref, node);
1913
+ },
1914
+ [ref]
1915
+ );
1916
+ useEffect6(() => {
1917
+ if (!open) return;
1918
+ function handleMouseDown(e) {
1919
+ if (contentRef.current && !contentRef.current.contains(e.target)) {
1920
+ setOpen(false);
1921
+ }
1922
+ }
1923
+ function handleKeyDown(e) {
1924
+ if (e.key === "Escape") {
1925
+ setOpen(false);
1926
+ }
1927
+ }
1928
+ document.addEventListener("mousedown", handleMouseDown);
1929
+ document.addEventListener("keydown", handleKeyDown);
1930
+ return () => {
1931
+ document.removeEventListener("mousedown", handleMouseDown);
1932
+ document.removeEventListener("keydown", handleKeyDown);
1933
+ };
1934
+ }, [open, setOpen]);
1935
+ if (!open || !portalContainer) return null;
1936
+ return createPortal6(
1937
+ /* @__PURE__ */ jsx18(
1938
+ "div",
1939
+ {
1940
+ ref: handleRef,
1941
+ style: { top: position.y, left: position.x },
1942
+ className: cn(
1943
+ "fixed z-50 min-w-40 rounded-md border border-border-base bg-surface-1 py-1 shadow-lg",
1944
+ className
1945
+ ),
1946
+ ...props,
1947
+ children
1948
+ }
1949
+ ),
1950
+ portalContainer
1951
+ );
1952
+ }
1953
+ );
1954
+ var ContextMenuItem = forwardRef18(
1955
+ ({ children, className, destructive = false, onClick, ...props }, ref) => {
1956
+ const { setOpen } = useContextMenuContext();
1957
+ function handleClick(e) {
1958
+ setOpen(false);
1959
+ onClick?.(e);
1960
+ }
1961
+ return /* @__PURE__ */ jsx18(
1962
+ "button",
1963
+ {
1964
+ ref,
1965
+ type: "button",
1966
+ className: cn(
1967
+ "w-full px-3 py-1.5 text-left text-sm transition-colors focus-visible:outline-none focus-visible:bg-surface-3",
1968
+ destructive ? "text-danger hover:bg-danger-soft/10" : "text-text-primary hover:bg-surface-3",
1969
+ className
1970
+ ),
1971
+ onClick: handleClick,
1972
+ ...props,
1973
+ children
1974
+ }
1975
+ );
1976
+ }
1977
+ );
1978
+ var ContextMenuSeparator = forwardRef18(
1979
+ ({ className, ...props }, ref) => {
1980
+ return /* @__PURE__ */ jsx18("div", { ref, className: cn("my-1 h-px bg-border-base", className), ...props });
1981
+ }
1982
+ );
1983
+ ContextMenu.displayName = "ContextMenu";
1984
+ ContextMenuTrigger.displayName = "ContextMenuTrigger";
1985
+ ContextMenuContent.displayName = "ContextMenuContent";
1986
+ ContextMenuItem.displayName = "ContextMenuItem";
1987
+ ContextMenuSeparator.displayName = "ContextMenuSeparator";
1988
+
1989
+ // src/components/navigation-menu.tsx
1990
+ import { createContext as createContext9, forwardRef as forwardRef19, useCallback as useCallback7, useContext as useContext9, useRef as useRef8, useState as useState10 } from "react";
1991
+ import { ChevronDown as ChevronDown3 } from "lucide-react";
1992
+ import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
1993
+ var NavigationMenuContext = createContext9(null);
1994
+ function useNavigationMenuContext() {
1995
+ const ctx = useContext9(NavigationMenuContext);
1996
+ if (!ctx) throw new Error("NavigationMenu \u5B50\u7EC4\u4EF6\u5FC5\u987B\u5728 <NavigationMenu> \u5185\u4F7F\u7528");
1997
+ return ctx;
1998
+ }
1999
+ var NavigationMenuItemContext = createContext9(null);
2000
+ function useNavigationMenuItemContext() {
2001
+ const ctx = useContext9(NavigationMenuItemContext);
2002
+ if (!ctx) throw new Error("NavigationMenuTrigger/Content \u5FC5\u987B\u5728 <NavigationMenuItem> \u5185\u4F7F\u7528");
2003
+ return ctx;
2004
+ }
2005
+ var _idCounter = 0;
2006
+ var NavigationMenu = forwardRef19(
2007
+ ({ className, children, ...props }, ref) => {
2008
+ const [activeItem, setActiveItem] = useState10(null);
2009
+ return /* @__PURE__ */ jsx19(NavigationMenuContext.Provider, { value: { activeItem, setActiveItem }, children: /* @__PURE__ */ jsx19(
2010
+ "nav",
2011
+ {
2012
+ ref,
2013
+ className: cn("relative flex items-center gap-1", className),
2014
+ ...props,
2015
+ children
2016
+ }
2017
+ ) });
2018
+ }
2019
+ );
2020
+ var NavigationMenuItem = forwardRef19(
2021
+ ({ className, children, ...props }, ref) => {
2022
+ const itemIdRef = useRef8(`nav-item-${++_idCounter}`);
2023
+ const { activeItem, setActiveItem } = useNavigationMenuContext();
2024
+ const isOpen = activeItem === itemIdRef.current;
2025
+ const handleMouseEnter = useCallback7(() => {
2026
+ setActiveItem(itemIdRef.current);
2027
+ }, [setActiveItem]);
2028
+ const handleMouseLeave = useCallback7(() => {
2029
+ setActiveItem(null);
2030
+ }, [setActiveItem]);
2031
+ return /* @__PURE__ */ jsx19(NavigationMenuItemContext.Provider, { value: { itemId: itemIdRef.current }, children: /* @__PURE__ */ jsx19(
2032
+ "div",
2033
+ {
2034
+ ref,
2035
+ className: cn("relative", className),
2036
+ onMouseEnter: handleMouseEnter,
2037
+ onMouseLeave: handleMouseLeave,
2038
+ "data-state": isOpen ? "open" : "closed",
2039
+ ...props,
2040
+ children
2041
+ }
2042
+ ) });
2043
+ }
2044
+ );
2045
+ var NavigationMenuLink = forwardRef19(
2046
+ ({ className, active = false, children, ...props }, ref) => {
2047
+ return /* @__PURE__ */ jsx19(
2048
+ "a",
2049
+ {
2050
+ ref,
2051
+ className: cn(
2052
+ "inline-flex items-center rounded-md px-3 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:ring-offset-1",
2053
+ active ? "bg-surface-3 text-text-primary" : "text-text-secondary hover:bg-surface-3 hover:text-text-primary",
2054
+ className
2055
+ ),
2056
+ ...props,
2057
+ children
2058
+ }
2059
+ );
2060
+ }
2061
+ );
2062
+ var NavigationMenuTrigger = forwardRef19(
2063
+ ({ className, children, ...props }, ref) => {
2064
+ const { activeItem } = useNavigationMenuContext();
2065
+ const { itemId } = useNavigationMenuItemContext();
2066
+ const isOpen = activeItem === itemId;
2067
+ return /* @__PURE__ */ jsxs12(
2068
+ "button",
2069
+ {
2070
+ ref,
2071
+ type: "button",
2072
+ "aria-expanded": isOpen,
2073
+ className: cn(
2074
+ "inline-flex items-center gap-1 rounded-md px-3 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:ring-offset-1",
2075
+ isOpen ? "bg-surface-3 text-text-primary" : "text-text-secondary hover:bg-surface-3 hover:text-text-primary",
2076
+ className
2077
+ ),
2078
+ ...props,
2079
+ children: [
2080
+ children,
2081
+ /* @__PURE__ */ jsx19(
2082
+ ChevronDown3,
2083
+ {
2084
+ size: 14,
2085
+ className: cn("transition-transform duration-200", isOpen && "rotate-180")
2086
+ }
2087
+ )
2088
+ ]
2089
+ }
2090
+ );
2091
+ }
2092
+ );
2093
+ var NavigationMenuContent = forwardRef19(
2094
+ ({ className, children, ...props }, ref) => {
2095
+ const { activeItem } = useNavigationMenuContext();
2096
+ const { itemId } = useNavigationMenuItemContext();
2097
+ const isOpen = activeItem === itemId;
2098
+ if (!isOpen) return null;
2099
+ return /* @__PURE__ */ jsx19(
2100
+ "div",
2101
+ {
2102
+ ref,
2103
+ className: cn(
2104
+ "absolute left-0 top-full z-50 mt-1 min-w-40 rounded-md border border-border-base bg-surface-1 py-1 shadow-lg",
2105
+ className
2106
+ ),
2107
+ ...props,
2108
+ children
2109
+ }
2110
+ );
2111
+ }
2112
+ );
2113
+ NavigationMenu.displayName = "NavigationMenu";
2114
+ NavigationMenuItem.displayName = "NavigationMenuItem";
2115
+ NavigationMenuLink.displayName = "NavigationMenuLink";
2116
+ NavigationMenuTrigger.displayName = "NavigationMenuTrigger";
2117
+ NavigationMenuContent.displayName = "NavigationMenuContent";
2118
+
2119
+ // src/components/avatar.tsx
2120
+ import {
2121
+ createContext as createContext10,
2122
+ forwardRef as forwardRef20,
2123
+ useContext as useContext10,
2124
+ useEffect as useEffect7,
2125
+ useState as useState11
2126
+ } from "react";
2127
+ import { User } from "lucide-react";
2128
+ import { cva as cva4 } from "class-variance-authority";
2129
+ import { jsx as jsx20 } from "react/jsx-runtime";
2130
+ var AvatarContext = createContext10(null);
2131
+ function useAvatarContext(componentName) {
2132
+ const ctx = useContext10(AvatarContext);
2133
+ if (!ctx) {
2134
+ throw new Error(`<${componentName}> \u5FC5\u987B\u5728 <Avatar> \u5185\u4F7F\u7528`);
2135
+ }
2136
+ return ctx;
2137
+ }
2138
+ var avatarVariants = cva4(
2139
+ "relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-surface-3",
2140
+ {
2141
+ variants: {
2142
+ size: {
2143
+ sm: "h-8 w-8 text-xs",
2144
+ md: "h-10 w-10 text-sm",
2145
+ lg: "h-14 w-14 text-base",
2146
+ xl: "h-20 w-20 text-xl"
2147
+ }
2148
+ },
2149
+ defaultVariants: { size: "md" }
2150
+ }
2151
+ );
2152
+ var Avatar = forwardRef20(
2153
+ ({ size, className, children, ...props }, ref) => {
2154
+ const [imageLoadingStatus, setImageLoadingStatus] = useState11("idle");
2155
+ return /* @__PURE__ */ jsx20(AvatarContext.Provider, { value: { imageLoadingStatus, setImageLoadingStatus }, children: /* @__PURE__ */ jsx20("span", { ref, className: cn(avatarVariants({ size }), className), ...props, children }) });
2156
+ }
2157
+ );
2158
+ var AvatarImage = forwardRef20(
2159
+ ({ src, alt = "", className, onLoad, onError, ...props }, ref) => {
2160
+ const { imageLoadingStatus, setImageLoadingStatus } = useAvatarContext("AvatarImage");
2161
+ useEffect7(() => {
2162
+ if (!src) {
2163
+ setImageLoadingStatus("error");
2164
+ return;
2165
+ }
2166
+ setImageLoadingStatus("loading");
2167
+ }, [src, setImageLoadingStatus]);
2168
+ if (imageLoadingStatus !== "loaded" && imageLoadingStatus !== "loading") {
2169
+ return null;
2170
+ }
2171
+ return /* @__PURE__ */ jsx20(
2172
+ "img",
2173
+ {
2174
+ ref,
2175
+ src,
2176
+ alt,
2177
+ onLoad: (event) => {
2178
+ setImageLoadingStatus("loaded");
2179
+ onLoad?.(event);
2180
+ },
2181
+ onError: (event) => {
2182
+ setImageLoadingStatus("error");
2183
+ onError?.(event);
2184
+ },
2185
+ className: cn(
2186
+ "h-full w-full object-cover",
2187
+ imageLoadingStatus === "loading" && "invisible",
2188
+ className
2189
+ ),
2190
+ ...props
2191
+ }
2192
+ );
2193
+ }
2194
+ );
2195
+ var AvatarFallback = forwardRef20(
2196
+ ({ className, children, ...props }, ref) => {
2197
+ const { imageLoadingStatus } = useAvatarContext("AvatarFallback");
2198
+ if (imageLoadingStatus === "loaded") return null;
2199
+ return /* @__PURE__ */ jsx20(
2200
+ "span",
2201
+ {
2202
+ ref,
2203
+ className: cn(
2204
+ "absolute inset-0 flex items-center justify-center font-medium text-text-secondary",
2205
+ className
2206
+ ),
2207
+ ...props,
2208
+ children: children ?? /* @__PURE__ */ jsx20(User, { size: 16 })
2209
+ }
2210
+ );
2211
+ }
2212
+ );
2213
+ Avatar.displayName = "Avatar";
2214
+ AvatarImage.displayName = "AvatarImage";
2215
+ AvatarFallback.displayName = "AvatarFallback";
2216
+
2217
+ // src/components/avatar-group.tsx
2218
+ import { forwardRef as forwardRef21, Children, isValidElement } from "react";
2219
+ import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
2220
+ var SIZE_CLASSES = {
2221
+ sm: "h-8 w-8 text-xs",
2222
+ default: "h-10 w-10 text-sm",
2223
+ lg: "h-14 w-14 text-base"
2224
+ };
2225
+ var RING_SIZE_CLASSES = {
2226
+ sm: "ring-2",
2227
+ default: "ring-2",
2228
+ lg: "ring-[3px]"
2229
+ };
2230
+ var OVERLAP_CLASSES = {
2231
+ sm: "-ml-1.5",
2232
+ default: "-ml-2",
2233
+ lg: "-ml-3"
2234
+ };
2235
+ var AvatarGroup = forwardRef21(
2236
+ ({ max = 5, size = "default", className, children, ...props }, ref) => {
2237
+ const allItems = Children.toArray(children).filter(isValidElement);
2238
+ const visibleItems = allItems.slice(0, max);
2239
+ const overflowCount = allItems.length - max;
2240
+ return /* @__PURE__ */ jsxs13(
2241
+ "div",
2242
+ {
2243
+ ref,
2244
+ className: cn("flex max-w-full items-center overflow-hidden py-0.5", className),
2245
+ ...props,
2246
+ children: [
2247
+ visibleItems.map((child, idx) => /* @__PURE__ */ jsx21(
2248
+ "span",
2249
+ {
2250
+ className: cn(
2251
+ "relative inline-flex shrink-0 overflow-hidden rounded-full border border-border-base bg-surface-2 ring-surface-1",
2252
+ SIZE_CLASSES[size],
2253
+ RING_SIZE_CLASSES[size],
2254
+ idx > 0 && OVERLAP_CLASSES[size]
2255
+ ),
2256
+ children: child
2257
+ },
2258
+ idx
2259
+ )),
2260
+ overflowCount > 0 && /* @__PURE__ */ jsxs13(
2261
+ "span",
2262
+ {
2263
+ className: cn(
2264
+ "relative inline-flex shrink-0 items-center justify-center rounded-full border border-border-base bg-surface-2 font-medium text-text-secondary ring-surface-1",
2265
+ SIZE_CLASSES[size],
2266
+ RING_SIZE_CLASSES[size],
2267
+ OVERLAP_CLASSES[size]
2268
+ ),
2269
+ "aria-label": `\u8FD8\u6709 ${overflowCount} \u4EBA`,
2270
+ children: [
2271
+ "+",
2272
+ overflowCount
2273
+ ]
2274
+ }
2275
+ )
2276
+ ]
2277
+ }
2278
+ );
2279
+ }
2280
+ );
2281
+ AvatarGroup.displayName = "AvatarGroup";
2282
+
2283
+ // src/components/tooltip.tsx
2284
+ import { createContext as createContext11, forwardRef as forwardRef22, useCallback as useCallback8, useContext as useContext11, useEffect as useEffect8, useRef as useRef9, useState as useState12 } from "react";
2285
+ import { createPortal as createPortal7 } from "react-dom";
2286
+ import { jsx as jsx22 } from "react/jsx-runtime";
2287
+ var TooltipContext = createContext11(null);
2288
+ function assignRef4(ref, value) {
2289
+ if (typeof ref === "function") {
2290
+ ref(value);
2291
+ return;
2292
+ }
2293
+ if (ref) {
2294
+ ref.current = value;
2295
+ }
2296
+ }
2297
+ function useTooltipContext() {
2298
+ const ctx = useContext11(TooltipContext);
2299
+ if (!ctx) throw new Error("Tooltip \u5B50\u7EC4\u4EF6\u5FC5\u987B\u5728 <Tooltip> \u5185\u4F7F\u7528");
2300
+ return ctx;
2301
+ }
2302
+ var Tooltip = forwardRef22(({ children, className }, ref) => {
2303
+ const [open, setOpen] = useState12(false);
2304
+ const triggerRef = useRef9(null);
2305
+ const delayRef = useRef9(null);
2306
+ useEffect8(() => {
2307
+ return () => {
2308
+ if (delayRef.current) {
2309
+ clearTimeout(delayRef.current);
2310
+ }
2311
+ };
2312
+ }, []);
2313
+ return /* @__PURE__ */ jsx22(TooltipContext.Provider, { value: { open, setOpen, triggerRef, delayRef }, children: /* @__PURE__ */ jsx22("span", { ref, className: cn("relative inline-flex", className), children }) });
2314
+ });
2315
+ var TooltipTrigger = forwardRef22(({ children, className }, ref) => {
2316
+ const { setOpen, triggerRef, delayRef } = useTooltipContext();
2317
+ const handleRef = useCallback8(
2318
+ (node) => {
2319
+ triggerRef.current = node;
2320
+ assignRef4(ref, node);
2321
+ },
2322
+ [ref, triggerRef]
2323
+ );
2324
+ function handleMouseEnter() {
2325
+ delayRef.current = setTimeout(() => setOpen(true), 200);
2326
+ }
2327
+ function handleMouseLeave() {
2328
+ if (delayRef.current) clearTimeout(delayRef.current);
2329
+ setOpen(false);
2330
+ }
2331
+ return /* @__PURE__ */ jsx22(
2332
+ "span",
2333
+ {
2334
+ ref: handleRef,
2335
+ className: cn("inline-flex", className),
2336
+ onMouseEnter: handleMouseEnter,
2337
+ onMouseLeave: handleMouseLeave,
2338
+ children
2339
+ }
2340
+ );
2341
+ });
2342
+ var arrowPositionClasses = {
2343
+ top: "after:absolute after:top-full after:left-1/2 after:-translate-x-1/2 after:border-4 after:border-transparent after:border-t-text-primary",
2344
+ bottom: "after:absolute after:bottom-full after:left-1/2 after:-translate-x-1/2 after:border-4 after:border-transparent after:border-b-text-primary",
2345
+ left: "after:absolute after:left-full after:top-1/2 after:-translate-y-1/2 after:border-4 after:border-transparent after:border-l-text-primary",
2346
+ right: "after:absolute after:right-full after:top-1/2 after:-translate-y-1/2 after:border-4 after:border-transparent after:border-r-text-primary"
2347
+ };
2348
+ var TooltipContent = forwardRef22(({ children, side = "top", className }, ref) => {
2349
+ const { open, triggerRef } = useTooltipContext();
2350
+ const contentRef = useRef9(null);
2351
+ const portalContainer = usePortalContainer();
2352
+ const [pos, setPos] = useState12({ top: 0, left: 0 });
2353
+ const handleRef = useCallback8(
2354
+ (node) => {
2355
+ contentRef.current = node;
2356
+ assignRef4(ref, node);
2357
+ },
2358
+ [ref]
2359
+ );
2360
+ const updatePos = useCallback8(() => {
2361
+ if (!triggerRef.current || !contentRef.current) return;
2362
+ const rect = triggerRef.current.getBoundingClientRect();
2363
+ const cw = contentRef.current.offsetWidth;
2364
+ const ch = contentRef.current.offsetHeight;
2365
+ const gap = 8;
2366
+ let top = 0;
2367
+ let left = 0;
2368
+ if (side === "top") {
2369
+ top = rect.top + window.scrollY - ch - gap;
2370
+ left = rect.left + window.scrollX + rect.width / 2 - cw / 2;
2371
+ } else if (side === "bottom") {
2372
+ top = rect.bottom + window.scrollY + gap;
2373
+ left = rect.left + window.scrollX + rect.width / 2 - cw / 2;
2374
+ } else if (side === "left") {
2375
+ top = rect.top + window.scrollY + rect.height / 2 - ch / 2;
2376
+ left = rect.left + window.scrollX - cw - gap;
2377
+ } else {
2378
+ top = rect.top + window.scrollY + rect.height / 2 - ch / 2;
2379
+ left = rect.right + window.scrollX + gap;
2380
+ }
2381
+ setPos({ top, left });
2382
+ }, [side, triggerRef]);
2383
+ useEffect8(() => {
2384
+ if (open) updatePos();
2385
+ }, [open, updatePos]);
2386
+ if (!open || !portalContainer) return null;
2387
+ return createPortal7(
2388
+ /* @__PURE__ */ jsx22(
2389
+ "div",
2390
+ {
2391
+ ref: handleRef,
2392
+ style: { top: pos.top, left: pos.left },
2393
+ className: cn(
2394
+ "absolute z-50 rounded-md bg-text-primary px-2.5 py-1.5 text-xs text-surface-1 shadow-md",
2395
+ arrowPositionClasses[side],
2396
+ className
2397
+ ),
2398
+ children
2399
+ }
2400
+ ),
2401
+ portalContainer
2402
+ );
2403
+ });
2404
+ Tooltip.displayName = "Tooltip";
2405
+ TooltipTrigger.displayName = "TooltipTrigger";
2406
+ TooltipContent.displayName = "TooltipContent";
2407
+
2408
+ // src/components/alert.tsx
2409
+ import { forwardRef as forwardRef23 } from "react";
2410
+ import { Info, CheckCircle, AlertTriangle, AlertCircle } from "lucide-react";
2411
+ import { cva as cva5 } from "class-variance-authority";
2412
+ import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
2413
+ var alertVariants = cva5(
2414
+ "relative flex gap-3 rounded-md border-l-4 p-4",
2415
+ {
2416
+ variants: {
2417
+ variant: {
2418
+ default: "border-l-primary bg-info-soft text-info-soft-fg",
2419
+ info: "border-l-info bg-info-soft text-info-soft-fg",
2420
+ success: "border-l-success bg-success-soft text-success-soft-fg",
2421
+ warning: "border-l-warning bg-warning-soft text-warning-soft-fg",
2422
+ destructive: "border-l-danger bg-danger-soft text-danger-soft-fg"
2423
+ }
2424
+ },
2425
+ defaultVariants: { variant: "default" }
2426
+ }
2427
+ );
2428
+ var iconMap = {
2429
+ default: AlertCircle,
2430
+ info: Info,
2431
+ success: CheckCircle,
2432
+ warning: AlertTriangle,
2433
+ destructive: AlertCircle
2434
+ };
2435
+ var iconColorMap = {
2436
+ default: "text-info",
2437
+ info: "text-info",
2438
+ success: "text-success",
2439
+ warning: "text-warning",
2440
+ destructive: "text-danger"
2441
+ };
2442
+ var Alert = forwardRef23(
2443
+ ({ variant = "default", className, children, ...props }, ref) => {
2444
+ const Icon = iconMap[variant ?? "default"];
2445
+ const iconColor = iconColorMap[variant ?? "default"];
2446
+ return /* @__PURE__ */ jsxs14(
2447
+ "div",
2448
+ {
2449
+ ref,
2450
+ role: "alert",
2451
+ className: cn(alertVariants({ variant }), className),
2452
+ ...props,
2453
+ children: [
2454
+ /* @__PURE__ */ jsx23(Icon, { size: 18, className: cn("mt-0.5 shrink-0", iconColor) }),
2455
+ /* @__PURE__ */ jsx23("div", { className: "flex flex-col gap-0.5", children })
2456
+ ]
2457
+ }
2458
+ );
2459
+ }
2460
+ );
2461
+ var AlertTitle = forwardRef23(({ className, ...props }, ref) => {
2462
+ return /* @__PURE__ */ jsx23("p", { ref, className: cn("text-sm font-semibold leading-5", className), ...props });
2463
+ });
2464
+ var AlertDescription = forwardRef23(({ className, ...props }, ref) => {
2465
+ return /* @__PURE__ */ jsx23("p", { ref, className: cn("text-sm leading-5 opacity-90", className), ...props });
2466
+ });
2467
+ Alert.displayName = "Alert";
2468
+ AlertTitle.displayName = "AlertTitle";
2469
+ AlertDescription.displayName = "AlertDescription";
2470
+
2471
+ // src/components/breadcrumb.tsx
2472
+ import { forwardRef as forwardRef24 } from "react";
2473
+ import { ChevronRight as ChevronRight2 } from "lucide-react";
2474
+ import { jsx as jsx24 } from "react/jsx-runtime";
2475
+ var Breadcrumb = forwardRef24(({ className, children, ...props }, ref) => {
2476
+ return /* @__PURE__ */ jsx24("nav", { ref, "aria-label": "\u9762\u5305\u5C51\u5BFC\u822A", className: cn(className), ...props, children: /* @__PURE__ */ jsx24("ol", { className: "flex flex-wrap items-center gap-0 text-sm text-text-secondary", children }) });
2477
+ });
2478
+ var BreadcrumbItem = forwardRef24(({ className, ...props }, ref) => {
2479
+ return /* @__PURE__ */ jsx24("li", { ref, className: cn("flex items-center gap-1", className), ...props });
2480
+ });
2481
+ var BreadcrumbLink = forwardRef24(
2482
+ ({ current = false, className, children, href, ...props }, ref) => {
2483
+ if (current) {
2484
+ return /* @__PURE__ */ jsx24("span", { "aria-current": "page", className: cn("font-medium text-text-primary", className), children });
2485
+ }
2486
+ return /* @__PURE__ */ jsx24(
2487
+ "a",
2488
+ {
2489
+ ref,
2490
+ href,
2491
+ className: cn("transition-colors hover:text-text-primary", className),
2492
+ ...props,
2493
+ children
2494
+ }
2495
+ );
2496
+ }
2497
+ );
2498
+ var BreadcrumbSeparator = forwardRef24(
2499
+ ({ className, children, ...props }, ref) => {
2500
+ return /* @__PURE__ */ jsx24(
2501
+ "li",
2502
+ {
2503
+ ref,
2504
+ role: "presentation",
2505
+ "aria-hidden": "true",
2506
+ className: cn("text-text-tertiary", className),
2507
+ ...props,
2508
+ children: children ?? /* @__PURE__ */ jsx24(ChevronRight2, { size: 14 })
2509
+ }
2510
+ );
2511
+ }
2512
+ );
2513
+ Breadcrumb.displayName = "Breadcrumb";
2514
+ BreadcrumbItem.displayName = "BreadcrumbItem";
2515
+ BreadcrumbLink.displayName = "BreadcrumbLink";
2516
+ BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
2517
+
2518
+ // src/components/switch.tsx
2519
+ import { forwardRef as forwardRef25, useId as useId5 } from "react";
2520
+ import { jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
2521
+ var Switch = forwardRef25(
2522
+ ({
2523
+ checked,
2524
+ onCheckedChange,
2525
+ disabled = false,
2526
+ label,
2527
+ labelPlacement = "right",
2528
+ className,
2529
+ id,
2530
+ ...props
2531
+ }, ref) => {
2532
+ const generatedId = useId5();
2533
+ const switchId = id ?? generatedId;
2534
+ const track = /* @__PURE__ */ jsx25(
2535
+ "button",
2536
+ {
2537
+ ref,
2538
+ id: switchId,
2539
+ type: "button",
2540
+ role: "switch",
2541
+ "aria-checked": checked,
2542
+ disabled,
2543
+ onClick: () => !disabled && onCheckedChange(!checked),
2544
+ className: cn(
2545
+ "relative inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-transparent p-0.5 shadow-sm transition-colors duration-200",
2546
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/35 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0",
2547
+ "disabled:cursor-not-allowed disabled:opacity-50",
2548
+ checked ? "bg-primary hover:bg-primary-hover" : "bg-surface-3 hover:bg-border-strong",
2549
+ className
2550
+ ),
2551
+ ...props,
2552
+ children: /* @__PURE__ */ jsx25(
2553
+ "span",
2554
+ {
2555
+ className: cn(
2556
+ "pointer-events-none block h-5 w-5 rounded-full bg-primary-fg shadow-md ring-0 transition-transform duration-200",
2557
+ checked ? "translate-x-5" : "translate-x-0"
2558
+ )
2559
+ }
2560
+ )
2561
+ }
2562
+ );
2563
+ if (!label) return track;
2564
+ return /* @__PURE__ */ jsxs15(
2565
+ "label",
2566
+ {
2567
+ htmlFor: switchId,
2568
+ className: cn(
2569
+ "inline-flex cursor-pointer items-center gap-2.5 text-sm font-medium leading-5 text-text-primary",
2570
+ disabled && "cursor-not-allowed opacity-50"
2571
+ ),
2572
+ children: [
2573
+ labelPlacement === "left" && /* @__PURE__ */ jsx25("span", { children: label }),
2574
+ track,
2575
+ labelPlacement === "right" && /* @__PURE__ */ jsx25("span", { children: label })
2576
+ ]
2577
+ }
2578
+ );
2579
+ }
2580
+ );
2581
+ Switch.displayName = "Switch";
2582
+
2583
+ // src/components/textarea.tsx
2584
+ import { forwardRef as forwardRef26, useId as useId6 } from "react";
2585
+ import { jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
2586
+ var Textarea = forwardRef26(
2587
+ ({ className, label, error, helperText, id, rows = 3, ...props }, ref) => {
2588
+ const generatedId = useId6();
2589
+ const textareaId = id ?? generatedId;
2590
+ return /* @__PURE__ */ jsxs16("div", { className: "flex flex-col gap-1", children: [
2591
+ label && /* @__PURE__ */ jsx26("label", { htmlFor: textareaId, className: "text-sm font-medium text-text-primary", children: label }),
2592
+ /* @__PURE__ */ jsx26(
2593
+ "textarea",
2594
+ {
2595
+ ref,
2596
+ id: textareaId,
2597
+ rows,
2598
+ className: cn(
2599
+ "w-full rounded-md border bg-surface-1 px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary",
2600
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1",
2601
+ "disabled:cursor-not-allowed disabled:opacity-50",
2602
+ "resize-y",
2603
+ error ? "border-danger focus-visible:ring-danger" : "border-border-strong focus-visible:ring-ring/30",
2604
+ className
2605
+ ),
2606
+ ...props
2607
+ }
2608
+ ),
2609
+ error && /* @__PURE__ */ jsx26("p", { className: "text-xs text-danger", children: error }),
2610
+ !error && helperText && /* @__PURE__ */ jsx26("p", { className: "text-xs text-text-secondary", children: helperText })
2611
+ ] });
2612
+ }
2613
+ );
2614
+ Textarea.displayName = "Textarea";
2615
+
2616
+ // src/components/skeleton.tsx
2617
+ import { forwardRef as forwardRef27 } from "react";
2618
+ import { cva as cva6 } from "class-variance-authority";
2619
+ import { jsx as jsx27 } from "react/jsx-runtime";
2620
+ var skeletonVariants = cva6(
2621
+ "animate-pulse bg-surface-3",
2622
+ {
2623
+ variants: {
2624
+ variant: {
2625
+ text: "h-4 w-full rounded",
2626
+ circle: "rounded-full",
2627
+ rect: "rounded-md"
2628
+ }
2629
+ },
2630
+ defaultVariants: { variant: "rect" }
2631
+ }
2632
+ );
2633
+ var Skeleton = forwardRef27(({ variant, className, ...props }, ref) => {
2634
+ return /* @__PURE__ */ jsx27("div", { ref, className: cn(skeletonVariants({ variant }), className), "aria-hidden": "true", ...props });
2635
+ });
2636
+ Skeleton.displayName = "Skeleton";
2637
+
2638
+ // src/components/progress.tsx
2639
+ import { forwardRef as forwardRef28 } from "react";
2640
+ import { jsx as jsx28, jsxs as jsxs17 } from "react/jsx-runtime";
2641
+ var progressColorClasses = {
2642
+ primary: "bg-primary",
2643
+ success: "bg-success",
2644
+ warning: "bg-warning",
2645
+ danger: "bg-danger",
2646
+ info: "bg-info"
2647
+ };
2648
+ var Progress = forwardRef28(({
2649
+ value,
2650
+ max = 100,
2651
+ showLabel = false,
2652
+ color = "primary",
2653
+ className,
2654
+ ...props
2655
+ }, ref) => {
2656
+ const clamped = Math.min(Math.max(value, 0), max);
2657
+ const percent = Math.round(clamped / max * 100);
2658
+ return /* @__PURE__ */ jsxs17("div", { ref, className: cn("flex items-center gap-3", className), ...props, children: [
2659
+ /* @__PURE__ */ jsx28(
2660
+ "div",
2661
+ {
2662
+ role: "progressbar",
2663
+ "aria-valuenow": clamped,
2664
+ "aria-valuemin": 0,
2665
+ "aria-valuemax": max,
2666
+ className: "h-2 w-full overflow-hidden rounded-full bg-surface-2",
2667
+ children: /* @__PURE__ */ jsx28(
2668
+ "div",
2669
+ {
2670
+ className: cn("h-full rounded-full transition-all duration-500 ease-out", progressColorClasses[color]),
2671
+ style: { width: `${percent}%` }
2672
+ }
2673
+ )
2674
+ }
2675
+ ),
2676
+ showLabel && /* @__PURE__ */ jsxs17("span", { className: "shrink-0 text-xs font-medium text-text-secondary tabular-nums w-9 text-right", children: [
2677
+ percent,
2678
+ "%"
2679
+ ] })
2680
+ ] });
2681
+ });
2682
+ Progress.displayName = "Progress";
2683
+
2684
+ // src/components/sheet.tsx
2685
+ import { forwardRef as forwardRef29 } from "react";
2686
+ import { createPortal as createPortal8 } from "react-dom";
2687
+ import { X as X3 } from "lucide-react";
2688
+ import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
2689
+ var Sheet = forwardRef29(({ open, onClose, side = "right", title, children, className }, ref) => {
2690
+ useEscapeKey(open, onClose);
2691
+ const portalContainer = usePortalContainer();
2692
+ if (!open || !portalContainer) return null;
2693
+ return createPortal8(
2694
+ /* @__PURE__ */ jsxs18("div", { className: "fixed inset-0 z-50 flex", role: "dialog", "aria-modal": "true", children: [
2695
+ /* @__PURE__ */ jsx29(
2696
+ "div",
2697
+ {
2698
+ className: "absolute inset-0 bg-overlay transition-opacity",
2699
+ onClick: onClose,
2700
+ "aria-hidden": "true"
2701
+ }
2702
+ ),
2703
+ /* @__PURE__ */ jsxs18(
2704
+ "div",
2705
+ {
2706
+ ref,
2707
+ className: cn(
2708
+ "relative flex h-full w-80 max-w-full flex-col bg-surface-1 shadow-xl",
2709
+ "transition-transform duration-300 ease-in-out",
2710
+ side === "right" ? "ml-auto translate-x-0" : "mr-auto translate-x-0",
2711
+ className
2712
+ ),
2713
+ children: [
2714
+ title && /* @__PURE__ */ jsxs18("div", { className: "flex items-center justify-between border-b border-border-base px-5 py-4", children: [
2715
+ /* @__PURE__ */ jsx29("h2", { className: "text-base font-semibold text-text-primary", children: title }),
2716
+ /* @__PURE__ */ jsx29(
2717
+ "button",
2718
+ {
2719
+ type: "button",
2720
+ onClick: onClose,
2721
+ className: "rounded-md p-1 text-text-tertiary hover:bg-surface-3 hover:text-text-secondary transition-colors",
2722
+ "aria-label": "\u5173\u95ED",
2723
+ children: /* @__PURE__ */ jsx29(X3, { size: 18 })
2724
+ }
2725
+ )
2726
+ ] }),
2727
+ /* @__PURE__ */ jsx29("div", { className: "flex-1 overflow-y-auto px-5 py-4", children })
2728
+ ]
2729
+ }
2730
+ )
2731
+ ] }),
2732
+ portalContainer
2733
+ );
2734
+ });
2735
+ Sheet.displayName = "Sheet";
2736
+
2737
+ // src/components/empty-state.tsx
2738
+ import { forwardRef as forwardRef30 } from "react";
2739
+ import { jsx as jsx30, jsxs as jsxs19 } from "react/jsx-runtime";
2740
+ var EmptyState = forwardRef30(
2741
+ ({ icon: Icon, title, description, action, className, ...props }, ref) => {
2742
+ return /* @__PURE__ */ jsxs19(
2743
+ "div",
2744
+ {
2745
+ ref,
2746
+ className: cn(
2747
+ "flex flex-col items-center justify-center gap-3 py-16 text-center",
2748
+ className
2749
+ ),
2750
+ ...props,
2751
+ children: [
2752
+ Icon && /* @__PURE__ */ jsx30("div", { className: "flex h-14 w-14 items-center justify-center rounded-full bg-surface-2", children: /* @__PURE__ */ jsx30(Icon, { size: 28, className: "text-text-tertiary" }) }),
2753
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-1", children: [
2754
+ /* @__PURE__ */ jsx30("p", { className: "text-base font-semibold text-text-primary", children: title }),
2755
+ description && /* @__PURE__ */ jsx30("p", { className: "max-w-xs text-sm text-text-secondary", children: description })
2756
+ ] }),
2757
+ action && /* @__PURE__ */ jsx30("div", { className: "mt-1", children: action })
2758
+ ]
2759
+ }
2760
+ );
2761
+ }
2762
+ );
2763
+ EmptyState.displayName = "EmptyState";
2764
+
2765
+ // src/components/descriptions.tsx
2766
+ import { forwardRef as forwardRef31, Children as Children2, Fragment, isValidElement as isValidElement2 } from "react";
2767
+ import { jsx as jsx31, jsxs as jsxs20 } from "react/jsx-runtime";
2768
+ var SIZE_LABEL_CLASSES = {
2769
+ sm: "px-3 py-2 text-xs",
2770
+ default: "px-3.5 py-2.5 text-sm",
2771
+ lg: "px-4 py-3 text-base"
2772
+ };
2773
+ var SIZE_VALUE_CLASSES = {
2774
+ sm: "px-3 py-2 text-xs",
2775
+ default: "px-3.5 py-2.5 text-sm",
2776
+ lg: "px-4 py-3 text-base"
2777
+ };
2778
+ var DescriptionsItem = forwardRef31(
2779
+ ({ label, children, className, span: _span, ...props }, ref) => {
2780
+ return /* @__PURE__ */ jsxs20("div", { ref, className, ...props, children: [
2781
+ /* @__PURE__ */ jsx31("span", { className: "text-text-secondary", children: label }),
2782
+ /* @__PURE__ */ jsx31("span", { className: "text-text-primary", children })
2783
+ ] });
2784
+ }
2785
+ );
2786
+ DescriptionsItem.displayName = "DescriptionsItem";
2787
+ var Descriptions = forwardRef31(
2788
+ ({ column = 2, bordered = false, size = "default", title, className, children, ...props }, ref) => {
2789
+ const items = Children2.toArray(children).filter(
2790
+ (child) => isValidElement2(child) && child.type.displayName === "DescriptionsItem"
2791
+ );
2792
+ if (bordered) {
2793
+ const rows = [];
2794
+ let currentRow = [];
2795
+ let currentColCount = 0;
2796
+ for (const item of items) {
2797
+ const span = item.props.span ?? 1;
2798
+ if (currentColCount + span > column) {
2799
+ rows.push(currentRow);
2800
+ currentRow = [];
2801
+ currentColCount = 0;
2802
+ }
2803
+ currentRow.push({ label: item.props.label, value: item.props.children, span });
2804
+ currentColCount += span;
2805
+ }
2806
+ if (currentRow.length > 0) rows.push(currentRow);
2807
+ const labelColumnWidth = column === 1 ? "34%" : `${Math.max(12, 100 / (column * 2))}%`;
2808
+ return /* @__PURE__ */ jsxs20("div", { ref, className: cn("w-full", className), ...props, children: [
2809
+ title && /* @__PURE__ */ jsx31("div", { className: "mb-3 text-base font-semibold text-text-primary", children: title }),
2810
+ /* @__PURE__ */ jsx31("div", { className: "overflow-hidden rounded-lg border border-border-base bg-surface-1", children: /* @__PURE__ */ jsx31("table", { className: "w-full table-fixed border-separate border-spacing-0", children: /* @__PURE__ */ jsx31("tbody", { children: rows.map((row, rowIdx) => /* @__PURE__ */ jsx31("tr", { children: row.map((cell, cellIdx) => {
2811
+ const hasRowDivider = rowIdx < rows.length - 1;
2812
+ return /* @__PURE__ */ jsxs20(Fragment, { children: [
2813
+ /* @__PURE__ */ jsx31(
2814
+ "td",
2815
+ {
2816
+ className: cn(
2817
+ "border-r border-border-base bg-surface-2/80 font-medium text-text-secondary",
2818
+ hasRowDivider && "border-b",
2819
+ SIZE_LABEL_CLASSES[size]
2820
+ ),
2821
+ style: { width: labelColumnWidth },
2822
+ children: cell.label
2823
+ }
2824
+ ),
2825
+ /* @__PURE__ */ jsx31(
2826
+ "td",
2827
+ {
2828
+ className: cn(
2829
+ "break-words font-medium text-text-primary",
2830
+ hasRowDivider && "border-b border-border-base",
2831
+ SIZE_VALUE_CLASSES[size]
2832
+ ),
2833
+ colSpan: cell.span > 1 ? cell.span * 2 - 1 : 1,
2834
+ children: cell.value
2835
+ }
2836
+ )
2837
+ ] }, `${rowIdx}-${cellIdx}`);
2838
+ }) }, rowIdx)) }) }) })
2839
+ ] });
2840
+ }
2841
+ const GRID_COLS = {
2842
+ 1: "grid-cols-1",
2843
+ 2: "grid-cols-2",
2844
+ 3: "grid-cols-3",
2845
+ 4: "grid-cols-4"
2846
+ };
2847
+ return /* @__PURE__ */ jsxs20("div", { ref, className: cn("w-full", className), ...props, children: [
2848
+ title && /* @__PURE__ */ jsx31("div", { className: "mb-3 text-base font-semibold text-text-primary", children: title }),
2849
+ /* @__PURE__ */ jsx31("div", { className: cn("grid gap-x-6 gap-y-4", GRID_COLS[column]), children: items.map((item, idx) => {
2850
+ const span = item.props.span ?? 1;
2851
+ const colSpanClass = span === 2 ? "col-span-2" : span === 3 ? "col-span-3" : span === 4 ? "col-span-4" : "";
2852
+ return /* @__PURE__ */ jsxs20("div", { className: cn("flex flex-col gap-0.5", colSpanClass, item.props.className), children: [
2853
+ /* @__PURE__ */ jsx31("span", { className: cn("font-medium text-text-secondary", size === "sm" ? "text-xs" : size === "lg" ? "text-sm" : "text-xs"), children: item.props.label }),
2854
+ /* @__PURE__ */ jsx31("span", { className: cn("text-text-primary", size === "sm" ? "text-sm" : size === "lg" ? "text-base" : "text-sm"), children: item.props.children })
2855
+ ] }, idx);
2856
+ }) })
2857
+ ] });
2858
+ }
2859
+ );
2860
+ Descriptions.displayName = "Descriptions";
2861
+
2862
+ // src/components/statistic.tsx
2863
+ import { forwardRef as forwardRef32 } from "react";
2864
+ import { TrendingUp, TrendingDown, Minus as Minus3 } from "lucide-react";
2865
+ import { jsx as jsx32, jsxs as jsxs21 } from "react/jsx-runtime";
2866
+ var SIZE_TITLE_CLASSES = {
2867
+ sm: "text-xs",
2868
+ default: "text-sm",
2869
+ lg: "text-base"
2870
+ };
2871
+ var SIZE_VALUE_CLASSES2 = {
2872
+ sm: "text-2xl",
2873
+ default: "text-3xl",
2874
+ lg: "text-4xl"
2875
+ };
2876
+ var SIZE_SUFFIX_CLASSES = {
2877
+ sm: "text-xs",
2878
+ default: "text-sm",
2879
+ lg: "text-base"
2880
+ };
2881
+ var TREND_ICON_COLOR = {
2882
+ up: "text-success",
2883
+ down: "text-danger",
2884
+ flat: "text-text-secondary"
2885
+ };
2886
+ var TREND_TEXT_COLOR = {
2887
+ up: "text-success",
2888
+ down: "text-danger",
2889
+ flat: "text-text-secondary"
2890
+ };
2891
+ function formatValue(value, precision) {
2892
+ if (typeof value === "string") return value;
2893
+ return new Intl.NumberFormat("zh-CN", {
2894
+ minimumFractionDigits: precision ?? 0,
2895
+ maximumFractionDigits: precision ?? 0
2896
+ }).format(value);
2897
+ }
2898
+ var Statistic = forwardRef32(
2899
+ ({ title, value, precision, prefix, suffix, trend, change, size = "default", className, ...props }, ref) => {
2900
+ const TrendIcon = trend === "up" ? TrendingUp : trend === "down" ? TrendingDown : trend === "flat" ? Minus3 : null;
2901
+ const iconSize = size === "sm" ? 14 : size === "lg" ? 18 : 16;
2902
+ return /* @__PURE__ */ jsxs21("div", { ref, className: cn("flex flex-col gap-1", className), ...props, children: [
2903
+ title && /* @__PURE__ */ jsx32("span", { className: cn("font-medium text-text-secondary", SIZE_TITLE_CLASSES[size]), children: title }),
2904
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-baseline gap-1", children: [
2905
+ prefix && /* @__PURE__ */ jsx32("span", { className: cn("text-text-primary", SIZE_SUFFIX_CLASSES[size]), children: prefix }),
2906
+ /* @__PURE__ */ jsx32("span", { className: cn("font-bold tabular-nums text-text-primary", SIZE_VALUE_CLASSES2[size]), children: formatValue(value, precision) }),
2907
+ suffix && /* @__PURE__ */ jsx32("span", { className: cn("text-text-secondary", SIZE_SUFFIX_CLASSES[size]), children: suffix })
2908
+ ] }),
2909
+ (trend || change) && /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-1", children: [
2910
+ TrendIcon && trend && /* @__PURE__ */ jsx32(TrendIcon, { size: iconSize, className: TREND_ICON_COLOR[trend] }),
2911
+ change && trend && /* @__PURE__ */ jsx32("span", { className: cn("text-xs font-medium", TREND_TEXT_COLOR[trend]), children: change })
2912
+ ] })
2913
+ ] });
2914
+ }
2915
+ );
2916
+ Statistic.displayName = "Statistic";
2917
+
2918
+ export {
2919
+ Dialog,
2920
+ Drawer,
2921
+ DrawerContent,
2922
+ DrawerHeader,
2923
+ DrawerTitle,
2924
+ DrawerDescription,
2925
+ DrawerFooter,
2926
+ DrawerClose,
2927
+ Table,
2928
+ TableHeader,
2929
+ TableBody,
2930
+ TableRow,
2931
+ TableHead,
2932
+ TableCell,
2933
+ toast,
2934
+ Toaster,
2935
+ Pagination,
2936
+ Select,
2937
+ checkboxVariants,
2938
+ Checkbox,
2939
+ Radio,
2940
+ RadioGroup,
2941
+ Slider,
2942
+ numberInputVariants,
2943
+ NumberInput,
2944
+ OTPInput,
2945
+ Form,
2946
+ FormField,
2947
+ FormItem,
2948
+ FormLabel,
2949
+ FormControl,
2950
+ FormDescription,
2951
+ FormMessage,
2952
+ Tabs,
2953
+ TabsList,
2954
+ TabsTrigger,
2955
+ TabsContent,
2956
+ Accordion,
2957
+ AccordionItem,
2958
+ AccordionTrigger,
2959
+ AccordionContent,
2960
+ Steps,
2961
+ DropdownMenu,
2962
+ DropdownTrigger,
2963
+ DropdownContent,
2964
+ DropdownItem,
2965
+ DropdownSeparator,
2966
+ Popover,
2967
+ PopoverTrigger,
2968
+ PopoverContent,
2969
+ ContextMenu,
2970
+ ContextMenuTrigger,
2971
+ ContextMenuContent,
2972
+ ContextMenuItem,
2973
+ ContextMenuSeparator,
2974
+ NavigationMenu,
2975
+ NavigationMenuItem,
2976
+ NavigationMenuLink,
2977
+ NavigationMenuTrigger,
2978
+ NavigationMenuContent,
2979
+ Avatar,
2980
+ AvatarImage,
2981
+ AvatarFallback,
2982
+ AvatarGroup,
2983
+ Tooltip,
2984
+ TooltipTrigger,
2985
+ TooltipContent,
2986
+ Alert,
2987
+ AlertTitle,
2988
+ AlertDescription,
2989
+ Breadcrumb,
2990
+ BreadcrumbItem,
2991
+ BreadcrumbLink,
2992
+ BreadcrumbSeparator,
2993
+ Switch,
2994
+ Textarea,
2995
+ Skeleton,
2996
+ Progress,
2997
+ Sheet,
2998
+ EmptyState,
2999
+ DescriptionsItem,
3000
+ Descriptions,
3001
+ Statistic
3002
+ };
3003
+ //# sourceMappingURL=chunk-73WQAE3E.js.map