@ambersecurityinc/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2359 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var react = require('react');
5
+ var reactDom = require('react-dom');
6
+
7
+ // src/components/layout/AppShell.tsx
8
+ var rootStyle = {
9
+ display: "flex",
10
+ minHeight: "100vh",
11
+ background: "var(--bg)",
12
+ color: "var(--text)",
13
+ fontFamily: "var(--font-body, 'Manrope', sans-serif)"
14
+ };
15
+ var mainWrapperStyle = {
16
+ display: "flex",
17
+ flexDirection: "column",
18
+ flex: 1,
19
+ minWidth: 0
20
+ // prevent flex child overflow
21
+ };
22
+ var contentStyle = {
23
+ flex: 1,
24
+ padding: "24px",
25
+ overflowY: "auto"
26
+ };
27
+ function AppShell({
28
+ sidebar,
29
+ header,
30
+ children,
31
+ className,
32
+ style
33
+ }) {
34
+ return /* @__PURE__ */ jsxRuntime.jsxs(
35
+ "div",
36
+ {
37
+ className,
38
+ style: { ...rootStyle, ...style },
39
+ children: [
40
+ sidebar,
41
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: mainWrapperStyle, children: [
42
+ header,
43
+ /* @__PURE__ */ jsxRuntime.jsx("main", { style: contentStyle, children })
44
+ ] })
45
+ ]
46
+ }
47
+ );
48
+ }
49
+ var SIDEBAR_WIDTH = 260;
50
+ var SIDEBAR_COLLAPSED_WIDTH = 64;
51
+ var MOBILE_BREAKPOINT = 768;
52
+ function Sidebar({
53
+ collapsed = false,
54
+ onToggle,
55
+ header,
56
+ children,
57
+ className,
58
+ style,
59
+ mobileOpen = false,
60
+ onMobileClose
61
+ }) {
62
+ react.useEffect(() => {
63
+ if (!mobileOpen) return;
64
+ const handleKeyDown = (e) => {
65
+ if (e.key === "Escape") onMobileClose?.();
66
+ };
67
+ document.addEventListener("keydown", handleKeyDown);
68
+ return () => document.removeEventListener("keydown", handleKeyDown);
69
+ }, [mobileOpen, onMobileClose]);
70
+ react.useEffect(() => {
71
+ if (!mobileOpen) return;
72
+ const prev = document.body.style.overflow;
73
+ document.body.style.overflow = "hidden";
74
+ return () => {
75
+ document.body.style.overflow = prev;
76
+ };
77
+ }, [mobileOpen]);
78
+ const handleToggleKeyDown = react.useCallback(
79
+ (e) => {
80
+ if (e.key === "Enter" || e.key === " ") {
81
+ e.preventDefault();
82
+ onToggle?.();
83
+ }
84
+ },
85
+ [onToggle]
86
+ );
87
+ const width = collapsed ? SIDEBAR_COLLAPSED_WIDTH : SIDEBAR_WIDTH;
88
+ const navStyle = {
89
+ width,
90
+ minWidth: width,
91
+ height: "100vh",
92
+ display: "flex",
93
+ flexDirection: "column",
94
+ background: "var(--sidebar)",
95
+ color: "var(--text-sidebar)",
96
+ borderRight: "1px solid var(--border-sidebar)",
97
+ transition: "width 200ms ease, min-width 200ms ease",
98
+ overflowX: "hidden",
99
+ overflowY: "auto",
100
+ position: "sticky",
101
+ top: 0
102
+ };
103
+ const mobileNavStyle = {
104
+ ...navStyle,
105
+ position: "fixed",
106
+ top: 0,
107
+ left: 0,
108
+ zIndex: 1100,
109
+ width: SIDEBAR_WIDTH,
110
+ minWidth: SIDEBAR_WIDTH,
111
+ transform: mobileOpen ? "translateX(0)" : `translateX(-100%)`,
112
+ transition: "transform 200ms ease"
113
+ };
114
+ const backdropStyle = {
115
+ position: "fixed",
116
+ inset: 0,
117
+ zIndex: 1099,
118
+ background: "rgba(0,0,0,0.5)",
119
+ opacity: mobileOpen ? 1 : 0,
120
+ pointerEvents: mobileOpen ? "auto" : "none",
121
+ transition: "opacity 200ms ease"
122
+ };
123
+ const headerSlotStyle = {
124
+ padding: collapsed ? "16px 8px" : "16px",
125
+ borderBottom: "1px solid var(--border-sidebar)",
126
+ display: "flex",
127
+ alignItems: "center",
128
+ justifyContent: collapsed ? "center" : "space-between",
129
+ gap: 8,
130
+ minHeight: 56
131
+ };
132
+ const toggleBtnStyle = {
133
+ background: "transparent",
134
+ border: "none",
135
+ color: "var(--text-sidebar-muted)",
136
+ cursor: "pointer",
137
+ padding: 4,
138
+ borderRadius: 4,
139
+ display: "flex",
140
+ alignItems: "center",
141
+ justifyContent: "center",
142
+ flexShrink: 0
143
+ };
144
+ const bodyStyle = {
145
+ flex: 1,
146
+ padding: collapsed ? "8px 4px" : "8px 12px",
147
+ overflowY: "auto",
148
+ overflowX: "hidden"
149
+ };
150
+ const toggleIcon = collapsed ? "\u25B6" : "\u25C0";
151
+ const sidebarContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
152
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerSlotStyle, children: [
153
+ !collapsed && header,
154
+ onToggle && /* @__PURE__ */ jsxRuntime.jsx(
155
+ "button",
156
+ {
157
+ type: "button",
158
+ onClick: onToggle,
159
+ onKeyDown: handleToggleKeyDown,
160
+ "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
161
+ style: toggleBtnStyle,
162
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: { fontSize: 12 }, children: toggleIcon })
163
+ }
164
+ )
165
+ ] }),
166
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: bodyStyle, role: "group", "aria-label": "Sidebar navigation", children })
167
+ ] });
168
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
169
+ /* @__PURE__ */ jsxRuntime.jsx(
170
+ "nav",
171
+ {
172
+ "aria-label": "Main navigation",
173
+ className,
174
+ style: { ...navStyle, ...style },
175
+ "data-sidebar-desktop": "",
176
+ children: sidebarContent
177
+ }
178
+ ),
179
+ /* @__PURE__ */ jsxRuntime.jsx(
180
+ "div",
181
+ {
182
+ "aria-hidden": !mobileOpen,
183
+ style: backdropStyle,
184
+ onClick: onMobileClose
185
+ }
186
+ ),
187
+ /* @__PURE__ */ jsxRuntime.jsx(
188
+ "nav",
189
+ {
190
+ "aria-label": "Main navigation",
191
+ "aria-modal": mobileOpen ? true : void 0,
192
+ role: mobileOpen ? "dialog" : void 0,
193
+ style: { ...mobileNavStyle, ...style },
194
+ "data-sidebar-mobile": "",
195
+ children: sidebarContent
196
+ }
197
+ ),
198
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
199
+ [data-sidebar-mobile] { display: none; }
200
+ @media (max-width: ${MOBILE_BREAKPOINT - 1}px) {
201
+ [data-sidebar-desktop] { display: none !important; }
202
+ [data-sidebar-mobile] { display: flex !important; }
203
+ }
204
+ ` })
205
+ ] });
206
+ }
207
+ var sectionStyle = {
208
+ marginBottom: 8
209
+ };
210
+ var sectionTitleStyle = {
211
+ fontSize: 11,
212
+ fontWeight: 600,
213
+ textTransform: "uppercase",
214
+ letterSpacing: "0.05em",
215
+ color: "var(--text-sidebar-muted)",
216
+ padding: "12px 8px 4px",
217
+ userSelect: "none"
218
+ };
219
+ function SidebarSection({
220
+ title,
221
+ children,
222
+ className,
223
+ style: customStyle
224
+ }) {
225
+ return /* @__PURE__ */ jsxRuntime.jsxs(
226
+ "div",
227
+ {
228
+ className,
229
+ style: { ...sectionStyle, ...customStyle },
230
+ role: "group",
231
+ "aria-label": title,
232
+ children: [
233
+ title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: sectionTitleStyle, children: title }),
234
+ children
235
+ ]
236
+ }
237
+ );
238
+ }
239
+ var barStyle = {
240
+ display: "flex",
241
+ alignItems: "center",
242
+ justifyContent: "space-between",
243
+ gap: 16,
244
+ minHeight: 56,
245
+ padding: "0 24px",
246
+ background: "var(--surface)",
247
+ borderBottom: "1px solid var(--border)",
248
+ flexShrink: 0
249
+ };
250
+ var breadcrumbNavStyle = {
251
+ display: "flex",
252
+ alignItems: "center",
253
+ gap: 6,
254
+ flex: 1,
255
+ minWidth: 0,
256
+ overflow: "hidden"
257
+ };
258
+ var crumbStyle = {
259
+ fontSize: 14,
260
+ color: "var(--text-muted)",
261
+ whiteSpace: "nowrap",
262
+ textDecoration: "none",
263
+ transition: "color 150ms ease"
264
+ };
265
+ var crumbCurrentStyle = {
266
+ ...crumbStyle,
267
+ color: "var(--text)",
268
+ fontWeight: 600
269
+ };
270
+ var separatorStyle = {
271
+ fontSize: 12,
272
+ color: "var(--text-muted)",
273
+ userSelect: "none",
274
+ flexShrink: 0
275
+ };
276
+ var rightSlotStyle = {
277
+ display: "flex",
278
+ alignItems: "center",
279
+ gap: 12,
280
+ flexShrink: 0
281
+ };
282
+ function Header({
283
+ breadcrumbs = [],
284
+ actions,
285
+ avatar,
286
+ className,
287
+ style
288
+ }) {
289
+ const lastIdx = breadcrumbs.length - 1;
290
+ return /* @__PURE__ */ jsxRuntime.jsxs(
291
+ "header",
292
+ {
293
+ className,
294
+ style: { ...barStyle, ...style },
295
+ children: [
296
+ breadcrumbs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Breadcrumb", style: breadcrumbNavStyle, children: /* @__PURE__ */ jsxRuntime.jsx(
297
+ "ol",
298
+ {
299
+ style: {
300
+ display: "flex",
301
+ alignItems: "center",
302
+ gap: 6,
303
+ listStyle: "none",
304
+ margin: 0,
305
+ padding: 0
306
+ },
307
+ children: breadcrumbs.map((crumb, i) => {
308
+ const isCurrent = i === lastIdx;
309
+ return /* @__PURE__ */ jsxRuntime.jsxs(
310
+ "li",
311
+ {
312
+ style: { display: "flex", alignItems: "center", gap: 6 },
313
+ children: [
314
+ i > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: separatorStyle, children: "/" }),
315
+ crumb.href && !isCurrent ? /* @__PURE__ */ jsxRuntime.jsx(
316
+ "a",
317
+ {
318
+ href: crumb.href,
319
+ style: crumbStyle,
320
+ children: crumb.label
321
+ }
322
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
323
+ "span",
324
+ {
325
+ style: isCurrent ? crumbCurrentStyle : crumbStyle,
326
+ "aria-current": isCurrent ? "page" : void 0,
327
+ children: crumb.label
328
+ }
329
+ )
330
+ ]
331
+ },
332
+ `${crumb.label}-${i}`
333
+ );
334
+ })
335
+ }
336
+ ) }),
337
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: rightSlotStyle, children: [
338
+ actions,
339
+ avatar
340
+ ] })
341
+ ]
342
+ }
343
+ );
344
+ }
345
+ var variantStyles = {
346
+ success: { background: "var(--success-soft)", color: "var(--success)" },
347
+ warning: { background: "var(--warning-soft)", color: "var(--warning)" },
348
+ error: { background: "var(--error-soft)", color: "var(--error)" },
349
+ info: { background: "var(--info-soft)", color: "var(--info)" },
350
+ accent: { background: "var(--accent-soft)", color: "var(--accent)" },
351
+ neutral: { background: "var(--surface-alt)", color: "var(--text-secondary)" }
352
+ };
353
+ var sizeStyles = {
354
+ sm: { fontSize: 11, padding: "1px 6px", borderRadius: "var(--radius-sm, 6px)" },
355
+ md: { fontSize: 12, padding: "2px 8px", borderRadius: "var(--radius-sm, 6px)" }
356
+ };
357
+ function Badge({
358
+ variant,
359
+ size = "md",
360
+ children,
361
+ className,
362
+ style,
363
+ ...rest
364
+ }) {
365
+ return /* @__PURE__ */ jsxRuntime.jsx(
366
+ "span",
367
+ {
368
+ className,
369
+ style: {
370
+ display: "inline-flex",
371
+ alignItems: "center",
372
+ fontFamily: "var(--font-body)",
373
+ fontWeight: 600,
374
+ lineHeight: 1.5,
375
+ whiteSpace: "nowrap",
376
+ ...variantStyles[variant],
377
+ ...sizeStyles[size],
378
+ ...style
379
+ },
380
+ ...rest,
381
+ children
382
+ }
383
+ );
384
+ }
385
+ var shimmerKeyframes = `
386
+ @keyframes amber-skeleton-shimmer {
387
+ 0% { background-position: -400px 0; }
388
+ 100% { background-position: 400px 0; }
389
+ }
390
+ `;
391
+ var styleInjected = false;
392
+ function injectKeyframes() {
393
+ if (styleInjected || typeof document === "undefined") return;
394
+ const sheet = document.createElement("style");
395
+ sheet.textContent = shimmerKeyframes;
396
+ document.head.appendChild(sheet);
397
+ styleInjected = true;
398
+ }
399
+ function baseStyle(extra) {
400
+ return {
401
+ background: `linear-gradient(90deg, var(--surface-alt, #eee) 25%, var(--surface-hover, #ddd) 50%, var(--surface-alt, #eee) 75%)`,
402
+ backgroundSize: "800px 100%",
403
+ animation: "amber-skeleton-shimmer 1.6s ease-in-out infinite",
404
+ borderRadius: "var(--radius-sm, 6px)",
405
+ ...extra
406
+ };
407
+ }
408
+ function Skeleton({
409
+ variant,
410
+ width,
411
+ height,
412
+ lines = 3,
413
+ className,
414
+ style,
415
+ ...rest
416
+ }) {
417
+ injectKeyframes();
418
+ if (variant === "text") {
419
+ return /* @__PURE__ */ jsxRuntime.jsxs(
420
+ "div",
421
+ {
422
+ role: "status",
423
+ "aria-label": "Loading",
424
+ className,
425
+ style: { display: "flex", flexDirection: "column", gap: 8, ...style },
426
+ ...rest,
427
+ children: [
428
+ Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx(
429
+ "div",
430
+ {
431
+ style: baseStyle({
432
+ width: i === lines - 1 ? "60%" : width ?? "100%",
433
+ height: typeof height === "number" ? height : height ?? 14,
434
+ borderRadius: "var(--radius-sm, 6px)"
435
+ })
436
+ },
437
+ i
438
+ )),
439
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0,0,0,0)" }, children: "Loading..." })
440
+ ]
441
+ }
442
+ );
443
+ }
444
+ const isCircle = variant === "circle";
445
+ const resolvedWidth = width ?? (isCircle ? 40 : "100%");
446
+ const resolvedHeight = height ?? (isCircle ? 40 : 20);
447
+ return /* @__PURE__ */ jsxRuntime.jsx(
448
+ "div",
449
+ {
450
+ role: "status",
451
+ "aria-label": "Loading",
452
+ className,
453
+ style: baseStyle({
454
+ width: resolvedWidth,
455
+ height: resolvedHeight,
456
+ borderRadius: isCircle ? "50%" : "var(--radius-sm, 6px)",
457
+ ...style
458
+ }),
459
+ ...rest,
460
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0,0,0,0)" }, children: "Loading..." })
461
+ }
462
+ );
463
+ }
464
+ function EmptyState({
465
+ icon,
466
+ title,
467
+ description,
468
+ action,
469
+ className,
470
+ style,
471
+ ...rest
472
+ }) {
473
+ return /* @__PURE__ */ jsxRuntime.jsxs(
474
+ "div",
475
+ {
476
+ role: "status",
477
+ className,
478
+ style: {
479
+ display: "flex",
480
+ flexDirection: "column",
481
+ alignItems: "center",
482
+ justifyContent: "center",
483
+ padding: 48,
484
+ textAlign: "center",
485
+ fontFamily: "var(--font-body)",
486
+ ...style
487
+ },
488
+ ...rest,
489
+ children: [
490
+ icon && /* @__PURE__ */ jsxRuntime.jsx(
491
+ "div",
492
+ {
493
+ style: {
494
+ marginBottom: 16,
495
+ color: "var(--text-muted)",
496
+ fontSize: 40,
497
+ lineHeight: 1
498
+ },
499
+ children: icon
500
+ }
501
+ ),
502
+ /* @__PURE__ */ jsxRuntime.jsx(
503
+ "h3",
504
+ {
505
+ style: {
506
+ margin: 0,
507
+ fontSize: 16,
508
+ fontWeight: 600,
509
+ color: "var(--text)",
510
+ fontFamily: "var(--font-display)"
511
+ },
512
+ children: title
513
+ }
514
+ ),
515
+ description && /* @__PURE__ */ jsxRuntime.jsx(
516
+ "p",
517
+ {
518
+ style: {
519
+ margin: "8px 0 0",
520
+ fontSize: 14,
521
+ color: "var(--text-secondary)",
522
+ maxWidth: 360,
523
+ lineHeight: 1.5
524
+ },
525
+ children: description
526
+ }
527
+ ),
528
+ action && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 20 }, children: action })
529
+ ]
530
+ }
531
+ );
532
+ }
533
+ function SortIcon({ direction, active }) {
534
+ return /* @__PURE__ */ jsxRuntime.jsxs(
535
+ "svg",
536
+ {
537
+ width: 14,
538
+ height: 14,
539
+ viewBox: "0 0 14 14",
540
+ fill: "none",
541
+ "aria-hidden": "true",
542
+ style: {
543
+ marginLeft: 4,
544
+ flexShrink: 0,
545
+ opacity: active ? 1 : 0.35,
546
+ transition: "opacity 0.15s"
547
+ },
548
+ children: [
549
+ /* @__PURE__ */ jsxRuntime.jsx(
550
+ "path",
551
+ {
552
+ d: "M7 3L10 6.5H4L7 3Z",
553
+ fill: active && direction === "asc" ? "currentColor" : "var(--text-muted)"
554
+ }
555
+ ),
556
+ /* @__PURE__ */ jsxRuntime.jsx(
557
+ "path",
558
+ {
559
+ d: "M7 11L4 7.5H10L7 11Z",
560
+ fill: active && direction === "desc" ? "currentColor" : "var(--text-muted)"
561
+ }
562
+ )
563
+ ]
564
+ }
565
+ );
566
+ }
567
+ var tableWrapperStyle = {
568
+ width: "100%",
569
+ overflowX: "auto",
570
+ border: "1px solid var(--border)",
571
+ borderRadius: "var(--radius-md, 10px)",
572
+ background: "var(--surface)",
573
+ fontFamily: "var(--font-body)"
574
+ };
575
+ var tableStyle = {
576
+ width: "100%",
577
+ borderCollapse: "collapse",
578
+ fontSize: 14
579
+ };
580
+ var thStyle = {
581
+ textAlign: "left",
582
+ padding: "10px 14px",
583
+ fontWeight: 600,
584
+ fontSize: 12,
585
+ color: "var(--text-secondary)",
586
+ textTransform: "uppercase",
587
+ letterSpacing: "0.04em",
588
+ borderBottom: "1px solid var(--border)",
589
+ background: "var(--surface-alt)",
590
+ whiteSpace: "nowrap",
591
+ userSelect: "none"
592
+ };
593
+ var tdStyle = {
594
+ padding: "10px 14px",
595
+ color: "var(--text)",
596
+ borderBottom: "1px solid var(--border)",
597
+ verticalAlign: "middle"
598
+ };
599
+ var checkboxCellStyle = {
600
+ width: 36,
601
+ textAlign: "center"
602
+ };
603
+ function DataTable({
604
+ columns,
605
+ data,
606
+ loading = false,
607
+ emptyState,
608
+ selectable = false,
609
+ selectedRows,
610
+ onSelectionChange,
611
+ sortKey,
612
+ sortDirection,
613
+ onSort,
614
+ cursor = null,
615
+ onPageChange,
616
+ pageSize = 10,
617
+ className,
618
+ style,
619
+ ...rest
620
+ }) {
621
+ const selected = selectedRows ?? /* @__PURE__ */ new Set();
622
+ const allSelected = react.useMemo(
623
+ () => data.length > 0 && data.every((_, i) => selected.has(i)),
624
+ [data, selected]
625
+ );
626
+ const someSelected = react.useMemo(
627
+ () => !allSelected && data.some((_, i) => selected.has(i)),
628
+ [data, selected, allSelected]
629
+ );
630
+ const toggleAll = react.useCallback(() => {
631
+ if (!onSelectionChange) return;
632
+ if (allSelected) {
633
+ onSelectionChange(/* @__PURE__ */ new Set());
634
+ } else {
635
+ onSelectionChange(new Set(data.map((_, i) => i)));
636
+ }
637
+ }, [allSelected, data, onSelectionChange]);
638
+ const toggleRow = react.useCallback(
639
+ (index) => {
640
+ if (!onSelectionChange) return;
641
+ const next = new Set(selected);
642
+ if (next.has(index)) {
643
+ next.delete(index);
644
+ } else {
645
+ next.add(index);
646
+ }
647
+ onSelectionChange(next);
648
+ },
649
+ [selected, onSelectionChange]
650
+ );
651
+ const handleSort = react.useCallback(
652
+ (key) => {
653
+ if (!onSort) return;
654
+ const nextDir = sortKey === key && sortDirection === "asc" ? "desc" : "asc";
655
+ onSort(key, nextDir);
656
+ },
657
+ [onSort, sortKey, sortDirection]
658
+ );
659
+ const getCellValue = (row, key) => {
660
+ const val = row[key];
661
+ if (val == null) return "";
662
+ return String(val);
663
+ };
664
+ if (loading) {
665
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...tableWrapperStyle, ...style }, ...rest, children: /* @__PURE__ */ jsxRuntime.jsxs("table", { style: tableStyle, role: "table", "aria-busy": "true", children: [
666
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
667
+ selectable && /* @__PURE__ */ jsxRuntime.jsx("th", { style: { ...thStyle, ...checkboxCellStyle } }),
668
+ columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("th", { style: thStyle, children: col.header }, col.key))
669
+ ] }) }),
670
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: Array.from({ length: pageSize }, (_, rowIdx) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
671
+ selectable && /* @__PURE__ */ jsxRuntime.jsx("td", { style: { ...tdStyle, ...checkboxCellStyle }, children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "rect", width: 16, height: 16 }) }),
672
+ columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: tdStyle, children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { variant: "rect", width: "80%", height: 14 }) }, col.key))
673
+ ] }, rowIdx)) })
674
+ ] }) });
675
+ }
676
+ if (data.length === 0) {
677
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...tableWrapperStyle, ...style }, ...rest, children: emptyState ?? /* @__PURE__ */ jsxRuntime.jsx(
678
+ EmptyState,
679
+ {
680
+ title: "No data",
681
+ description: "There are no items to display."
682
+ }
683
+ ) });
684
+ }
685
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { ...tableWrapperStyle, ...style }, ...rest, children: [
686
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { style: tableStyle, role: "table", children: [
687
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
688
+ selectable && /* @__PURE__ */ jsxRuntime.jsx("th", { style: { ...thStyle, ...checkboxCellStyle }, children: /* @__PURE__ */ jsxRuntime.jsx(
689
+ "input",
690
+ {
691
+ type: "checkbox",
692
+ checked: allSelected,
693
+ ref: (el) => {
694
+ if (el) el.indeterminate = someSelected;
695
+ },
696
+ onChange: toggleAll,
697
+ "aria-label": "Select all rows",
698
+ style: { cursor: "pointer" }
699
+ }
700
+ ) }),
701
+ columns.map((col) => {
702
+ const isSortable = col.sortable && onSort;
703
+ const isActive = sortKey === col.key;
704
+ return /* @__PURE__ */ jsxRuntime.jsx(
705
+ "th",
706
+ {
707
+ style: {
708
+ ...thStyle,
709
+ cursor: isSortable ? "pointer" : "default"
710
+ },
711
+ onClick: isSortable ? () => handleSort(col.key) : void 0,
712
+ "aria-sort": isActive ? sortDirection === "asc" ? "ascending" : "descending" : void 0,
713
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
714
+ col.header,
715
+ isSortable && /* @__PURE__ */ jsxRuntime.jsx(
716
+ SortIcon,
717
+ {
718
+ direction: isActive ? sortDirection : void 0,
719
+ active: isActive
720
+ }
721
+ )
722
+ ] })
723
+ },
724
+ col.key
725
+ );
726
+ })
727
+ ] }) }),
728
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: data.map((row, rowIndex) => {
729
+ const isSelected = selected.has(rowIndex);
730
+ return /* @__PURE__ */ jsxRuntime.jsxs(
731
+ "tr",
732
+ {
733
+ style: {
734
+ background: isSelected ? "var(--accent-soft)" : void 0,
735
+ transition: "background 0.12s"
736
+ },
737
+ children: [
738
+ selectable && /* @__PURE__ */ jsxRuntime.jsx("td", { style: { ...tdStyle, ...checkboxCellStyle }, children: /* @__PURE__ */ jsxRuntime.jsx(
739
+ "input",
740
+ {
741
+ type: "checkbox",
742
+ checked: isSelected,
743
+ onChange: () => toggleRow(rowIndex),
744
+ "aria-label": `Select row ${rowIndex + 1}`,
745
+ style: { cursor: "pointer" }
746
+ }
747
+ ) }),
748
+ columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: tdStyle, children: col.render ? col.render(row, rowIndex) : getCellValue(row, col.key) }, col.key))
749
+ ]
750
+ },
751
+ rowIndex
752
+ );
753
+ }) })
754
+ ] }),
755
+ onPageChange && /* @__PURE__ */ jsxRuntime.jsxs(
756
+ "div",
757
+ {
758
+ style: {
759
+ display: "flex",
760
+ alignItems: "center",
761
+ justifyContent: "flex-end",
762
+ gap: 8,
763
+ padding: "10px 14px",
764
+ borderTop: "1px solid var(--border)"
765
+ },
766
+ children: [
767
+ /* @__PURE__ */ jsxRuntime.jsx(
768
+ PaginationButton,
769
+ {
770
+ label: "Previous",
771
+ onClick: () => onPageChange("prev", cursor)
772
+ }
773
+ ),
774
+ /* @__PURE__ */ jsxRuntime.jsx(
775
+ PaginationButton,
776
+ {
777
+ label: "Next",
778
+ onClick: () => onPageChange("next", cursor)
779
+ }
780
+ )
781
+ ]
782
+ }
783
+ )
784
+ ] });
785
+ }
786
+ function PaginationButton({ label, onClick }) {
787
+ return /* @__PURE__ */ jsxRuntime.jsx(
788
+ "button",
789
+ {
790
+ type: "button",
791
+ onClick,
792
+ style: {
793
+ display: "inline-flex",
794
+ alignItems: "center",
795
+ padding: "6px 14px",
796
+ fontSize: 13,
797
+ fontWeight: 500,
798
+ fontFamily: "var(--font-body)",
799
+ color: "var(--text)",
800
+ background: "var(--surface-alt)",
801
+ border: "1px solid var(--border)",
802
+ borderRadius: "var(--radius-sm, 6px)",
803
+ cursor: "pointer",
804
+ transition: "background 0.15s"
805
+ },
806
+ onMouseEnter: (e) => {
807
+ e.currentTarget.style.background = "var(--surface-hover)";
808
+ },
809
+ onMouseLeave: (e) => {
810
+ e.currentTarget.style.background = "var(--surface-alt)";
811
+ },
812
+ children: label
813
+ }
814
+ );
815
+ }
816
+ function DeltaArrow({ direction }) {
817
+ return /* @__PURE__ */ jsxRuntime.jsx(
818
+ "svg",
819
+ {
820
+ width: 12,
821
+ height: 12,
822
+ viewBox: "0 0 12 12",
823
+ fill: "none",
824
+ "aria-hidden": "true",
825
+ style: {
826
+ transform: direction === "down" ? "rotate(180deg)" : void 0,
827
+ flexShrink: 0
828
+ },
829
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 2.5L10 8.5H2L6 2.5Z", fill: "currentColor" })
830
+ }
831
+ );
832
+ }
833
+ function StatCard({
834
+ label,
835
+ value,
836
+ delta,
837
+ deltaLabel,
838
+ icon,
839
+ className,
840
+ style,
841
+ ...rest
842
+ }) {
843
+ const [hovered, setHovered] = react.useState(false);
844
+ const deltaDirection = delta != null && delta >= 0 ? "up" : "down";
845
+ const deltaColor = delta != null ? delta >= 0 ? "var(--success)" : "var(--error)" : void 0;
846
+ return /* @__PURE__ */ jsxRuntime.jsxs(
847
+ "div",
848
+ {
849
+ className,
850
+ onMouseEnter: () => setHovered(true),
851
+ onMouseLeave: () => setHovered(false),
852
+ style: {
853
+ position: "relative",
854
+ background: "var(--surface)",
855
+ border: "1px solid var(--border)",
856
+ borderRadius: "var(--radius-md, 10px)",
857
+ padding: 20,
858
+ fontFamily: "var(--font-body)",
859
+ boxShadow: hovered ? "var(--shadow-md)" : "var(--shadow)",
860
+ transition: "box-shadow 0.2s ease, transform 0.2s ease",
861
+ transform: hovered ? "translateY(-1px)" : "none",
862
+ cursor: "default",
863
+ ...style
864
+ },
865
+ ...rest,
866
+ children: [
867
+ /* @__PURE__ */ jsxRuntime.jsxs(
868
+ "div",
869
+ {
870
+ style: {
871
+ display: "flex",
872
+ alignItems: "flex-start",
873
+ justifyContent: "space-between"
874
+ },
875
+ children: [
876
+ /* @__PURE__ */ jsxRuntime.jsx(
877
+ "span",
878
+ {
879
+ style: {
880
+ fontSize: 13,
881
+ fontWeight: 500,
882
+ color: "var(--text-secondary)",
883
+ lineHeight: 1.4
884
+ },
885
+ children: label
886
+ }
887
+ ),
888
+ icon && /* @__PURE__ */ jsxRuntime.jsx(
889
+ "span",
890
+ {
891
+ style: {
892
+ color: "var(--text-muted)",
893
+ fontSize: 20,
894
+ lineHeight: 1,
895
+ flexShrink: 0,
896
+ marginLeft: 8
897
+ },
898
+ children: icon
899
+ }
900
+ )
901
+ ]
902
+ }
903
+ ),
904
+ /* @__PURE__ */ jsxRuntime.jsx(
905
+ "div",
906
+ {
907
+ style: {
908
+ marginTop: 8,
909
+ fontSize: 28,
910
+ fontWeight: 700,
911
+ color: "var(--text)",
912
+ fontFamily: "var(--font-display)",
913
+ lineHeight: 1.2
914
+ },
915
+ children: value
916
+ }
917
+ ),
918
+ delta != null && /* @__PURE__ */ jsxRuntime.jsxs(
919
+ "div",
920
+ {
921
+ style: {
922
+ display: "flex",
923
+ alignItems: "center",
924
+ gap: 4,
925
+ marginTop: 8,
926
+ fontSize: 13,
927
+ fontWeight: 500,
928
+ color: deltaColor
929
+ },
930
+ children: [
931
+ /* @__PURE__ */ jsxRuntime.jsx(DeltaArrow, { direction: deltaDirection }),
932
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
933
+ delta > 0 ? "+" : "",
934
+ delta,
935
+ "%"
936
+ ] }),
937
+ deltaLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--text-muted)", marginLeft: 2 }, children: deltaLabel })
938
+ ]
939
+ }
940
+ )
941
+ ]
942
+ }
943
+ );
944
+ }
945
+ var VARIANT_COLORS = {
946
+ success: {
947
+ bg: "var(--successSoft)",
948
+ border: "var(--success)",
949
+ icon: "\u2713"
950
+ },
951
+ error: {
952
+ bg: "var(--errorSoft)",
953
+ border: "var(--error)",
954
+ icon: "\u2717"
955
+ },
956
+ warning: {
957
+ bg: "var(--warningSoft)",
958
+ border: "var(--warning)",
959
+ icon: "\u26A0"
960
+ },
961
+ info: {
962
+ bg: "var(--infoSoft)",
963
+ border: "var(--info)",
964
+ icon: "\u2139"
965
+ }
966
+ };
967
+ function Toast({
968
+ variant = "info",
969
+ message,
970
+ onDismiss,
971
+ className,
972
+ style
973
+ }) {
974
+ const colors = VARIANT_COLORS[variant];
975
+ const toastRef = react.useRef(null);
976
+ react.useEffect(() => {
977
+ const el = toastRef.current;
978
+ if (!el) return;
979
+ requestAnimationFrame(() => {
980
+ el.style.transform = "translateX(0)";
981
+ el.style.opacity = "1";
982
+ });
983
+ }, []);
984
+ const baseStyle2 = {
985
+ display: "flex",
986
+ alignItems: "flex-start",
987
+ gap: 10,
988
+ padding: "12px 16px",
989
+ borderRadius: 10,
990
+ border: `1px solid ${colors.border}`,
991
+ backgroundColor: colors.bg,
992
+ color: "var(--text)",
993
+ fontFamily: "var(--font-body, inherit)",
994
+ fontSize: 14,
995
+ lineHeight: 1.5,
996
+ boxShadow: "var(--shadowMd)",
997
+ pointerEvents: "auto",
998
+ transform: "translateX(120%)",
999
+ opacity: 0,
1000
+ transition: "transform 0.3s ease, opacity 0.3s ease",
1001
+ maxWidth: 400,
1002
+ minWidth: 280,
1003
+ ...style
1004
+ };
1005
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1006
+ "div",
1007
+ {
1008
+ ref: toastRef,
1009
+ role: "alert",
1010
+ "aria-live": "assertive",
1011
+ className,
1012
+ style: baseStyle2,
1013
+ children: [
1014
+ /* @__PURE__ */ jsxRuntime.jsx(
1015
+ "span",
1016
+ {
1017
+ "aria-hidden": "true",
1018
+ style: {
1019
+ flexShrink: 0,
1020
+ fontSize: 16,
1021
+ lineHeight: "20px",
1022
+ color: colors.border
1023
+ },
1024
+ children: colors.icon
1025
+ }
1026
+ ),
1027
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1, wordBreak: "break-word" }, children: message }),
1028
+ /* @__PURE__ */ jsxRuntime.jsx(
1029
+ "button",
1030
+ {
1031
+ type: "button",
1032
+ onClick: onDismiss,
1033
+ "aria-label": "Dismiss notification",
1034
+ style: {
1035
+ flexShrink: 0,
1036
+ background: "none",
1037
+ border: "none",
1038
+ cursor: "pointer",
1039
+ padding: 0,
1040
+ color: "var(--textMuted)",
1041
+ fontSize: 18,
1042
+ lineHeight: "20px"
1043
+ },
1044
+ children: "\\u00D7"
1045
+ }
1046
+ )
1047
+ ]
1048
+ }
1049
+ );
1050
+ }
1051
+ var ToastContext = react.createContext(null);
1052
+ var nextId = 0;
1053
+ function ToastProvider({ children }) {
1054
+ const [toasts, setToasts] = react.useState([]);
1055
+ const timersRef = react.useRef(/* @__PURE__ */ new Map());
1056
+ const dismiss = react.useCallback((id) => {
1057
+ const timer = timersRef.current.get(id);
1058
+ if (timer) {
1059
+ clearTimeout(timer);
1060
+ timersRef.current.delete(id);
1061
+ }
1062
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1063
+ }, []);
1064
+ const toast = react.useCallback(
1065
+ (message, options) => {
1066
+ const id = nextId++;
1067
+ const variant = options?.variant ?? "info";
1068
+ const duration = options?.duration ?? 5e3;
1069
+ setToasts((prev) => [...prev, { id, message, variant, duration }]);
1070
+ if (duration > 0) {
1071
+ const timer = setTimeout(() => {
1072
+ dismiss(id);
1073
+ }, duration);
1074
+ timersRef.current.set(id, timer);
1075
+ }
1076
+ },
1077
+ [dismiss]
1078
+ );
1079
+ react.useEffect(() => {
1080
+ const timers = timersRef.current;
1081
+ return () => {
1082
+ timers.forEach((timer) => clearTimeout(timer));
1083
+ timers.clear();
1084
+ };
1085
+ }, []);
1086
+ const containerStyle = {
1087
+ position: "fixed",
1088
+ bottom: 20,
1089
+ right: 20,
1090
+ display: "flex",
1091
+ flexDirection: "column",
1092
+ gap: 8,
1093
+ zIndex: 9999,
1094
+ pointerEvents: "none"
1095
+ };
1096
+ return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value: { toast }, children: [
1097
+ children,
1098
+ typeof document !== "undefined" && toasts.length > 0 && reactDom.createPortal(
1099
+ /* @__PURE__ */ jsxRuntime.jsx(
1100
+ "div",
1101
+ {
1102
+ "aria-label": "Notifications",
1103
+ style: containerStyle,
1104
+ children: toasts.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
1105
+ Toast,
1106
+ {
1107
+ variant: t.variant,
1108
+ message: t.message,
1109
+ onDismiss: () => dismiss(t.id)
1110
+ },
1111
+ t.id
1112
+ ))
1113
+ }
1114
+ ),
1115
+ document.body
1116
+ )
1117
+ ] });
1118
+ }
1119
+ function useToast() {
1120
+ const ctx = react.useContext(ToastContext);
1121
+ if (!ctx) {
1122
+ throw new Error("useToast must be used within a <ToastProvider>");
1123
+ }
1124
+ return ctx;
1125
+ }
1126
+ var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
1127
+ function Modal({
1128
+ open,
1129
+ onClose,
1130
+ title,
1131
+ children,
1132
+ footer,
1133
+ className,
1134
+ style
1135
+ }) {
1136
+ const panelRef = react.useRef(null);
1137
+ const previousFocusRef = react.useRef(null);
1138
+ react.useEffect(() => {
1139
+ if (open) {
1140
+ previousFocusRef.current = document.activeElement;
1141
+ const raf = requestAnimationFrame(() => {
1142
+ const panel = panelRef.current;
1143
+ if (!panel) return;
1144
+ const first = panel.querySelector(FOCUSABLE_SELECTOR);
1145
+ if (first) {
1146
+ first.focus();
1147
+ } else {
1148
+ panel.focus();
1149
+ }
1150
+ });
1151
+ return () => cancelAnimationFrame(raf);
1152
+ } else {
1153
+ previousFocusRef.current?.focus();
1154
+ }
1155
+ }, [open]);
1156
+ const handleKeyDown = react.useCallback(
1157
+ (e) => {
1158
+ if (e.key === "Escape") {
1159
+ e.stopPropagation();
1160
+ onClose();
1161
+ }
1162
+ },
1163
+ [onClose]
1164
+ );
1165
+ const handleBackdropClick = react.useCallback(
1166
+ (e) => {
1167
+ if (e.target === e.currentTarget) {
1168
+ onClose();
1169
+ }
1170
+ },
1171
+ [onClose]
1172
+ );
1173
+ if (!open || typeof document === "undefined") return null;
1174
+ const backdropStyle = {
1175
+ position: "fixed",
1176
+ inset: 0,
1177
+ display: "flex",
1178
+ alignItems: "center",
1179
+ justifyContent: "center",
1180
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
1181
+ zIndex: 9998,
1182
+ padding: 20
1183
+ };
1184
+ const panelStyle = {
1185
+ position: "relative",
1186
+ backgroundColor: "var(--surface)",
1187
+ color: "var(--text)",
1188
+ borderRadius: 14,
1189
+ border: "1px solid var(--border)",
1190
+ boxShadow: "var(--shadowLg)",
1191
+ fontFamily: "var(--font-body, inherit)",
1192
+ maxWidth: 560,
1193
+ width: "100%",
1194
+ maxHeight: "calc(100vh - 40px)",
1195
+ display: "flex",
1196
+ flexDirection: "column",
1197
+ overflow: "hidden",
1198
+ outline: "none",
1199
+ ...style
1200
+ };
1201
+ const headerStyle = {
1202
+ display: "flex",
1203
+ alignItems: "center",
1204
+ justifyContent: "space-between",
1205
+ padding: "16px 20px",
1206
+ borderBottom: "1px solid var(--border)",
1207
+ flexShrink: 0
1208
+ };
1209
+ const titleStyle = {
1210
+ margin: 0,
1211
+ fontSize: 18,
1212
+ fontWeight: 600,
1213
+ fontFamily: "var(--font-display, inherit)",
1214
+ color: "var(--text)",
1215
+ lineHeight: 1.3
1216
+ };
1217
+ const closeButtonStyle = {
1218
+ background: "none",
1219
+ border: "none",
1220
+ cursor: "pointer",
1221
+ padding: 4,
1222
+ color: "var(--textMuted)",
1223
+ fontSize: 20,
1224
+ lineHeight: 1,
1225
+ borderRadius: 6
1226
+ };
1227
+ const bodyStyle = {
1228
+ padding: 20,
1229
+ flex: 1,
1230
+ overflowY: "auto",
1231
+ fontSize: 14,
1232
+ lineHeight: 1.6,
1233
+ color: "var(--textSecondary)"
1234
+ };
1235
+ const footerStyle = {
1236
+ padding: "12px 20px",
1237
+ borderTop: "1px solid var(--border)",
1238
+ display: "flex",
1239
+ justifyContent: "flex-end",
1240
+ gap: 8,
1241
+ flexShrink: 0
1242
+ };
1243
+ return reactDom.createPortal(
1244
+ /* @__PURE__ */ jsxRuntime.jsx(
1245
+ "div",
1246
+ {
1247
+ role: "presentation",
1248
+ style: backdropStyle,
1249
+ onClick: handleBackdropClick,
1250
+ onKeyDown: handleKeyDown,
1251
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1252
+ "div",
1253
+ {
1254
+ ref: panelRef,
1255
+ role: "dialog",
1256
+ "aria-modal": "true",
1257
+ "aria-label": title ?? "Dialog",
1258
+ tabIndex: -1,
1259
+ className,
1260
+ style: panelStyle,
1261
+ children: [
1262
+ title != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyle, children: [
1263
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: titleStyle, children: title }),
1264
+ /* @__PURE__ */ jsxRuntime.jsx(
1265
+ "button",
1266
+ {
1267
+ type: "button",
1268
+ onClick: onClose,
1269
+ "aria-label": "Close dialog",
1270
+ style: closeButtonStyle,
1271
+ children: "\xD7"
1272
+ }
1273
+ )
1274
+ ] }),
1275
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: bodyStyle, children }),
1276
+ footer != null && /* @__PURE__ */ jsxRuntime.jsx("div", { style: footerStyle, children: footer })
1277
+ ]
1278
+ }
1279
+ )
1280
+ }
1281
+ ),
1282
+ document.body
1283
+ );
1284
+ }
1285
+ function ConfirmDialog({
1286
+ open,
1287
+ onClose,
1288
+ onConfirm,
1289
+ title,
1290
+ description,
1291
+ confirmLabel = "Confirm",
1292
+ cancelLabel = "Cancel",
1293
+ destructive = false,
1294
+ loading = false,
1295
+ className,
1296
+ style
1297
+ }) {
1298
+ const buttonBase = {
1299
+ padding: "8px 16px",
1300
+ borderRadius: 8,
1301
+ fontSize: 14,
1302
+ fontWeight: 500,
1303
+ fontFamily: "var(--font-body, inherit)",
1304
+ cursor: loading ? "not-allowed" : "pointer",
1305
+ border: "1px solid var(--border)",
1306
+ lineHeight: 1.4,
1307
+ transition: "background-color 0.15s ease"
1308
+ };
1309
+ const cancelStyle = {
1310
+ ...buttonBase,
1311
+ backgroundColor: "var(--surface)",
1312
+ color: "var(--text)"
1313
+ };
1314
+ const confirmStyle = {
1315
+ ...buttonBase,
1316
+ backgroundColor: destructive ? "var(--error)" : "var(--accent)",
1317
+ color: destructive ? "#fff" : "var(--accentText)",
1318
+ border: "1px solid transparent",
1319
+ opacity: loading ? 0.7 : 1
1320
+ };
1321
+ const footer = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1322
+ /* @__PURE__ */ jsxRuntime.jsx(
1323
+ "button",
1324
+ {
1325
+ type: "button",
1326
+ onClick: onClose,
1327
+ disabled: loading,
1328
+ style: cancelStyle,
1329
+ children: cancelLabel
1330
+ }
1331
+ ),
1332
+ /* @__PURE__ */ jsxRuntime.jsx(
1333
+ "button",
1334
+ {
1335
+ type: "button",
1336
+ onClick: onConfirm,
1337
+ disabled: loading,
1338
+ "aria-busy": loading || void 0,
1339
+ style: confirmStyle,
1340
+ children: loading ? "Loading\u2026" : confirmLabel
1341
+ }
1342
+ )
1343
+ ] });
1344
+ return /* @__PURE__ */ jsxRuntime.jsx(
1345
+ Modal,
1346
+ {
1347
+ open,
1348
+ onClose,
1349
+ title,
1350
+ footer,
1351
+ className,
1352
+ style,
1353
+ children: description != null && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--textSecondary)", lineHeight: 1.6 }, children: description })
1354
+ }
1355
+ );
1356
+ }
1357
+ var Input = react.forwardRef(
1358
+ ({
1359
+ type = "text",
1360
+ label,
1361
+ helperText,
1362
+ error,
1363
+ iconLeft,
1364
+ iconRight,
1365
+ className,
1366
+ style,
1367
+ disabled,
1368
+ id: idProp,
1369
+ ...rest
1370
+ }, ref) => {
1371
+ const autoId = react.useId();
1372
+ const id = idProp ?? autoId;
1373
+ const helperId = `${id}-helper`;
1374
+ const errorId = `${id}-error`;
1375
+ const hasError = Boolean(error);
1376
+ const errorMessage = typeof error === "string" ? error : void 0;
1377
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1378
+ "div",
1379
+ {
1380
+ className,
1381
+ style: {
1382
+ display: "flex",
1383
+ flexDirection: "column",
1384
+ gap: "6px",
1385
+ ...style
1386
+ },
1387
+ children: [
1388
+ label && /* @__PURE__ */ jsxRuntime.jsx(
1389
+ "label",
1390
+ {
1391
+ htmlFor: id,
1392
+ style: {
1393
+ fontSize: "14px",
1394
+ fontWeight: 500,
1395
+ color: "var(--text)"
1396
+ },
1397
+ children: label
1398
+ }
1399
+ ),
1400
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", display: "flex", alignItems: "center" }, children: [
1401
+ iconLeft && /* @__PURE__ */ jsxRuntime.jsx(
1402
+ "span",
1403
+ {
1404
+ "aria-hidden": "true",
1405
+ style: {
1406
+ position: "absolute",
1407
+ left: "10px",
1408
+ display: "flex",
1409
+ alignItems: "center",
1410
+ color: "var(--text-muted, var(--text))",
1411
+ pointerEvents: "none"
1412
+ },
1413
+ children: iconLeft
1414
+ }
1415
+ ),
1416
+ /* @__PURE__ */ jsxRuntime.jsx(
1417
+ "input",
1418
+ {
1419
+ ref,
1420
+ id,
1421
+ type,
1422
+ disabled,
1423
+ "aria-invalid": hasError || void 0,
1424
+ "aria-describedby": [hasError && errorId, helperText && helperId].filter(Boolean).join(" ") || void 0,
1425
+ style: {
1426
+ width: "100%",
1427
+ padding: "8px 12px",
1428
+ paddingLeft: iconLeft ? "36px" : "12px",
1429
+ paddingRight: iconRight ? "36px" : "12px",
1430
+ fontSize: "14px",
1431
+ lineHeight: "20px",
1432
+ color: "var(--text)",
1433
+ backgroundColor: "var(--surface)",
1434
+ border: `1px solid ${hasError ? "var(--error)" : "var(--border)"}`,
1435
+ borderRadius: "6px",
1436
+ outline: "none",
1437
+ transition: "border-color 0.15s, box-shadow 0.15s",
1438
+ opacity: disabled ? 0.5 : 1,
1439
+ cursor: disabled ? "not-allowed" : void 0,
1440
+ boxSizing: "border-box"
1441
+ },
1442
+ onFocus: (e) => {
1443
+ e.currentTarget.style.borderColor = hasError ? "var(--error)" : "var(--accent)";
1444
+ e.currentTarget.style.boxShadow = `0 0 0 2px ${hasError ? "var(--error)" : "var(--accent)"}33`;
1445
+ rest.onFocus?.(e);
1446
+ },
1447
+ onBlur: (e) => {
1448
+ e.currentTarget.style.borderColor = hasError ? "var(--error)" : "var(--border)";
1449
+ e.currentTarget.style.boxShadow = "none";
1450
+ rest.onBlur?.(e);
1451
+ },
1452
+ ...rest
1453
+ }
1454
+ ),
1455
+ iconRight && /* @__PURE__ */ jsxRuntime.jsx(
1456
+ "span",
1457
+ {
1458
+ "aria-hidden": "true",
1459
+ style: {
1460
+ position: "absolute",
1461
+ right: "10px",
1462
+ display: "flex",
1463
+ alignItems: "center",
1464
+ color: "var(--text-muted, var(--text))",
1465
+ pointerEvents: "none"
1466
+ },
1467
+ children: iconRight
1468
+ }
1469
+ )
1470
+ ] }),
1471
+ errorMessage && /* @__PURE__ */ jsxRuntime.jsx(
1472
+ "span",
1473
+ {
1474
+ id: errorId,
1475
+ role: "alert",
1476
+ style: { fontSize: "12px", color: "var(--error)" },
1477
+ children: errorMessage
1478
+ }
1479
+ ),
1480
+ helperText && !errorMessage && /* @__PURE__ */ jsxRuntime.jsx(
1481
+ "span",
1482
+ {
1483
+ id: helperId,
1484
+ style: {
1485
+ fontSize: "12px",
1486
+ color: "var(--text-muted, var(--text))"
1487
+ },
1488
+ children: helperText
1489
+ }
1490
+ )
1491
+ ]
1492
+ }
1493
+ );
1494
+ }
1495
+ );
1496
+ Input.displayName = "Input";
1497
+ var Select = react.forwardRef(
1498
+ ({
1499
+ label,
1500
+ error,
1501
+ helperText,
1502
+ options,
1503
+ placeholder,
1504
+ className,
1505
+ style,
1506
+ disabled,
1507
+ id: idProp,
1508
+ ...rest
1509
+ }, ref) => {
1510
+ const autoId = react.useId();
1511
+ const id = idProp ?? autoId;
1512
+ const helperId = `${id}-helper`;
1513
+ const errorId = `${id}-error`;
1514
+ const hasError = Boolean(error);
1515
+ const errorMessage = typeof error === "string" ? error : void 0;
1516
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1517
+ "div",
1518
+ {
1519
+ className,
1520
+ style: {
1521
+ display: "flex",
1522
+ flexDirection: "column",
1523
+ gap: "6px",
1524
+ ...style
1525
+ },
1526
+ children: [
1527
+ label && /* @__PURE__ */ jsxRuntime.jsx(
1528
+ "label",
1529
+ {
1530
+ htmlFor: id,
1531
+ style: {
1532
+ fontSize: "14px",
1533
+ fontWeight: 500,
1534
+ color: "var(--text)"
1535
+ },
1536
+ children: label
1537
+ }
1538
+ ),
1539
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", display: "flex", alignItems: "center" }, children: [
1540
+ /* @__PURE__ */ jsxRuntime.jsxs(
1541
+ "select",
1542
+ {
1543
+ ref,
1544
+ id,
1545
+ disabled,
1546
+ "aria-invalid": hasError || void 0,
1547
+ "aria-describedby": [hasError && errorId, helperText && helperId].filter(Boolean).join(" ") || void 0,
1548
+ style: {
1549
+ width: "100%",
1550
+ padding: "8px 32px 8px 12px",
1551
+ fontSize: "14px",
1552
+ lineHeight: "20px",
1553
+ color: "var(--text)",
1554
+ backgroundColor: "var(--surface)",
1555
+ border: `1px solid ${hasError ? "var(--error)" : "var(--border)"}`,
1556
+ borderRadius: "6px",
1557
+ outline: "none",
1558
+ appearance: "none",
1559
+ cursor: disabled ? "not-allowed" : "pointer",
1560
+ opacity: disabled ? 0.5 : 1,
1561
+ transition: "border-color 0.15s, box-shadow 0.15s",
1562
+ boxSizing: "border-box"
1563
+ },
1564
+ onFocus: (e) => {
1565
+ e.currentTarget.style.borderColor = hasError ? "var(--error)" : "var(--accent)";
1566
+ e.currentTarget.style.boxShadow = `0 0 0 2px ${hasError ? "var(--error)" : "var(--accent)"}33`;
1567
+ rest.onFocus?.(e);
1568
+ },
1569
+ onBlur: (e) => {
1570
+ e.currentTarget.style.borderColor = hasError ? "var(--error)" : "var(--border)";
1571
+ e.currentTarget.style.boxShadow = "none";
1572
+ rest.onBlur?.(e);
1573
+ },
1574
+ ...rest,
1575
+ children: [
1576
+ placeholder && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }),
1577
+ options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
1578
+ ]
1579
+ }
1580
+ ),
1581
+ /* @__PURE__ */ jsxRuntime.jsx(
1582
+ "svg",
1583
+ {
1584
+ "aria-hidden": "true",
1585
+ width: "16",
1586
+ height: "16",
1587
+ viewBox: "0 0 16 16",
1588
+ fill: "none",
1589
+ style: {
1590
+ position: "absolute",
1591
+ right: "10px",
1592
+ pointerEvents: "none",
1593
+ color: "var(--text-muted, var(--text))"
1594
+ },
1595
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1596
+ "path",
1597
+ {
1598
+ d: "M4 6l4 4 4-4",
1599
+ stroke: "currentColor",
1600
+ strokeWidth: "1.5",
1601
+ strokeLinecap: "round",
1602
+ strokeLinejoin: "round"
1603
+ }
1604
+ )
1605
+ }
1606
+ )
1607
+ ] }),
1608
+ errorMessage && /* @__PURE__ */ jsxRuntime.jsx(
1609
+ "span",
1610
+ {
1611
+ id: errorId,
1612
+ role: "alert",
1613
+ style: { fontSize: "12px", color: "var(--error)" },
1614
+ children: errorMessage
1615
+ }
1616
+ ),
1617
+ helperText && !errorMessage && /* @__PURE__ */ jsxRuntime.jsx(
1618
+ "span",
1619
+ {
1620
+ id: helperId,
1621
+ style: {
1622
+ fontSize: "12px",
1623
+ color: "var(--text-muted, var(--text))"
1624
+ },
1625
+ children: helperText
1626
+ }
1627
+ )
1628
+ ]
1629
+ }
1630
+ );
1631
+ }
1632
+ );
1633
+ Select.displayName = "Select";
1634
+ var variantStyles2 = {
1635
+ primary: {
1636
+ backgroundColor: "var(--accent)",
1637
+ color: "var(--accent-text, #fff)",
1638
+ border: "1px solid transparent"
1639
+ },
1640
+ secondary: {
1641
+ backgroundColor: "var(--surface)",
1642
+ color: "var(--text)",
1643
+ border: "1px solid var(--border)"
1644
+ },
1645
+ ghost: {
1646
+ backgroundColor: "transparent",
1647
+ color: "var(--text)",
1648
+ border: "1px solid transparent"
1649
+ },
1650
+ danger: {
1651
+ backgroundColor: "var(--error)",
1652
+ color: "var(--error-text, #fff)",
1653
+ border: "1px solid transparent"
1654
+ }
1655
+ };
1656
+ var Spinner = () => /* @__PURE__ */ jsxRuntime.jsxs(
1657
+ "svg",
1658
+ {
1659
+ "aria-hidden": "true",
1660
+ width: "16",
1661
+ height: "16",
1662
+ viewBox: "0 0 16 16",
1663
+ fill: "none",
1664
+ style: {
1665
+ animation: "amber-spin 0.6s linear infinite",
1666
+ flexShrink: 0
1667
+ },
1668
+ children: [
1669
+ /* @__PURE__ */ jsxRuntime.jsx(
1670
+ "circle",
1671
+ {
1672
+ cx: "8",
1673
+ cy: "8",
1674
+ r: "6",
1675
+ stroke: "currentColor",
1676
+ strokeOpacity: "0.25",
1677
+ strokeWidth: "2"
1678
+ }
1679
+ ),
1680
+ /* @__PURE__ */ jsxRuntime.jsx(
1681
+ "path",
1682
+ {
1683
+ d: "M14 8a6 6 0 0 0-6-6",
1684
+ stroke: "currentColor",
1685
+ strokeWidth: "2",
1686
+ strokeLinecap: "round"
1687
+ }
1688
+ )
1689
+ ]
1690
+ }
1691
+ );
1692
+ if (typeof document !== "undefined") {
1693
+ const styleId = "amber-spin-keyframes";
1694
+ if (!document.getElementById(styleId)) {
1695
+ const sheet = document.createElement("style");
1696
+ sheet.id = styleId;
1697
+ sheet.textContent = `@keyframes amber-spin { to { transform: rotate(360deg); } }`;
1698
+ document.head.appendChild(sheet);
1699
+ }
1700
+ }
1701
+ var Button = react.forwardRef(
1702
+ ({
1703
+ variant = "primary",
1704
+ icon,
1705
+ loading = false,
1706
+ disabled,
1707
+ children,
1708
+ className,
1709
+ style,
1710
+ type = "button",
1711
+ ...rest
1712
+ }, ref) => {
1713
+ const isDisabled = disabled || loading;
1714
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1715
+ "button",
1716
+ {
1717
+ ref,
1718
+ type,
1719
+ disabled: isDisabled,
1720
+ className,
1721
+ "aria-busy": loading || void 0,
1722
+ style: {
1723
+ display: "inline-flex",
1724
+ alignItems: "center",
1725
+ justifyContent: "center",
1726
+ gap: "8px",
1727
+ padding: "8px 16px",
1728
+ fontSize: "14px",
1729
+ fontWeight: 500,
1730
+ lineHeight: "20px",
1731
+ borderRadius: "6px",
1732
+ cursor: isDisabled ? "not-allowed" : "pointer",
1733
+ opacity: isDisabled ? 0.5 : 1,
1734
+ transition: "background-color 0.15s, opacity 0.15s",
1735
+ outline: "none",
1736
+ textDecoration: "none",
1737
+ whiteSpace: "nowrap",
1738
+ boxSizing: "border-box",
1739
+ ...variantStyles2[variant],
1740
+ ...style
1741
+ },
1742
+ onFocus: (e) => {
1743
+ e.currentTarget.style.boxShadow = "0 0 0 2px var(--accent)33";
1744
+ rest.onFocus?.(e);
1745
+ },
1746
+ onBlur: (e) => {
1747
+ e.currentTarget.style.boxShadow = "none";
1748
+ rest.onBlur?.(e);
1749
+ },
1750
+ ...rest,
1751
+ children: [
1752
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: { display: "flex", alignItems: "center", flexShrink: 0 }, children: icon }) : null,
1753
+ children
1754
+ ]
1755
+ }
1756
+ );
1757
+ }
1758
+ );
1759
+ Button.displayName = "Button";
1760
+ var TRACK_W = 40;
1761
+ var TRACK_H = 22;
1762
+ var THUMB_SIZE = 16;
1763
+ var THUMB_OFFSET = 3;
1764
+ var Toggle = ({
1765
+ checked,
1766
+ onChange,
1767
+ label,
1768
+ disabled = false,
1769
+ className,
1770
+ style
1771
+ }) => {
1772
+ const autoId = react.useId();
1773
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1774
+ "div",
1775
+ {
1776
+ className,
1777
+ style: {
1778
+ display: "inline-flex",
1779
+ alignItems: "center",
1780
+ gap: "8px",
1781
+ opacity: disabled ? 0.5 : 1,
1782
+ cursor: disabled ? "not-allowed" : "pointer",
1783
+ ...style
1784
+ },
1785
+ children: [
1786
+ /* @__PURE__ */ jsxRuntime.jsx(
1787
+ "button",
1788
+ {
1789
+ id: autoId,
1790
+ type: "button",
1791
+ role: "switch",
1792
+ "aria-checked": checked,
1793
+ "aria-label": label ?? void 0,
1794
+ disabled,
1795
+ onClick: () => onChange(!checked),
1796
+ style: {
1797
+ position: "relative",
1798
+ width: TRACK_W,
1799
+ height: TRACK_H,
1800
+ borderRadius: TRACK_H / 2,
1801
+ backgroundColor: checked ? "var(--accent)" : "var(--border)",
1802
+ border: "none",
1803
+ padding: 0,
1804
+ cursor: disabled ? "not-allowed" : "pointer",
1805
+ transition: "background-color 0.2s",
1806
+ outline: "none",
1807
+ flexShrink: 0
1808
+ },
1809
+ onFocus: (e) => {
1810
+ e.currentTarget.style.boxShadow = "0 0 0 2px var(--accent)33";
1811
+ },
1812
+ onBlur: (e) => {
1813
+ e.currentTarget.style.boxShadow = "none";
1814
+ },
1815
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1816
+ "span",
1817
+ {
1818
+ "aria-hidden": "true",
1819
+ style: {
1820
+ position: "absolute",
1821
+ top: THUMB_OFFSET,
1822
+ left: checked ? TRACK_W - THUMB_SIZE - THUMB_OFFSET : THUMB_OFFSET,
1823
+ width: THUMB_SIZE,
1824
+ height: THUMB_SIZE,
1825
+ borderRadius: "50%",
1826
+ backgroundColor: "#fff",
1827
+ transition: "left 0.2s",
1828
+ boxShadow: "0 1px 3px rgba(0,0,0,0.2)"
1829
+ }
1830
+ }
1831
+ )
1832
+ }
1833
+ ),
1834
+ label && /* @__PURE__ */ jsxRuntime.jsx(
1835
+ "label",
1836
+ {
1837
+ htmlFor: autoId,
1838
+ style: {
1839
+ fontSize: "14px",
1840
+ color: "var(--text)",
1841
+ cursor: disabled ? "not-allowed" : "pointer",
1842
+ userSelect: "none"
1843
+ },
1844
+ children: label
1845
+ }
1846
+ )
1847
+ ]
1848
+ }
1849
+ );
1850
+ };
1851
+ Toggle.displayName = "Toggle";
1852
+ var SIZE = 18;
1853
+ var Checkbox = ({
1854
+ checked,
1855
+ onChange,
1856
+ label,
1857
+ disabled = false,
1858
+ indeterminate = false,
1859
+ className,
1860
+ style
1861
+ }) => {
1862
+ const autoId = react.useId();
1863
+ const inputRef = react.useRef(null);
1864
+ react.useEffect(() => {
1865
+ if (inputRef.current) {
1866
+ inputRef.current.indeterminate = indeterminate;
1867
+ }
1868
+ }, [indeterminate]);
1869
+ const isActive = checked || indeterminate;
1870
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1871
+ "div",
1872
+ {
1873
+ className,
1874
+ style: {
1875
+ display: "inline-flex",
1876
+ alignItems: "center",
1877
+ gap: "8px",
1878
+ opacity: disabled ? 0.5 : 1,
1879
+ cursor: disabled ? "not-allowed" : "pointer",
1880
+ ...style
1881
+ },
1882
+ children: [
1883
+ /* @__PURE__ */ jsxRuntime.jsxs(
1884
+ "span",
1885
+ {
1886
+ style: {
1887
+ position: "relative",
1888
+ display: "inline-flex",
1889
+ alignItems: "center",
1890
+ justifyContent: "center",
1891
+ width: SIZE,
1892
+ height: SIZE,
1893
+ flexShrink: 0
1894
+ },
1895
+ children: [
1896
+ /* @__PURE__ */ jsxRuntime.jsx(
1897
+ "input",
1898
+ {
1899
+ ref: inputRef,
1900
+ id: autoId,
1901
+ type: "checkbox",
1902
+ checked,
1903
+ disabled,
1904
+ onChange: (e) => onChange(e.target.checked),
1905
+ style: {
1906
+ position: "absolute",
1907
+ width: SIZE,
1908
+ height: SIZE,
1909
+ margin: 0,
1910
+ opacity: 0,
1911
+ cursor: disabled ? "not-allowed" : "pointer"
1912
+ }
1913
+ }
1914
+ ),
1915
+ /* @__PURE__ */ jsxRuntime.jsxs(
1916
+ "span",
1917
+ {
1918
+ "aria-hidden": "true",
1919
+ style: {
1920
+ width: SIZE,
1921
+ height: SIZE,
1922
+ borderRadius: "4px",
1923
+ border: `1.5px solid ${isActive ? "var(--accent)" : "var(--border)"}`,
1924
+ backgroundColor: isActive ? "var(--accent)" : "var(--surface)",
1925
+ display: "flex",
1926
+ alignItems: "center",
1927
+ justifyContent: "center",
1928
+ transition: "background-color 0.15s, border-color 0.15s",
1929
+ pointerEvents: "none"
1930
+ },
1931
+ children: [
1932
+ checked && !indeterminate && /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
1933
+ "path",
1934
+ {
1935
+ d: "M2.5 6l2.5 2.5 4.5-5",
1936
+ stroke: "var(--accent-text, #fff)",
1937
+ strokeWidth: "1.5",
1938
+ strokeLinecap: "round",
1939
+ strokeLinejoin: "round"
1940
+ }
1941
+ ) }),
1942
+ indeterminate && /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
1943
+ "path",
1944
+ {
1945
+ d: "M3 6h6",
1946
+ stroke: "var(--accent-text, #fff)",
1947
+ strokeWidth: "1.5",
1948
+ strokeLinecap: "round"
1949
+ }
1950
+ ) })
1951
+ ]
1952
+ }
1953
+ )
1954
+ ]
1955
+ }
1956
+ ),
1957
+ label && /* @__PURE__ */ jsxRuntime.jsx(
1958
+ "label",
1959
+ {
1960
+ htmlFor: autoId,
1961
+ style: {
1962
+ fontSize: "14px",
1963
+ color: "var(--text)",
1964
+ cursor: disabled ? "not-allowed" : "pointer",
1965
+ userSelect: "none"
1966
+ },
1967
+ children: label
1968
+ }
1969
+ )
1970
+ ]
1971
+ }
1972
+ );
1973
+ };
1974
+ Checkbox.displayName = "Checkbox";
1975
+ var barStyle2 = {
1976
+ display: "flex",
1977
+ gap: 4,
1978
+ borderBottom: "1px solid var(--border)",
1979
+ background: "var(--surface)",
1980
+ position: "relative",
1981
+ overflowX: "auto"
1982
+ };
1983
+ var baseTabStyle = {
1984
+ position: "relative",
1985
+ padding: "10px 16px",
1986
+ fontSize: 14,
1987
+ fontWeight: 500,
1988
+ fontFamily: "inherit",
1989
+ lineHeight: "20px",
1990
+ color: "var(--text-secondary)",
1991
+ background: "none",
1992
+ border: "none",
1993
+ borderBottom: "2px solid transparent",
1994
+ cursor: "pointer",
1995
+ whiteSpace: "nowrap",
1996
+ transition: "color 150ms, border-color 150ms",
1997
+ outline: "none"
1998
+ };
1999
+ var activeTabStyle = {
2000
+ color: "var(--accent)",
2001
+ borderBottomColor: "var(--accent)"
2002
+ };
2003
+ var disabledTabStyle = {
2004
+ color: "var(--text-muted)",
2005
+ cursor: "not-allowed",
2006
+ opacity: 0.6
2007
+ };
2008
+ var focusVisibleOutline = {
2009
+ outline: "2px solid var(--accent)",
2010
+ outlineOffset: -2,
2011
+ borderRadius: 4
2012
+ };
2013
+ function Tabs({ tabs, activeKey, onChange, className, style }) {
2014
+ const tabRefs = react.useRef([]);
2015
+ const enabledIndices = tabs.reduce((acc, tab, i) => {
2016
+ if (!tab.disabled) acc.push(i);
2017
+ return acc;
2018
+ }, []);
2019
+ function focusTab(index) {
2020
+ tabRefs.current[index]?.focus();
2021
+ }
2022
+ function handleKeyDown(e) {
2023
+ const target = e.target;
2024
+ const currentIndex = tabRefs.current.indexOf(target);
2025
+ if (currentIndex === -1) return;
2026
+ const pos = enabledIndices.indexOf(currentIndex);
2027
+ let nextIndex;
2028
+ switch (e.key) {
2029
+ case "ArrowRight": {
2030
+ const next = pos < enabledIndices.length - 1 ? pos + 1 : 0;
2031
+ nextIndex = enabledIndices[next];
2032
+ break;
2033
+ }
2034
+ case "ArrowLeft": {
2035
+ const prev = pos > 0 ? pos - 1 : enabledIndices.length - 1;
2036
+ nextIndex = enabledIndices[prev];
2037
+ break;
2038
+ }
2039
+ case "Home":
2040
+ nextIndex = enabledIndices[0];
2041
+ break;
2042
+ case "End":
2043
+ nextIndex = enabledIndices[enabledIndices.length - 1];
2044
+ break;
2045
+ default:
2046
+ return;
2047
+ }
2048
+ if (nextIndex !== void 0) {
2049
+ e.preventDefault();
2050
+ focusTab(nextIndex);
2051
+ }
2052
+ }
2053
+ return /* @__PURE__ */ jsxRuntime.jsx(
2054
+ "div",
2055
+ {
2056
+ role: "tablist",
2057
+ className,
2058
+ style: { ...barStyle2, ...style },
2059
+ onKeyDown: handleKeyDown,
2060
+ children: tabs.map((tab, i) => {
2061
+ const isActive = tab.key === activeKey;
2062
+ const isDisabled = !!tab.disabled;
2063
+ return /* @__PURE__ */ jsxRuntime.jsx(
2064
+ "button",
2065
+ {
2066
+ ref: (el) => {
2067
+ tabRefs.current[i] = el;
2068
+ },
2069
+ role: "tab",
2070
+ id: `tab-${tab.key}`,
2071
+ "aria-selected": isActive,
2072
+ "aria-disabled": isDisabled || void 0,
2073
+ "aria-controls": `tabpanel-${tab.key}`,
2074
+ tabIndex: isActive ? 0 : -1,
2075
+ disabled: isDisabled,
2076
+ onClick: () => {
2077
+ if (!isDisabled) onChange(tab.key);
2078
+ },
2079
+ onFocus: (e) => {
2080
+ Object.assign(e.currentTarget.style, focusVisibleOutline);
2081
+ },
2082
+ onBlur: (e) => {
2083
+ e.currentTarget.style.outline = "none";
2084
+ e.currentTarget.style.outlineOffset = "";
2085
+ e.currentTarget.style.borderRadius = "";
2086
+ },
2087
+ style: {
2088
+ ...baseTabStyle,
2089
+ ...isActive ? activeTabStyle : void 0,
2090
+ ...isDisabled ? disabledTabStyle : void 0
2091
+ },
2092
+ children: tab.label
2093
+ },
2094
+ tab.key
2095
+ );
2096
+ })
2097
+ }
2098
+ );
2099
+ }
2100
+ var wrapperStyle = {
2101
+ position: "relative",
2102
+ display: "inline-block"
2103
+ };
2104
+ var triggerBtnStyle = {
2105
+ background: "none",
2106
+ border: "none",
2107
+ padding: 0,
2108
+ cursor: "pointer",
2109
+ font: "inherit",
2110
+ color: "inherit",
2111
+ display: "inline-flex",
2112
+ alignItems: "center"
2113
+ };
2114
+ var menuBase = {
2115
+ position: "absolute",
2116
+ top: "calc(100% + 4px)",
2117
+ zIndex: 1e3,
2118
+ minWidth: 180,
2119
+ padding: "4px 0",
2120
+ background: "var(--surface)",
2121
+ border: "1px solid var(--border)",
2122
+ borderRadius: 10,
2123
+ boxShadow: "var(--shadow-md)",
2124
+ listStyle: "none",
2125
+ margin: 0,
2126
+ outline: "none"
2127
+ };
2128
+ var itemBase = {
2129
+ display: "flex",
2130
+ alignItems: "center",
2131
+ gap: 8,
2132
+ width: "100%",
2133
+ padding: "8px 12px",
2134
+ fontSize: 14,
2135
+ lineHeight: "20px",
2136
+ fontFamily: "inherit",
2137
+ color: "var(--text)",
2138
+ background: "none",
2139
+ border: "none",
2140
+ cursor: "pointer",
2141
+ textAlign: "left",
2142
+ whiteSpace: "nowrap",
2143
+ outline: "none"
2144
+ };
2145
+ var itemDisabled = {
2146
+ color: "var(--text-muted)",
2147
+ cursor: "not-allowed",
2148
+ opacity: 0.5
2149
+ };
2150
+ var itemDanger = {
2151
+ color: "var(--error)"
2152
+ };
2153
+ var itemHighlighted = {
2154
+ background: "var(--surface-hover)"
2155
+ };
2156
+ var itemIconStyle = {
2157
+ display: "inline-flex",
2158
+ flexShrink: 0,
2159
+ width: 16,
2160
+ height: 16,
2161
+ alignItems: "center",
2162
+ justifyContent: "center"
2163
+ };
2164
+ function Dropdown({
2165
+ trigger,
2166
+ items,
2167
+ onSelect,
2168
+ align = "left",
2169
+ className,
2170
+ style
2171
+ }) {
2172
+ const [open, setOpen] = react.useState(false);
2173
+ const [highlightIndex, setHighlightIndex] = react.useState(-1);
2174
+ const wrapperRef = react.useRef(null);
2175
+ const menuRef = react.useRef(null);
2176
+ const itemRefs = react.useRef([]);
2177
+ const enabledIndices = items.reduce((acc, item, i) => {
2178
+ if (!item.disabled) acc.push(i);
2179
+ return acc;
2180
+ }, []);
2181
+ const openMenu = react.useCallback(() => {
2182
+ setOpen(true);
2183
+ setHighlightIndex(enabledIndices[0] ?? -1);
2184
+ }, [enabledIndices]);
2185
+ const closeMenu = react.useCallback(() => {
2186
+ setOpen(false);
2187
+ setHighlightIndex(-1);
2188
+ }, []);
2189
+ react.useEffect(() => {
2190
+ if (!open) return;
2191
+ function handleClick(e) {
2192
+ if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
2193
+ closeMenu();
2194
+ }
2195
+ }
2196
+ document.addEventListener("mousedown", handleClick);
2197
+ return () => document.removeEventListener("mousedown", handleClick);
2198
+ }, [open, closeMenu]);
2199
+ react.useEffect(() => {
2200
+ if (open) {
2201
+ menuRef.current?.focus();
2202
+ }
2203
+ }, [open]);
2204
+ react.useEffect(() => {
2205
+ if (highlightIndex >= 0) {
2206
+ itemRefs.current[highlightIndex]?.scrollIntoView({ block: "nearest" });
2207
+ }
2208
+ }, [highlightIndex]);
2209
+ function handleMenuKeyDown(e) {
2210
+ switch (e.key) {
2211
+ case "ArrowDown": {
2212
+ e.preventDefault();
2213
+ const pos = enabledIndices.indexOf(highlightIndex);
2214
+ const next = pos < enabledIndices.length - 1 ? pos + 1 : 0;
2215
+ setHighlightIndex(enabledIndices[next]);
2216
+ break;
2217
+ }
2218
+ case "ArrowUp": {
2219
+ e.preventDefault();
2220
+ const pos = enabledIndices.indexOf(highlightIndex);
2221
+ const prev = pos > 0 ? pos - 1 : enabledIndices.length - 1;
2222
+ setHighlightIndex(enabledIndices[prev]);
2223
+ break;
2224
+ }
2225
+ case "Home":
2226
+ e.preventDefault();
2227
+ setHighlightIndex(enabledIndices[0]);
2228
+ break;
2229
+ case "End":
2230
+ e.preventDefault();
2231
+ setHighlightIndex(enabledIndices[enabledIndices.length - 1]);
2232
+ break;
2233
+ case "Enter":
2234
+ case " ": {
2235
+ e.preventDefault();
2236
+ const item = items[highlightIndex];
2237
+ if (item && !item.disabled) {
2238
+ onSelect(item.key);
2239
+ closeMenu();
2240
+ }
2241
+ break;
2242
+ }
2243
+ case "Escape":
2244
+ e.preventDefault();
2245
+ closeMenu();
2246
+ break;
2247
+ case "Tab":
2248
+ closeMenu();
2249
+ break;
2250
+ default: {
2251
+ if (e.key.length === 1) {
2252
+ const char = e.key.toLowerCase();
2253
+ const match = enabledIndices.find(
2254
+ (i) => items[i].label.toLowerCase().startsWith(char)
2255
+ );
2256
+ if (match !== void 0) setHighlightIndex(match);
2257
+ }
2258
+ }
2259
+ }
2260
+ }
2261
+ function handleTriggerKeyDown(e) {
2262
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
2263
+ e.preventDefault();
2264
+ if (!open) openMenu();
2265
+ }
2266
+ }
2267
+ const menuAlignStyle = align === "right" ? { right: 0 } : { left: 0 };
2268
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2269
+ "div",
2270
+ {
2271
+ ref: wrapperRef,
2272
+ className,
2273
+ style: { ...wrapperStyle, ...style },
2274
+ children: [
2275
+ /* @__PURE__ */ jsxRuntime.jsx(
2276
+ "button",
2277
+ {
2278
+ type: "button",
2279
+ "aria-haspopup": "true",
2280
+ "aria-expanded": open,
2281
+ onClick: () => open ? closeMenu() : openMenu(),
2282
+ onKeyDown: handleTriggerKeyDown,
2283
+ style: triggerBtnStyle,
2284
+ children: trigger
2285
+ }
2286
+ ),
2287
+ open && /* @__PURE__ */ jsxRuntime.jsx(
2288
+ "ul",
2289
+ {
2290
+ ref: menuRef,
2291
+ role: "menu",
2292
+ tabIndex: -1,
2293
+ onKeyDown: handleMenuKeyDown,
2294
+ style: { ...menuBase, ...menuAlignStyle },
2295
+ children: items.map((item, i) => {
2296
+ const isHighlighted = i === highlightIndex;
2297
+ const isDisabled = !!item.disabled;
2298
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2299
+ "li",
2300
+ {
2301
+ ref: (el) => {
2302
+ itemRefs.current[i] = el;
2303
+ },
2304
+ role: "menuitem",
2305
+ "aria-disabled": isDisabled || void 0,
2306
+ onMouseEnter: () => {
2307
+ if (!isDisabled) setHighlightIndex(i);
2308
+ },
2309
+ onMouseLeave: () => setHighlightIndex(-1),
2310
+ onClick: () => {
2311
+ if (!isDisabled) {
2312
+ onSelect(item.key);
2313
+ closeMenu();
2314
+ }
2315
+ },
2316
+ style: {
2317
+ ...itemBase,
2318
+ ...item.danger ? itemDanger : void 0,
2319
+ ...isDisabled ? itemDisabled : void 0,
2320
+ ...isHighlighted && !isDisabled ? itemHighlighted : void 0
2321
+ },
2322
+ children: [
2323
+ item.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { style: itemIconStyle, children: item.icon }),
2324
+ item.label
2325
+ ]
2326
+ },
2327
+ item.key
2328
+ );
2329
+ })
2330
+ }
2331
+ )
2332
+ ]
2333
+ }
2334
+ );
2335
+ }
2336
+
2337
+ exports.AppShell = AppShell;
2338
+ exports.Badge = Badge;
2339
+ exports.Button = Button;
2340
+ exports.Checkbox = Checkbox;
2341
+ exports.ConfirmDialog = ConfirmDialog;
2342
+ exports.DataTable = DataTable;
2343
+ exports.Dropdown = Dropdown;
2344
+ exports.EmptyState = EmptyState;
2345
+ exports.Header = Header;
2346
+ exports.Input = Input;
2347
+ exports.Modal = Modal;
2348
+ exports.Select = Select;
2349
+ exports.Sidebar = Sidebar;
2350
+ exports.SidebarSection = SidebarSection;
2351
+ exports.Skeleton = Skeleton;
2352
+ exports.StatCard = StatCard;
2353
+ exports.Tabs = Tabs;
2354
+ exports.Toast = Toast;
2355
+ exports.ToastProvider = ToastProvider;
2356
+ exports.Toggle = Toggle;
2357
+ exports.useToast = useToast;
2358
+ //# sourceMappingURL=chunk-2NDCSOJ6.js.map
2359
+ //# sourceMappingURL=chunk-2NDCSOJ6.js.map