@crimson_/altarev 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2951 @@
1
+ "use client";
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
5
+ import { cva } from 'class-variance-authority';
6
+ import { createContext, useRef, useState, useId, useEffect, cloneElement, Fragment, Children, useCallback, isValidElement, useContext, useMemo } from 'react';
7
+ import { createPortal } from 'react-dom';
8
+ import { DayPicker } from 'react-day-picker';
9
+
10
+ // src/lib/cn.ts
11
+ function cn(...inputs) {
12
+ return twMerge(clsx(inputs));
13
+ }
14
+ function Container({ className, ...props }) {
15
+ return /* @__PURE__ */ jsx(
16
+ "div",
17
+ {
18
+ className: cn(
19
+ "mx-auto w-full max-w-container px-4 md:px-6 lg:px-8",
20
+ className
21
+ ),
22
+ ...props
23
+ }
24
+ );
25
+ }
26
+ var accordion = cva(
27
+ "group rounded-[8px] bg-surface text-text border border-border [&[open]>summary>svg]:rotate-180",
28
+ {
29
+ variants: {
30
+ size: {
31
+ sm: "text-xs",
32
+ md: "text-sm",
33
+ lg: "text-sm",
34
+ xl: "text-md"
35
+ }
36
+ },
37
+ defaultVariants: { size: "md" }
38
+ }
39
+ );
40
+ var PAD = { sm: "p-2", md: "px-3 py-2", lg: "p-3", xl: "p-4" };
41
+ var summary = cva(
42
+ "flex cursor-pointer list-none items-center gap-2 font-bold marker:hidden outline-none focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2",
43
+ {
44
+ variants: { size: PAD },
45
+ defaultVariants: { size: "md" }
46
+ }
47
+ );
48
+ function Accordion({
49
+ size = "md",
50
+ title,
51
+ leading,
52
+ children,
53
+ className,
54
+ ...props
55
+ }) {
56
+ return /* @__PURE__ */ jsxs("details", { className: cn(accordion({ size }), className), ...props, children: [
57
+ /* @__PURE__ */ jsxs("summary", { className: summary({ size }), children: [
58
+ leading,
59
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: title }),
60
+ /* @__PURE__ */ jsx(ChevronDown, {})
61
+ ] }),
62
+ /* @__PURE__ */ jsx("div", { className: "border-t border-border" }),
63
+ /* @__PURE__ */ jsx("div", { className: PAD[size ?? "md"], children })
64
+ ] });
65
+ }
66
+ function ChevronDown() {
67
+ return /* @__PURE__ */ jsx(
68
+ "svg",
69
+ {
70
+ className: "size-6 shrink-0 transition-transform duration-200 ease-in-out",
71
+ viewBox: "0 0 24 24",
72
+ fill: "none",
73
+ stroke: "currentColor",
74
+ strokeWidth: "2",
75
+ strokeLinecap: "round",
76
+ strokeLinejoin: "round",
77
+ "aria-hidden": true,
78
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
79
+ }
80
+ );
81
+ }
82
+ var alert = cva(
83
+ "flex items-center gap-3 rounded-[8px] p-4 text-sm [&>svg]:size-6 [&>svg]:shrink-0",
84
+ {
85
+ variants: {
86
+ status: {
87
+ success: "",
88
+ error: "",
89
+ warning: "",
90
+ info: ""
91
+ },
92
+ styleVariant: {
93
+ colourful: "",
94
+ outline: "border",
95
+ generic: "bg-surface text-text"
96
+ }
97
+ },
98
+ compoundVariants: [
99
+ {
100
+ styleVariant: "colourful",
101
+ status: "success",
102
+ class: "bg-success-tint text-success"
103
+ },
104
+ {
105
+ styleVariant: "colourful",
106
+ status: "error",
107
+ class: "bg-error-tint text-error"
108
+ },
109
+ {
110
+ styleVariant: "colourful",
111
+ status: "warning",
112
+ class: "bg-warning-tint text-warning"
113
+ },
114
+ {
115
+ styleVariant: "colourful",
116
+ status: "info",
117
+ class: "bg-info-tint text-info"
118
+ },
119
+ {
120
+ styleVariant: "outline",
121
+ status: "success",
122
+ class: "border-success text-success"
123
+ },
124
+ {
125
+ styleVariant: "outline",
126
+ status: "error",
127
+ class: "border-error text-error"
128
+ },
129
+ {
130
+ styleVariant: "outline",
131
+ status: "warning",
132
+ class: "border-warning text-warning"
133
+ },
134
+ {
135
+ styleVariant: "outline",
136
+ status: "info",
137
+ class: "border-info text-info"
138
+ }
139
+ ],
140
+ defaultVariants: { status: "info", styleVariant: "generic" }
141
+ }
142
+ );
143
+ function Alert({
144
+ status = "info",
145
+ variant = "generic",
146
+ icon,
147
+ trailing,
148
+ children,
149
+ className,
150
+ ...props
151
+ }) {
152
+ return /* @__PURE__ */ jsxs(
153
+ "div",
154
+ {
155
+ role: "alert",
156
+ className: cn(alert({ status, styleVariant: variant }), className),
157
+ ...props,
158
+ children: [
159
+ icon,
160
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children }),
161
+ trailing
162
+ ]
163
+ }
164
+ );
165
+ }
166
+ function EmptyState({
167
+ icon,
168
+ title,
169
+ description,
170
+ children,
171
+ className,
172
+ ...props
173
+ }) {
174
+ return /* @__PURE__ */ jsxs(
175
+ "div",
176
+ {
177
+ className: cn(
178
+ "flex w-[374px] max-w-full flex-col items-center gap-2 px-4 text-center",
179
+ className
180
+ ),
181
+ ...props,
182
+ children: [
183
+ icon && /* @__PURE__ */ jsx("div", { className: "flex size-20 items-center justify-center [&_svg]:size-20", children: icon }),
184
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 pb-6 text-text", children: [
185
+ /* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold leading-8", children: title }),
186
+ description && /* @__PURE__ */ jsx("p", { className: "text-base leading-6 text-text", children: description })
187
+ ] }),
188
+ children && /* @__PURE__ */ jsx("div", { className: "flex items-start justify-center gap-3", children })
189
+ ]
190
+ }
191
+ );
192
+ }
193
+ var avatar = cva(
194
+ "relative inline-flex shrink-0 items-center justify-center overflow-visible rounded-full bg-surface font-bold text-text select-none",
195
+ {
196
+ variants: {
197
+ size: {
198
+ xs: "size-6 text-2xs",
199
+ sm: "size-8 text-xs",
200
+ md: "size-10 text-sm",
201
+ lg: "size-12 text-md",
202
+ xl: "size-14 text-md",
203
+ "2xl": "size-16 text-xl"
204
+ }
205
+ },
206
+ defaultVariants: { size: "md" }
207
+ }
208
+ );
209
+ var badgeCorner = {
210
+ tr: "top-0 right-0",
211
+ tl: "top-0 left-0",
212
+ br: "bottom-0 right-0",
213
+ bl: "bottom-0 left-0"
214
+ };
215
+ var badgeSize = {
216
+ xs: "size-1 border-2",
217
+ sm: "size-2 border-2",
218
+ md: "size-2 border-2",
219
+ lg: "size-3 border-4",
220
+ xl: "size-3 border-4",
221
+ "2xl": "size-3 border-4"
222
+ };
223
+ function Avatar({
224
+ size = "md",
225
+ src,
226
+ alt = "",
227
+ children,
228
+ badge,
229
+ badgeColor = "bg-success",
230
+ className,
231
+ ...props
232
+ }) {
233
+ return /* @__PURE__ */ jsxs("span", { className: cn(avatar({ size }), className), ...props, children: [
234
+ src ? /* @__PURE__ */ jsx(
235
+ "img",
236
+ {
237
+ src,
238
+ alt,
239
+ className: "absolute inset-0 size-full rounded-full object-cover"
240
+ }
241
+ ) : children,
242
+ badge && /* @__PURE__ */ jsx(
243
+ "span",
244
+ {
245
+ className: cn(
246
+ "absolute rounded-full border-background",
247
+ badgeCorner[badge],
248
+ badgeSize[size ?? "md"],
249
+ badgeColor
250
+ )
251
+ }
252
+ )
253
+ ] });
254
+ }
255
+ var cell = cva(
256
+ "size-14 w-12 rounded-[8px] border bg-surface text-center text-sm text-text outline-none transition-shadow focus:border-[1.5px] focus:border-primary focus:ring-2 focus:ring-primary-tint",
257
+ {
258
+ variants: {
259
+ error: {
260
+ true: "border-[1.5px] border-[var(--color-input-error)] text-[var(--color-input-error)] focus:border-[var(--color-input-error)] focus:ring-0",
261
+ false: "border-border"
262
+ }
263
+ },
264
+ defaultVariants: { error: false }
265
+ }
266
+ );
267
+ function splitCode(raw, length) {
268
+ const digits = raw.replace(/\D/g, "").slice(0, length).split("");
269
+ return Array.from({ length }, (_, i) => digits[i] ?? "");
270
+ }
271
+ function AuthCode({
272
+ value,
273
+ onChange,
274
+ length = 6,
275
+ error = false,
276
+ disabled,
277
+ className
278
+ }) {
279
+ const refs = useRef([]);
280
+ const cells = splitCode(value, length);
281
+ const hasError = Boolean(error);
282
+ const setCharAt = (index, char) => {
283
+ const next = cells.slice();
284
+ next[index] = char;
285
+ onChange(next.join(""));
286
+ };
287
+ const focusCell = (index) => {
288
+ const el = refs.current[Math.max(0, Math.min(index, length - 1))];
289
+ el?.focus();
290
+ el?.select();
291
+ };
292
+ const handleInput = (index, raw) => {
293
+ const char = raw.replace(/\D/g, "").slice(-1);
294
+ if (!char) return;
295
+ setCharAt(index, char);
296
+ if (index < length - 1) focusCell(index + 1);
297
+ };
298
+ const handleKeyDown = (index, e) => {
299
+ if (e.key === "Backspace") {
300
+ e.preventDefault();
301
+ if (cells[index]) {
302
+ setCharAt(index, "");
303
+ } else if (index > 0) {
304
+ setCharAt(index - 1, "");
305
+ focusCell(index - 1);
306
+ }
307
+ } else if (e.key === "ArrowLeft") {
308
+ focusCell(index - 1);
309
+ } else if (e.key === "ArrowRight") {
310
+ focusCell(index + 1);
311
+ }
312
+ };
313
+ const handlePaste = (index, e) => {
314
+ e.preventDefault();
315
+ const pasted = e.clipboardData.getData("text").replace(/\D/g, "");
316
+ if (!pasted) return;
317
+ const next = cells.slice();
318
+ for (let i = 0; i < pasted.length && index + i < length; i++) {
319
+ next[index + i] = pasted[i];
320
+ }
321
+ onChange(next.join(""));
322
+ focusCell(index + pasted.length);
323
+ };
324
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-2", className), children: [
325
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: cells.map((char, i) => /* @__PURE__ */ jsx(
326
+ "input",
327
+ {
328
+ ref: (el) => {
329
+ refs.current[i] = el;
330
+ },
331
+ value: char,
332
+ disabled,
333
+ inputMode: "numeric",
334
+ autoComplete: "one-time-code",
335
+ "aria-label": `Digit ${i + 1}`,
336
+ "aria-invalid": hasError,
337
+ className: cn(cell({ error: hasError }), "disabled:opacity-50"),
338
+ onChange: (e) => handleInput(i, e.target.value),
339
+ onKeyDown: (e) => handleKeyDown(i, e),
340
+ onPaste: (e) => handlePaste(i, e),
341
+ onFocus: (e) => e.target.select()
342
+ },
343
+ i
344
+ )) }),
345
+ typeof error === "string" && error && /* @__PURE__ */ jsx("p", { className: "px-2 text-center text-xs text-[var(--color-input-error)]", children: error })
346
+ ] });
347
+ }
348
+ function Popover({
349
+ trigger,
350
+ children,
351
+ open: controlledOpen,
352
+ onOpenChange,
353
+ align = "start",
354
+ className
355
+ }) {
356
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
357
+ const isControlled = controlledOpen !== void 0;
358
+ const open = isControlled ? controlledOpen : uncontrolledOpen;
359
+ const anchorRef = useRef(null);
360
+ const panelRef = useRef(null);
361
+ const id = useId();
362
+ const setOpen = (next) => {
363
+ if (!isControlled) setUncontrolledOpen(next);
364
+ onOpenChange?.(next);
365
+ };
366
+ useEffect(() => {
367
+ if (!open) return;
368
+ const onPointerDown = (e) => {
369
+ const target = e.target;
370
+ if (!anchorRef.current?.contains(target) && !panelRef.current?.contains(target)) {
371
+ setOpen(false);
372
+ }
373
+ };
374
+ const onKeyDown = (e) => {
375
+ if (e.key === "Escape") setOpen(false);
376
+ };
377
+ document.addEventListener("pointerdown", onPointerDown);
378
+ document.addEventListener("keydown", onKeyDown);
379
+ return () => {
380
+ document.removeEventListener("pointerdown", onPointerDown);
381
+ document.removeEventListener("keydown", onKeyDown);
382
+ };
383
+ });
384
+ const triggerEl = cloneElement(trigger, {
385
+ onClick: (e) => {
386
+ trigger.props.onClick?.(e);
387
+ setOpen(!open);
388
+ },
389
+ "aria-expanded": open,
390
+ "aria-haspopup": true
391
+ });
392
+ return /* @__PURE__ */ jsxs("div", { ref: anchorRef, className: "relative inline-block w-full", children: [
393
+ triggerEl,
394
+ open && /* @__PURE__ */ jsx(
395
+ "div",
396
+ {
397
+ ref: panelRef,
398
+ id,
399
+ role: "listbox",
400
+ className: cn(
401
+ "absolute z-50 mt-1 min-w-full rounded-[12px] border border-border bg-surface p-1 shadow-lg",
402
+ align === "end" ? "right-0" : "left-0",
403
+ className
404
+ ),
405
+ children
406
+ }
407
+ )
408
+ ] });
409
+ }
410
+ var ELLIPSIS = /* @__PURE__ */ Symbol("ellipsis");
411
+ function collapseItems(items, maxItems) {
412
+ const indexed = items.map((item, index) => ({ item, index }));
413
+ if (!maxItems || items.length <= maxItems || items.length <= 2) {
414
+ return indexed;
415
+ }
416
+ const tail = Math.max(1, maxItems - 1);
417
+ return [indexed[0], ELLIPSIS, ...indexed.slice(items.length - tail)];
418
+ }
419
+ function ArrowSeparator() {
420
+ return /* @__PURE__ */ jsx(
421
+ "svg",
422
+ {
423
+ className: "size-4 shrink-0 text-text-muted",
424
+ viewBox: "0 0 16 16",
425
+ fill: "none",
426
+ stroke: "currentColor",
427
+ strokeWidth: "1.5",
428
+ strokeLinecap: "round",
429
+ strokeLinejoin: "round",
430
+ "aria-hidden": true,
431
+ children: /* @__PURE__ */ jsx("path", { d: "M3 8h9M9 5l3 3-3 3" })
432
+ }
433
+ );
434
+ }
435
+ function Breadcrumbs({
436
+ items,
437
+ separator,
438
+ maxItems,
439
+ className,
440
+ ...props
441
+ }) {
442
+ const sep = separator ?? /* @__PURE__ */ jsx(ArrowSeparator, {});
443
+ const slots = collapseItems(items, maxItems);
444
+ const hidden = slots.includes(ELLIPSIS) && maxItems ? items.slice(1, items.length - Math.max(1, maxItems - 1)) : [];
445
+ return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className, ...props, children: /* @__PURE__ */ jsx("ol", { className: "flex items-center gap-2 text-sm", children: slots.map((slot, i) => {
446
+ const isLast = i === slots.length - 1;
447
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
448
+ /* @__PURE__ */ jsx("li", { className: "flex items-center", children: slot === ELLIPSIS ? /* @__PURE__ */ jsx(
449
+ Popover,
450
+ {
451
+ align: "start",
452
+ trigger: /* @__PURE__ */ jsx(
453
+ "button",
454
+ {
455
+ type: "button",
456
+ "aria-label": "Show more",
457
+ className: "rounded-[4px] px-1 text-text-muted hover:text-text focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2",
458
+ children: "\u2026"
459
+ }
460
+ ),
461
+ children: /* @__PURE__ */ jsx("ul", { className: "min-w-32", children: hidden.map((h, hi) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
462
+ "a",
463
+ {
464
+ href: h.href,
465
+ className: "block rounded-[8px] px-3 py-2 text-sm text-text hover:bg-hover",
466
+ children: h.label
467
+ }
468
+ ) }, hi)) })
469
+ }
470
+ ) : isLast || !slot.item.href ? /* @__PURE__ */ jsx(
471
+ "span",
472
+ {
473
+ "aria-current": isLast ? "page" : void 0,
474
+ className: cn(
475
+ isLast ? "font-bold text-text" : "text-text-muted"
476
+ ),
477
+ children: slot.item.label
478
+ }
479
+ ) : /* @__PURE__ */ jsx(
480
+ "a",
481
+ {
482
+ href: slot.item.href,
483
+ className: "text-text-muted hover:text-text hover:underline",
484
+ children: slot.item.label
485
+ }
486
+ ) }),
487
+ !isLast && /* @__PURE__ */ jsx("li", { "aria-hidden": true, className: "flex items-center text-text-muted", children: sep })
488
+ ] }, i);
489
+ }) }) });
490
+ }
491
+ var buttonVariants = cva(
492
+ "inline-flex shrink-0 items-center justify-center gap-2 rounded-[8px] font-bold whitespace-nowrap transition-colors outline-none focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2 disabled:pointer-events-none disabled:opacity-50 active:scale-95 hover:brightness-95",
493
+ {
494
+ variants: {
495
+ variant: {
496
+ fill: "",
497
+ outline: "border bg-transparent",
498
+ ghost: "bg-transparent",
499
+ text: "bg-transparent hover:underline hover:brightness-100"
500
+ },
501
+ color: {
502
+ primary: "",
503
+ success: "",
504
+ error: "",
505
+ warning: "",
506
+ info: ""
507
+ },
508
+ size: {
509
+ xs: "h-8 px-3 text-xs",
510
+ sm: "h-8 px-3 text-sm",
511
+ md: "h-10 px-4 text-sm",
512
+ lg: "h-12 px-5 text-md",
513
+ xl: "h-14 px-6 text-md"
514
+ },
515
+ iconOnly: {
516
+ true: "aspect-square p-0",
517
+ false: ""
518
+ },
519
+ fullWidth: {
520
+ true: "w-full",
521
+ false: ""
522
+ }
523
+ },
524
+ compoundVariants: [
525
+ { variant: "fill", color: "primary", class: "bg-primary text-white" },
526
+ { variant: "fill", color: "success", class: "bg-success text-white" },
527
+ { variant: "fill", color: "error", class: "bg-error text-white" },
528
+ { variant: "fill", color: "warning", class: "bg-warning text-text" },
529
+ { variant: "fill", color: "info", class: "bg-info text-white" },
530
+ {
531
+ variant: "outline",
532
+ color: "primary",
533
+ class: "border-primary text-primary hover:bg-primary-tint"
534
+ },
535
+ {
536
+ variant: "outline",
537
+ color: "success",
538
+ class: "border-success text-success hover:bg-success-tint"
539
+ },
540
+ {
541
+ variant: "outline",
542
+ color: "error",
543
+ class: "border-error text-error hover:bg-error-tint"
544
+ },
545
+ {
546
+ variant: "outline",
547
+ color: "warning",
548
+ class: "border-warning text-warning hover:bg-warning-tint"
549
+ },
550
+ {
551
+ variant: "outline",
552
+ color: "info",
553
+ class: "border-info text-info hover:bg-info-tint"
554
+ },
555
+ {
556
+ variant: "ghost",
557
+ color: "primary",
558
+ class: "text-primary hover:bg-primary-tint"
559
+ },
560
+ {
561
+ variant: "ghost",
562
+ color: "success",
563
+ class: "text-success hover:bg-success-tint"
564
+ },
565
+ {
566
+ variant: "ghost",
567
+ color: "error",
568
+ class: "text-error hover:bg-error-tint"
569
+ },
570
+ {
571
+ variant: "ghost",
572
+ color: "warning",
573
+ class: "text-warning hover:bg-warning-tint"
574
+ },
575
+ {
576
+ variant: "ghost",
577
+ color: "info",
578
+ class: "text-info hover:bg-info-tint"
579
+ },
580
+ { variant: "text", color: "primary", class: "text-primary" },
581
+ { variant: "text", color: "success", class: "text-success" },
582
+ { variant: "text", color: "error", class: "text-error" },
583
+ { variant: "text", color: "warning", class: "text-warning" },
584
+ { variant: "text", color: "info", class: "text-info" },
585
+ { iconOnly: true, size: "xs", class: "size-8" },
586
+ { iconOnly: true, size: "sm", class: "size-8" },
587
+ { iconOnly: true, size: "md", class: "size-10" },
588
+ { iconOnly: true, size: "lg", class: "size-12" },
589
+ { iconOnly: true, size: "xl", class: "size-14" }
590
+ ],
591
+ defaultVariants: {
592
+ variant: "fill",
593
+ color: "primary",
594
+ size: "md",
595
+ iconOnly: false,
596
+ fullWidth: false
597
+ }
598
+ }
599
+ );
600
+ function Button({
601
+ variant = "fill",
602
+ color = "primary",
603
+ size = "md",
604
+ fullWidth = false,
605
+ leftIcon,
606
+ rightIcon,
607
+ className,
608
+ children,
609
+ ...props
610
+ }) {
611
+ const iconOnly = !children && Boolean(leftIcon ?? rightIcon);
612
+ return /* @__PURE__ */ jsxs(
613
+ "button",
614
+ {
615
+ className: cn(
616
+ buttonVariants({ variant, color, size, fullWidth, iconOnly }),
617
+ fullWidth && (leftIcon || rightIcon) && "justify-between",
618
+ className
619
+ ),
620
+ ...props,
621
+ children: [
622
+ leftIcon,
623
+ children,
624
+ rightIcon
625
+ ]
626
+ }
627
+ );
628
+ }
629
+ function nextIndex(current, count, dir, loop) {
630
+ const n = current + dir;
631
+ if (n < 0) return loop ? count - 1 : 0;
632
+ if (n >= count) return loop ? 0 : count - 1;
633
+ return n;
634
+ }
635
+ function Carousel({
636
+ children,
637
+ title,
638
+ description,
639
+ seeAllHref,
640
+ arrows = true,
641
+ indicators = true,
642
+ autoSlide = false,
643
+ interval = 5e3,
644
+ loop = true,
645
+ gap = 16,
646
+ className,
647
+ ...props
648
+ }) {
649
+ const trackRef = useRef(null);
650
+ const items = Children.toArray(children);
651
+ const count = items.length;
652
+ const [active, setActive] = useState(0);
653
+ const [progress, setProgress] = useState(0);
654
+ const scrollToIndex = useCallback((index) => {
655
+ const track2 = trackRef.current;
656
+ if (!track2) return;
657
+ const child = track2.children[index];
658
+ child?.scrollIntoView({
659
+ behavior: "smooth",
660
+ inline: "start",
661
+ block: "nearest"
662
+ });
663
+ }, []);
664
+ const go = useCallback(
665
+ (dir) => {
666
+ setActive((cur) => {
667
+ const target = nextIndex(cur, count, dir, loop);
668
+ scrollToIndex(target);
669
+ return target;
670
+ });
671
+ },
672
+ [count, loop, scrollToIndex]
673
+ );
674
+ useEffect(() => {
675
+ const track2 = trackRef.current;
676
+ if (!track2) return;
677
+ const observer = new IntersectionObserver(
678
+ (entries) => {
679
+ for (const entry of entries) {
680
+ if (entry.isIntersecting) {
681
+ const idx = Array.prototype.indexOf.call(
682
+ track2.children,
683
+ entry.target
684
+ );
685
+ if (idx >= 0) setActive(idx);
686
+ }
687
+ }
688
+ },
689
+ { root: track2, threshold: 0.6 }
690
+ );
691
+ for (const child of Array.from(track2.children)) observer.observe(child);
692
+ return () => observer.disconnect();
693
+ }, [count]);
694
+ useEffect(() => {
695
+ if (!autoSlide || count <= 1) return;
696
+ const step = 50;
697
+ let elapsed = 0;
698
+ const id = setInterval(() => {
699
+ elapsed += step;
700
+ setProgress(Math.min(1, elapsed / interval));
701
+ if (elapsed >= interval) {
702
+ elapsed = 0;
703
+ go(1);
704
+ }
705
+ }, step);
706
+ return () => clearInterval(id);
707
+ }, [autoSlide, interval, count, active, go]);
708
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-4", className), ...props, children: [
709
+ (title || description || seeAllHref || arrows) && /* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-4", children: [
710
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
711
+ title && /* @__PURE__ */ jsx("h3", { className: "text-xl font-bold text-text", children: title }),
712
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-text-muted", children: description })
713
+ ] }),
714
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
715
+ seeAllHref && /* @__PURE__ */ jsx(
716
+ "a",
717
+ {
718
+ href: seeAllHref,
719
+ className: "text-sm font-bold text-primary hover:underline",
720
+ children: "See all"
721
+ }
722
+ ),
723
+ arrows && /* @__PURE__ */ jsxs(Fragment$1, { children: [
724
+ /* @__PURE__ */ jsx(ArrowButton, { dir: "prev", onClick: () => go(-1) }),
725
+ /* @__PURE__ */ jsx(ArrowButton, { dir: "next", onClick: () => go(1) })
726
+ ] })
727
+ ] })
728
+ ] }),
729
+ /* @__PURE__ */ jsx(
730
+ "div",
731
+ {
732
+ ref: trackRef,
733
+ className: "flex snap-x snap-mandatory overflow-x-auto scroll-smooth [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
734
+ style: { gap },
735
+ children: items.map((child, i) => /* @__PURE__ */ jsx("div", { className: "shrink-0 snap-start", children: child }, i))
736
+ }
737
+ ),
738
+ indicators && count > 1 && /* @__PURE__ */ jsx("div", { className: "flex justify-center gap-2", children: items.map((_, i) => /* @__PURE__ */ jsx(
739
+ "button",
740
+ {
741
+ "aria-label": `Go to slide ${i + 1}`,
742
+ "aria-current": i === active,
743
+ onClick: () => {
744
+ setActive(i);
745
+ scrollToIndex(i);
746
+ },
747
+ className: "relative size-2 overflow-hidden rounded-full bg-border",
748
+ children: /* @__PURE__ */ jsx(
749
+ "span",
750
+ {
751
+ className: cn(
752
+ "absolute inset-0 rounded-full bg-primary transition-transform",
753
+ i === active ? "scale-100" : "scale-0"
754
+ ),
755
+ style: autoSlide && i === active ? {
756
+ transform: `scaleX(${progress})`,
757
+ transformOrigin: "left"
758
+ } : void 0
759
+ }
760
+ )
761
+ },
762
+ i
763
+ )) })
764
+ ] });
765
+ }
766
+ function ArrowButton({
767
+ dir,
768
+ onClick
769
+ }) {
770
+ return /* @__PURE__ */ jsx(
771
+ "button",
772
+ {
773
+ type: "button",
774
+ "aria-label": dir === "prev" ? "Previous" : "Next",
775
+ onClick,
776
+ className: "flex size-8 items-center justify-center rounded-full border border-border text-text transition-colors hover:bg-hover",
777
+ children: /* @__PURE__ */ jsx(
778
+ "svg",
779
+ {
780
+ className: cn("size-4", dir === "prev" && "rotate-180"),
781
+ viewBox: "0 0 16 16",
782
+ fill: "none",
783
+ stroke: "currentColor",
784
+ strokeWidth: "1.5",
785
+ strokeLinecap: "round",
786
+ strokeLinejoin: "round",
787
+ "aria-hidden": true,
788
+ children: /* @__PURE__ */ jsx("path", { d: "m6 4 4 4-4 4" })
789
+ }
790
+ )
791
+ }
792
+ );
793
+ }
794
+ function Checkbox({
795
+ label,
796
+ indeterminate = false,
797
+ className,
798
+ disabled,
799
+ ...props
800
+ }) {
801
+ const ref = useRef(null);
802
+ useEffect(() => {
803
+ if (ref.current) ref.current.indeterminate = indeterminate;
804
+ }, [indeterminate]);
805
+ const input = /* @__PURE__ */ jsxs("span", { className: "relative inline-flex size-6 shrink-0", children: [
806
+ /* @__PURE__ */ jsx(
807
+ "input",
808
+ {
809
+ ref,
810
+ type: "checkbox",
811
+ disabled,
812
+ className: cn(
813
+ "peer size-6 cursor-pointer appearance-none rounded-[4px] border-[1.5px] border-text-muted bg-transparent transition-colors",
814
+ "checked:border-primary checked:bg-primary indeterminate:border-primary indeterminate:bg-primary",
815
+ "focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2",
816
+ "disabled:cursor-not-allowed disabled:opacity-50",
817
+ className
818
+ ),
819
+ ...props
820
+ }
821
+ ),
822
+ /* @__PURE__ */ jsx(
823
+ "svg",
824
+ {
825
+ className: "pointer-events-none absolute inset-0 m-auto hidden size-4 text-white peer-checked:block peer-indeterminate:hidden",
826
+ viewBox: "0 0 16 16",
827
+ fill: "none",
828
+ stroke: "currentColor",
829
+ strokeWidth: "2",
830
+ strokeLinecap: "round",
831
+ strokeLinejoin: "round",
832
+ "aria-hidden": true,
833
+ children: /* @__PURE__ */ jsx("path", { d: "m3 8 3.5 3.5L13 5" })
834
+ }
835
+ ),
836
+ /* @__PURE__ */ jsx(
837
+ "svg",
838
+ {
839
+ className: "pointer-events-none absolute inset-0 m-auto hidden size-4 text-white peer-indeterminate:block",
840
+ viewBox: "0 0 16 16",
841
+ fill: "none",
842
+ stroke: "currentColor",
843
+ strokeWidth: "2",
844
+ strokeLinecap: "round",
845
+ "aria-hidden": true,
846
+ children: /* @__PURE__ */ jsx("path", { d: "M4 8h8" })
847
+ }
848
+ )
849
+ ] });
850
+ if (!label) return input;
851
+ return /* @__PURE__ */ jsxs(
852
+ "label",
853
+ {
854
+ className: cn(
855
+ "inline-flex items-center gap-2 text-sm text-text",
856
+ disabled && "cursor-not-allowed opacity-50"
857
+ ),
858
+ children: [
859
+ input,
860
+ label
861
+ ]
862
+ }
863
+ );
864
+ }
865
+ var chip = cva(
866
+ "inline-flex items-center gap-2 bg-surface text-text whitespace-nowrap",
867
+ {
868
+ variants: {
869
+ size: {
870
+ sm: "h-8 rounded-[4px] px-2 text-sm [&_svg]:size-4",
871
+ md: "h-10 rounded-[8px] px-3 text-sm [&_svg]:size-6"
872
+ }
873
+ },
874
+ defaultVariants: { size: "md" }
875
+ }
876
+ );
877
+ function Chip({
878
+ size = "md",
879
+ leading,
880
+ trailing,
881
+ onDismiss,
882
+ className,
883
+ children,
884
+ ...props
885
+ }) {
886
+ return /* @__PURE__ */ jsxs("span", { className: cn(chip({ size }), className), ...props, children: [
887
+ leading,
888
+ children,
889
+ onDismiss ? /* @__PURE__ */ jsx(
890
+ "button",
891
+ {
892
+ type: "button",
893
+ "aria-label": "Remove",
894
+ onClick: onDismiss,
895
+ className: "-mr-1 inline-flex shrink-0 items-center justify-center rounded-full text-text-muted hover:text-text focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2",
896
+ children: /* @__PURE__ */ jsx(
897
+ "svg",
898
+ {
899
+ viewBox: "0 0 24 24",
900
+ fill: "none",
901
+ stroke: "currentColor",
902
+ strokeWidth: "2",
903
+ strokeLinecap: "round",
904
+ "aria-hidden": true,
905
+ children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6L6 18" })
906
+ }
907
+ )
908
+ }
909
+ ) : trailing
910
+ ] });
911
+ }
912
+ var tag = cva(
913
+ "inline-flex items-center gap-1 rounded-[4px] bg-background font-bold text-text whitespace-nowrap",
914
+ {
915
+ variants: {
916
+ size: {
917
+ "2xs": "h-4 px-1 text-[10px] [&_svg]:size-3",
918
+ xs: "h-6 px-2 text-xs [&_svg]:size-4"
919
+ },
920
+ uppercase: {
921
+ true: "uppercase",
922
+ false: ""
923
+ }
924
+ },
925
+ defaultVariants: { size: "xs", uppercase: false }
926
+ }
927
+ );
928
+ function Tag({
929
+ size = "xs",
930
+ uppercase,
931
+ leading,
932
+ trailing,
933
+ className,
934
+ children,
935
+ ...props
936
+ }) {
937
+ return /* @__PURE__ */ jsxs("span", { className: cn(tag({ size, uppercase }), className), ...props, children: [
938
+ leading,
939
+ children,
940
+ trailing
941
+ ] });
942
+ }
943
+ function Radio({ label, className, disabled, ...props }) {
944
+ const input = /* @__PURE__ */ jsxs("span", { className: "relative inline-flex size-6 shrink-0", children: [
945
+ /* @__PURE__ */ jsx(
946
+ "input",
947
+ {
948
+ type: "radio",
949
+ disabled,
950
+ className: cn(
951
+ "peer size-6 cursor-pointer appearance-none rounded-full border-[1.5px] border-text-muted bg-transparent transition-colors",
952
+ "checked:border-primary",
953
+ "focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2",
954
+ "disabled:cursor-not-allowed disabled:opacity-50",
955
+ className
956
+ ),
957
+ ...props
958
+ }
959
+ ),
960
+ /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute inset-0 m-auto size-3 scale-0 rounded-full bg-primary transition-transform peer-checked:scale-100" })
961
+ ] });
962
+ if (!label) return input;
963
+ return /* @__PURE__ */ jsxs(
964
+ "label",
965
+ {
966
+ className: cn(
967
+ "inline-flex items-center gap-2 text-sm text-text",
968
+ disabled && "cursor-not-allowed opacity-50"
969
+ ),
970
+ children: [
971
+ input,
972
+ label
973
+ ]
974
+ }
975
+ );
976
+ }
977
+ var loader = cva(
978
+ "inline-block animate-spin rounded-full border-current border-t-transparent text-primary",
979
+ {
980
+ variants: {
981
+ size: {
982
+ "2xs": "size-4 border-2",
983
+ xs: "size-6 border-2",
984
+ sm: "size-8 border-[3px]",
985
+ md: "size-10 border-4",
986
+ lg: "size-12 border-4"
987
+ }
988
+ },
989
+ defaultVariants: { size: "md" }
990
+ }
991
+ );
992
+ function Loader({
993
+ size = "md",
994
+ label = "Loading",
995
+ className,
996
+ ...props
997
+ }) {
998
+ return /* @__PURE__ */ jsx(
999
+ "span",
1000
+ {
1001
+ role: "status",
1002
+ "aria-label": label,
1003
+ className: cn(loader({ size }), className),
1004
+ ...props
1005
+ }
1006
+ );
1007
+ }
1008
+ function Search({
1009
+ value,
1010
+ onValueChange,
1011
+ results,
1012
+ loading = false,
1013
+ emptyState = "No results",
1014
+ resultsTitle = "Search results",
1015
+ shortcut = "\u2318K",
1016
+ mobile = false,
1017
+ open: controlledOpen,
1018
+ onOpenChange,
1019
+ placeholder = "Search\u2026",
1020
+ className,
1021
+ ...props
1022
+ }) {
1023
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
1024
+ const isControlled = controlledOpen !== void 0;
1025
+ const open = isControlled ? controlledOpen : uncontrolledOpen;
1026
+ const rootRef = useRef(null);
1027
+ const inputRef = useRef(null);
1028
+ const setOpen = (next) => {
1029
+ if (!isControlled) setUncontrolledOpen(next);
1030
+ onOpenChange?.(next);
1031
+ };
1032
+ useEffect(() => {
1033
+ if (!open || mobile) return;
1034
+ const onPointerDown = (e) => {
1035
+ if (!rootRef.current?.contains(e.target)) setOpen(false);
1036
+ };
1037
+ const onKey = (e) => {
1038
+ if (e.key === "Escape") setOpen(false);
1039
+ };
1040
+ document.addEventListener("pointerdown", onPointerDown);
1041
+ document.addEventListener("keydown", onKey);
1042
+ return () => {
1043
+ document.removeEventListener("pointerdown", onPointerDown);
1044
+ document.removeEventListener("keydown", onKey);
1045
+ };
1046
+ });
1047
+ const hasResults = Boolean(results);
1048
+ const inputRow = /* @__PURE__ */ jsxs("div", { className: "flex h-14 items-center gap-3 px-4", children: [
1049
+ loading ? /* @__PURE__ */ jsx(Loader, { size: "2xs", className: "text-text-muted" }) : /* @__PURE__ */ jsx(SearchIcon, { className: "size-6 shrink-0 text-text-muted" }),
1050
+ /* @__PURE__ */ jsx(
1051
+ "input",
1052
+ {
1053
+ ref: inputRef,
1054
+ type: "search",
1055
+ value,
1056
+ placeholder,
1057
+ role: "searchbox",
1058
+ className: "flex-1 bg-transparent text-sm text-text outline-none placeholder:text-text-muted [&::-webkit-search-cancel-button]:hidden",
1059
+ onChange: (e) => onValueChange(e.target.value),
1060
+ onFocus: () => setOpen(true),
1061
+ ...props
1062
+ }
1063
+ ),
1064
+ value ? /* @__PURE__ */ jsx(
1065
+ "button",
1066
+ {
1067
+ type: "button",
1068
+ "aria-label": "Clear search",
1069
+ onClick: () => {
1070
+ onValueChange("");
1071
+ inputRef.current?.focus();
1072
+ },
1073
+ className: "inline-flex size-6 shrink-0 items-center justify-center rounded-full text-text-muted hover:bg-hover hover:text-text",
1074
+ children: /* @__PURE__ */ jsx(
1075
+ "svg",
1076
+ {
1077
+ viewBox: "0 0 24 24",
1078
+ className: "size-4",
1079
+ fill: "none",
1080
+ stroke: "currentColor",
1081
+ strokeWidth: "2",
1082
+ strokeLinecap: "round",
1083
+ "aria-hidden": true,
1084
+ children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6L6 18" })
1085
+ }
1086
+ )
1087
+ }
1088
+ ) : shortcut && /* @__PURE__ */ jsx("kbd", { className: "shrink-0 text-xs text-text-muted", children: shortcut })
1089
+ ] });
1090
+ const panel = /* @__PURE__ */ jsxs(Fragment$1, { children: [
1091
+ /* @__PURE__ */ jsx("div", { className: "border-t border-border" }),
1092
+ hasResults && /* @__PURE__ */ jsx("div", { className: "px-6 py-3 text-sm font-bold text-text-muted", children: resultsTitle }),
1093
+ /* @__PURE__ */ jsx("div", { className: "max-h-80 overflow-auto p-1", children: loading ? /* @__PURE__ */ jsx("div", { className: "flex justify-center py-8", children: /* @__PURE__ */ jsx(Loader, {}) }) : hasResults ? results : /* @__PURE__ */ jsx("div", { className: "px-3 py-8 text-center text-sm text-text-muted", children: emptyState }) })
1094
+ ] });
1095
+ if (mobile && open) {
1096
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex flex-col bg-surface", children: [
1097
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
1098
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: inputRow }),
1099
+ /* @__PURE__ */ jsx(
1100
+ "button",
1101
+ {
1102
+ type: "button",
1103
+ "aria-label": "Close search",
1104
+ onClick: () => setOpen(false),
1105
+ className: "px-4 text-sm font-bold text-primary",
1106
+ children: "Cancel"
1107
+ }
1108
+ )
1109
+ ] }),
1110
+ panel
1111
+ ] });
1112
+ }
1113
+ return /* @__PURE__ */ jsx("div", { ref: rootRef, className: cn("relative w-full", className), children: /* @__PURE__ */ jsxs("div", { className: "rounded-[12px] border border-border bg-surface", children: [
1114
+ inputRow,
1115
+ open && panel
1116
+ ] }) });
1117
+ }
1118
+ function SearchIcon({ className }) {
1119
+ return /* @__PURE__ */ jsxs(
1120
+ "svg",
1121
+ {
1122
+ className,
1123
+ viewBox: "0 0 24 24",
1124
+ fill: "none",
1125
+ stroke: "currentColor",
1126
+ strokeWidth: "2",
1127
+ strokeLinecap: "round",
1128
+ strokeLinejoin: "round",
1129
+ "aria-hidden": true,
1130
+ children: [
1131
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
1132
+ /* @__PURE__ */ jsx("path", { d: "m21 21-4.3-4.3" })
1133
+ ]
1134
+ }
1135
+ );
1136
+ }
1137
+ var container = cva(
1138
+ "inline-flex items-center gap-1 rounded-[12px] bg-background p-1",
1139
+ {
1140
+ variants: {
1141
+ size: {
1142
+ sm: "h-10",
1143
+ md: "h-12"
1144
+ }
1145
+ },
1146
+ defaultVariants: { size: "md" }
1147
+ }
1148
+ );
1149
+ var segment = cva(
1150
+ "inline-flex h-full flex-1 items-center justify-center gap-2 rounded-[8px] px-3 text-sm font-bold whitespace-nowrap transition-colors focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2 [&_svg]:size-5",
1151
+ {
1152
+ variants: {
1153
+ active: {
1154
+ true: "bg-surface text-text shadow-sm",
1155
+ false: "text-text-muted hover:text-text"
1156
+ }
1157
+ },
1158
+ defaultVariants: { active: false }
1159
+ }
1160
+ );
1161
+ function SegmentedControl({
1162
+ items,
1163
+ value,
1164
+ onChange,
1165
+ size = "md",
1166
+ className,
1167
+ ...props
1168
+ }) {
1169
+ return /* @__PURE__ */ jsx(
1170
+ "div",
1171
+ {
1172
+ role: "tablist",
1173
+ className: cn(container({ size }), className),
1174
+ ...props,
1175
+ children: items.map((item) => {
1176
+ const active = item.value === value;
1177
+ return /* @__PURE__ */ jsxs(
1178
+ "button",
1179
+ {
1180
+ type: "button",
1181
+ role: "tab",
1182
+ "aria-selected": active,
1183
+ onClick: () => onChange(item.value),
1184
+ className: segment({ active }),
1185
+ children: [
1186
+ item.icon,
1187
+ item.label
1188
+ ]
1189
+ },
1190
+ item.value
1191
+ );
1192
+ })
1193
+ }
1194
+ );
1195
+ }
1196
+ var TabsContext = createContext(null);
1197
+ function useTabs() {
1198
+ const ctx = useContext(TabsContext);
1199
+ if (!ctx) throw new Error("Tabs components must be used within <Tabs>");
1200
+ return ctx;
1201
+ }
1202
+ function Tabs({
1203
+ value,
1204
+ onValueChange,
1205
+ type = "underline",
1206
+ size = "md",
1207
+ children,
1208
+ className
1209
+ }) {
1210
+ const baseId = useId();
1211
+ return /* @__PURE__ */ jsx(
1212
+ TabsContext.Provider,
1213
+ {
1214
+ value: { value, setValue: onValueChange, type, size, baseId },
1215
+ children: /* @__PURE__ */ jsx("div", { className, children })
1216
+ }
1217
+ );
1218
+ }
1219
+ var list = cva("flex items-center", {
1220
+ variants: {
1221
+ type: {
1222
+ default: "gap-1",
1223
+ pill: "gap-1 rounded-[12px] bg-background p-1",
1224
+ underline: "gap-1 border-b border-border"
1225
+ }
1226
+ },
1227
+ defaultVariants: { type: "underline" }
1228
+ });
1229
+ function TabList({
1230
+ children,
1231
+ className
1232
+ }) {
1233
+ const { type } = useTabs();
1234
+ return /* @__PURE__ */ jsx("div", { role: "tablist", className: cn(list({ type }), className), children });
1235
+ }
1236
+ var tab = cva(
1237
+ "inline-flex items-center justify-center gap-2 font-bold whitespace-nowrap transition-colors outline-none focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-5",
1238
+ {
1239
+ variants: {
1240
+ type: {
1241
+ default: "rounded-[8px] px-3",
1242
+ pill: "rounded-[8px] px-3",
1243
+ underline: "-mb-px border-b-2 border-transparent px-3"
1244
+ },
1245
+ size: { sm: "h-8 text-sm", md: "h-10 text-sm" },
1246
+ active: { true: "", false: "text-text-muted hover:text-text" }
1247
+ },
1248
+ compoundVariants: [
1249
+ { type: "default", active: true, class: "text-primary" },
1250
+ { type: "pill", active: true, class: "bg-surface text-text shadow-sm" },
1251
+ { type: "underline", active: true, class: "border-primary text-primary" }
1252
+ ],
1253
+ defaultVariants: { type: "underline", size: "md", active: false }
1254
+ }
1255
+ );
1256
+ function Tab({
1257
+ value,
1258
+ leading,
1259
+ trailing,
1260
+ children,
1261
+ className,
1262
+ ...props
1263
+ }) {
1264
+ const ctx = useTabs();
1265
+ const active = ctx.value === value;
1266
+ return /* @__PURE__ */ jsxs(
1267
+ "button",
1268
+ {
1269
+ type: "button",
1270
+ role: "tab",
1271
+ id: `${ctx.baseId}-tab-${value}`,
1272
+ "aria-selected": active,
1273
+ "aria-controls": `${ctx.baseId}-panel-${value}`,
1274
+ tabIndex: active ? 0 : -1,
1275
+ onClick: () => ctx.setValue(value),
1276
+ onKeyDown: (e) => {
1277
+ if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
1278
+ const tabs = Array.from(
1279
+ e.currentTarget.parentElement?.querySelectorAll(
1280
+ '[role="tab"]'
1281
+ ) ?? []
1282
+ );
1283
+ const i = tabs.indexOf(e.currentTarget);
1284
+ const next = e.key === "ArrowRight" ? tabs[i + 1] : tabs[i - 1];
1285
+ next?.focus();
1286
+ next?.click();
1287
+ },
1288
+ className: cn(tab({ type: ctx.type, size: ctx.size, active }), className),
1289
+ ...props,
1290
+ children: [
1291
+ leading,
1292
+ children,
1293
+ trailing
1294
+ ]
1295
+ }
1296
+ );
1297
+ }
1298
+ function TabPanel({
1299
+ value,
1300
+ children,
1301
+ className
1302
+ }) {
1303
+ const ctx = useTabs();
1304
+ if (ctx.value !== value) return null;
1305
+ return /* @__PURE__ */ jsx(
1306
+ "div",
1307
+ {
1308
+ role: "tabpanel",
1309
+ id: `${ctx.baseId}-panel-${value}`,
1310
+ "aria-labelledby": `${ctx.baseId}-tab-${value}`,
1311
+ className,
1312
+ children
1313
+ }
1314
+ );
1315
+ }
1316
+ var field = cva(
1317
+ "w-full appearance-none rounded-[8px] border bg-surface text-sm text-text outline-none transition-shadow disabled:cursor-not-allowed disabled:opacity-50",
1318
+ {
1319
+ variants: {
1320
+ size: {
1321
+ sm: "h-8 px-2",
1322
+ md: "h-10 px-3",
1323
+ lg: "h-12 px-3",
1324
+ xl: "h-14 px-4"
1325
+ },
1326
+ invalid: {
1327
+ true: "border-[2px] border-[var(--color-input-error)]",
1328
+ false: "border-border hover:border-text-muted focus:border-[1.5px] focus:border-primary focus:ring-2 focus:ring-primary-tint"
1329
+ }
1330
+ },
1331
+ defaultVariants: { size: "md", invalid: false }
1332
+ }
1333
+ );
1334
+ function Select({
1335
+ size = "md",
1336
+ invalid,
1337
+ label,
1338
+ leadingIcon,
1339
+ error,
1340
+ helperText,
1341
+ className,
1342
+ id,
1343
+ disabled,
1344
+ children,
1345
+ ...props
1346
+ }) {
1347
+ const isInvalid = invalid ?? Boolean(error);
1348
+ const hint = error ?? helperText;
1349
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-1", className), children: [
1350
+ label && /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm font-bold text-text", children: label }),
1351
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1352
+ leadingIcon && /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute inset-y-0 left-3 flex items-center text-text-muted [&_svg]:size-5", children: leadingIcon }),
1353
+ /* @__PURE__ */ jsx(
1354
+ "select",
1355
+ {
1356
+ id,
1357
+ disabled,
1358
+ "aria-invalid": isInvalid || void 0,
1359
+ className: cn(
1360
+ field({ size, invalid: isInvalid }),
1361
+ leadingIcon && "pl-10",
1362
+ "pr-9"
1363
+ ),
1364
+ ...props,
1365
+ children
1366
+ }
1367
+ ),
1368
+ /* @__PURE__ */ jsx(ChevronDown2, { className: "pointer-events-none absolute inset-y-0 right-3 my-auto size-5 text-text-muted" })
1369
+ ] }),
1370
+ hint && /* @__PURE__ */ jsx(
1371
+ "span",
1372
+ {
1373
+ className: cn(
1374
+ "text-xs",
1375
+ error ? "text-[var(--color-input-error)]" : "text-text-muted"
1376
+ ),
1377
+ children: hint
1378
+ }
1379
+ )
1380
+ ] });
1381
+ }
1382
+ function ChevronDown2({ className }) {
1383
+ return /* @__PURE__ */ jsx(
1384
+ "svg",
1385
+ {
1386
+ className,
1387
+ viewBox: "0 0 24 24",
1388
+ fill: "none",
1389
+ stroke: "currentColor",
1390
+ strokeWidth: "2",
1391
+ strokeLinecap: "round",
1392
+ strokeLinejoin: "round",
1393
+ "aria-hidden": true,
1394
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
1395
+ }
1396
+ );
1397
+ }
1398
+ var field2 = cva(
1399
+ "w-full appearance-none rounded-[8px] border bg-surface text-sm text-text outline-none transition-shadow placeholder:text-text-muted disabled:cursor-not-allowed disabled:opacity-50",
1400
+ {
1401
+ variants: {
1402
+ size: {
1403
+ sm: "h-8 px-2",
1404
+ md: "h-10 px-3",
1405
+ lg: "h-12 px-3",
1406
+ xl: "h-14 px-4"
1407
+ },
1408
+ invalid: {
1409
+ true: "border-[2px] border-[var(--color-input-error)]",
1410
+ false: "border-border hover:border-text-muted focus:border-[1.5px] focus:border-primary focus:ring-2 focus:ring-primary-tint"
1411
+ }
1412
+ },
1413
+ defaultVariants: { size: "md", invalid: false }
1414
+ }
1415
+ );
1416
+ function TextInput({
1417
+ size = "md",
1418
+ invalid,
1419
+ label,
1420
+ leadingIcon,
1421
+ trailingIcon,
1422
+ error,
1423
+ helperText,
1424
+ floatingLabel = false,
1425
+ className,
1426
+ id,
1427
+ disabled,
1428
+ placeholder,
1429
+ ...props
1430
+ }) {
1431
+ const isInvalid = invalid ?? Boolean(error);
1432
+ const hint = error ?? helperText;
1433
+ const floating = floatingLabel && Boolean(label);
1434
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-1", className), children: [
1435
+ label && !floating && /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm font-bold text-text", children: label }),
1436
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1437
+ leadingIcon && /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute inset-y-0 left-3 flex items-center text-text-muted [&_svg]:size-5", children: leadingIcon }),
1438
+ /* @__PURE__ */ jsx(
1439
+ "input",
1440
+ {
1441
+ id,
1442
+ disabled,
1443
+ placeholder: floating ? " " : placeholder,
1444
+ "aria-invalid": isInvalid || void 0,
1445
+ className: cn(
1446
+ field2({ size, invalid: isInvalid }),
1447
+ "peer",
1448
+ leadingIcon && "pl-10",
1449
+ trailingIcon && "pr-10",
1450
+ floating && "placeholder-shown:[&::placeholder]:opacity-0"
1451
+ ),
1452
+ ...props
1453
+ }
1454
+ ),
1455
+ floating && /* @__PURE__ */ jsx(
1456
+ "label",
1457
+ {
1458
+ htmlFor: id,
1459
+ className: cn(
1460
+ "pointer-events-none absolute left-3 top-0 flex h-full origin-left items-center text-sm text-text-muted transition-all peer-focus:top-0 peer-focus:h-auto peer-focus:-translate-y-1/2 peer-focus:text-xs peer-focus:text-primary peer-not-placeholder-shown:top-0 peer-not-placeholder-shown:h-auto peer-not-placeholder-shown:-translate-y-1/2 peer-not-placeholder-shown:text-xs",
1461
+ leadingIcon && "left-10 peer-focus:left-3 peer-not-placeholder-shown:left-3",
1462
+ isInvalid && "peer-focus:text-[var(--color-input-error)]"
1463
+ ),
1464
+ children: /* @__PURE__ */ jsx("span", { className: "bg-surface px-1", children: label })
1465
+ }
1466
+ ),
1467
+ trailingIcon && /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute inset-y-0 right-3 flex items-center text-text-muted [&_svg]:size-5", children: trailingIcon })
1468
+ ] }),
1469
+ hint && /* @__PURE__ */ jsx(
1470
+ "span",
1471
+ {
1472
+ className: cn(
1473
+ "px-1 text-xs",
1474
+ error ? "text-[var(--color-input-error)]" : "text-text-muted"
1475
+ ),
1476
+ children: hint
1477
+ }
1478
+ )
1479
+ ] });
1480
+ }
1481
+ var cellBase = "relative flex w-full items-center rounded-[8px] border-[1.5px] border-transparent";
1482
+ var cellActive = "focus-within:z-10 focus-within:border-primary focus-within:ring-4 focus-within:ring-primary-tint";
1483
+ var cellError = "z-10 border-[var(--color-input-error)] focus-within:border-[var(--color-input-error)]";
1484
+ function GroupField({
1485
+ select,
1486
+ invalid,
1487
+ className,
1488
+ children,
1489
+ ...props
1490
+ }) {
1491
+ const cell3 = cn(cellBase, invalid ? cellError : cellActive, className);
1492
+ if (select) {
1493
+ return /* @__PURE__ */ jsxs("div", { className: cell3, children: [
1494
+ /* @__PURE__ */ jsx(
1495
+ "select",
1496
+ {
1497
+ className: "w-full appearance-none bg-transparent px-4 py-4 pr-10 text-base text-text outline-none disabled:cursor-not-allowed disabled:opacity-50",
1498
+ ...props,
1499
+ children
1500
+ }
1501
+ ),
1502
+ /* @__PURE__ */ jsx(ChevronDown3, { className: "pointer-events-none absolute right-4 size-5 text-text-muted" })
1503
+ ] });
1504
+ }
1505
+ return /* @__PURE__ */ jsxs("div", { className: cell3, children: [
1506
+ /* @__PURE__ */ jsx(
1507
+ "input",
1508
+ {
1509
+ className: "w-full bg-transparent px-4 py-4 text-base text-text outline-none placeholder:text-text-muted disabled:cursor-not-allowed disabled:opacity-50",
1510
+ ...props
1511
+ }
1512
+ ),
1513
+ children
1514
+ ] });
1515
+ }
1516
+ function TextInputGroup({
1517
+ direction = "vertical",
1518
+ errors,
1519
+ className,
1520
+ children,
1521
+ ...props
1522
+ }) {
1523
+ const fields = Children.toArray(children).filter(isValidElement);
1524
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-1", className), ...props, children: [
1525
+ /* @__PURE__ */ jsx(
1526
+ "div",
1527
+ {
1528
+ className: cn(
1529
+ "flex rounded-[8px] border border-border bg-surface",
1530
+ direction === "vertical" ? "flex-col divide-y divide-border" : "flex-row divide-x divide-border"
1531
+ ),
1532
+ children: fields.map(
1533
+ (child, i) => cloneElement(child, {
1534
+ key: i,
1535
+ className: cn(
1536
+ "-m-px",
1537
+ direction === "horizontal" && "flex-1",
1538
+ child.props.className
1539
+ )
1540
+ })
1541
+ )
1542
+ }
1543
+ ),
1544
+ errors?.filter(Boolean).map((msg, i) => /* @__PURE__ */ jsx("span", { className: "px-1 text-xs text-[var(--color-input-error)]", children: msg }, i))
1545
+ ] });
1546
+ }
1547
+ function ChevronDown3({ className }) {
1548
+ return /* @__PURE__ */ jsx(
1549
+ "svg",
1550
+ {
1551
+ className,
1552
+ viewBox: "0 0 24 24",
1553
+ fill: "none",
1554
+ stroke: "currentColor",
1555
+ strokeWidth: "2",
1556
+ strokeLinecap: "round",
1557
+ strokeLinejoin: "round",
1558
+ "aria-hidden": true,
1559
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
1560
+ }
1561
+ );
1562
+ }
1563
+ var field3 = cva(
1564
+ "w-full rounded-[8px] border bg-surface px-3 py-2 text-sm text-text outline-none transition-shadow placeholder:text-text-muted disabled:cursor-not-allowed disabled:opacity-50",
1565
+ {
1566
+ variants: {
1567
+ invalid: {
1568
+ true: "border-[2px] border-[var(--color-input-error)]",
1569
+ false: "border-border hover:border-text-muted focus:border-[1.5px] focus:border-primary focus:ring-2 focus:ring-primary-tint"
1570
+ }
1571
+ },
1572
+ defaultVariants: { invalid: false }
1573
+ }
1574
+ );
1575
+ function Textarea({
1576
+ invalid,
1577
+ label,
1578
+ error,
1579
+ helperText,
1580
+ autoResize = false,
1581
+ className,
1582
+ id,
1583
+ disabled,
1584
+ value,
1585
+ onChange,
1586
+ rows = 3,
1587
+ ...props
1588
+ }) {
1589
+ const ref = useRef(null);
1590
+ const isInvalid = invalid ?? Boolean(error);
1591
+ const hint = error ?? helperText;
1592
+ const resize = () => {
1593
+ const el = ref.current;
1594
+ if (!el || !autoResize) return;
1595
+ el.style.height = "auto";
1596
+ el.style.height = `${el.scrollHeight}px`;
1597
+ };
1598
+ useEffect(resize, [value, autoResize]);
1599
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-1", className), children: [
1600
+ label && /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm font-bold text-text", children: label }),
1601
+ /* @__PURE__ */ jsx(
1602
+ "textarea",
1603
+ {
1604
+ ref,
1605
+ id,
1606
+ rows,
1607
+ disabled,
1608
+ value,
1609
+ "aria-invalid": isInvalid || void 0,
1610
+ className: cn(
1611
+ field3({ invalid: isInvalid }),
1612
+ autoResize && "resize-none overflow-hidden"
1613
+ ),
1614
+ onChange: (e) => {
1615
+ onChange?.(e);
1616
+ resize();
1617
+ },
1618
+ ...props
1619
+ }
1620
+ ),
1621
+ hint && /* @__PURE__ */ jsx(
1622
+ "span",
1623
+ {
1624
+ className: cn(
1625
+ "text-xs",
1626
+ error ? "text-[var(--color-input-error)]" : "text-text-muted"
1627
+ ),
1628
+ children: hint
1629
+ }
1630
+ )
1631
+ ] });
1632
+ }
1633
+ var track = cva(
1634
+ "peer relative inline-flex shrink-0 cursor-pointer appearance-none items-center rounded-full bg-border transition-colors checked:bg-primary focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
1635
+ {
1636
+ variants: {
1637
+ size: {
1638
+ "2xs": "h-4 w-7",
1639
+ xs: "h-6 w-11",
1640
+ sm: "h-8 w-[60px]"
1641
+ }
1642
+ },
1643
+ defaultVariants: { size: "xs" }
1644
+ }
1645
+ );
1646
+ var thumb = cva(
1647
+ "pointer-events-none absolute left-0.5 rounded-full bg-surface transition-transform",
1648
+ {
1649
+ variants: {
1650
+ size: {
1651
+ "2xs": "size-3 peer-checked:translate-x-3",
1652
+ xs: "size-5 peer-checked:translate-x-5",
1653
+ sm: "size-7 peer-checked:translate-x-7"
1654
+ }
1655
+ },
1656
+ defaultVariants: { size: "xs" }
1657
+ }
1658
+ );
1659
+ function Switch({
1660
+ size = "xs",
1661
+ label,
1662
+ className,
1663
+ disabled,
1664
+ ...props
1665
+ }) {
1666
+ const control = /* @__PURE__ */ jsxs("span", { className: "relative inline-flex", children: [
1667
+ /* @__PURE__ */ jsx(
1668
+ "input",
1669
+ {
1670
+ type: "checkbox",
1671
+ role: "switch",
1672
+ disabled,
1673
+ className: cn(track({ size }), className),
1674
+ ...props
1675
+ }
1676
+ ),
1677
+ /* @__PURE__ */ jsx("span", { className: thumb({ size }), "aria-hidden": true })
1678
+ ] });
1679
+ if (!label) return control;
1680
+ return /* @__PURE__ */ jsxs(
1681
+ "label",
1682
+ {
1683
+ className: cn(
1684
+ "inline-flex items-center gap-2 text-sm text-text",
1685
+ disabled && "cursor-not-allowed opacity-50"
1686
+ ),
1687
+ children: [
1688
+ control,
1689
+ label
1690
+ ]
1691
+ }
1692
+ );
1693
+ }
1694
+ var cell2 = cva("px-3 text-left text-sm text-text", {
1695
+ variants: {
1696
+ size: {
1697
+ xs: "h-6",
1698
+ sm: "h-8",
1699
+ md: "h-10",
1700
+ lg: "h-12",
1701
+ xl: "h-14",
1702
+ "2xl": "h-16"
1703
+ }
1704
+ },
1705
+ defaultVariants: { size: "md" }
1706
+ });
1707
+ function Table({
1708
+ className,
1709
+ ...props
1710
+ }) {
1711
+ return /* @__PURE__ */ jsx(
1712
+ "table",
1713
+ {
1714
+ className: cn("w-full border-collapse text-text", className),
1715
+ ...props
1716
+ }
1717
+ );
1718
+ }
1719
+ function TableHead({
1720
+ className,
1721
+ ...props
1722
+ }) {
1723
+ return /* @__PURE__ */ jsx("thead", { className: cn("border-b border-border", className), ...props });
1724
+ }
1725
+ function TableBody(props) {
1726
+ return /* @__PURE__ */ jsx("tbody", { ...props });
1727
+ }
1728
+ function TableRow({
1729
+ zebra,
1730
+ hover = true,
1731
+ selected,
1732
+ className,
1733
+ ...props
1734
+ }) {
1735
+ return /* @__PURE__ */ jsx(
1736
+ "tr",
1737
+ {
1738
+ "aria-selected": selected || void 0,
1739
+ className: cn(
1740
+ "border-b border-border",
1741
+ zebra && "even:bg-background",
1742
+ hover && "hover:bg-hover",
1743
+ selected && "bg-primary-tint",
1744
+ className
1745
+ ),
1746
+ ...props
1747
+ }
1748
+ );
1749
+ }
1750
+ function TableCell({
1751
+ size = "md",
1752
+ header,
1753
+ className,
1754
+ ...props
1755
+ }) {
1756
+ const Tag2 = header ? "th" : "td";
1757
+ return /* @__PURE__ */ jsx(
1758
+ Tag2,
1759
+ {
1760
+ className: cn(
1761
+ cell2({ size }),
1762
+ header && "font-bold text-text-muted",
1763
+ className
1764
+ ),
1765
+ ...props
1766
+ }
1767
+ );
1768
+ }
1769
+ function selectionState(selectedCount, total) {
1770
+ return {
1771
+ checked: total > 0 && selectedCount === total,
1772
+ indeterminate: selectedCount > 0 && selectedCount < total
1773
+ };
1774
+ }
1775
+ function DataTable({
1776
+ columns,
1777
+ data,
1778
+ rowKey,
1779
+ size = "md",
1780
+ zebra,
1781
+ selectable,
1782
+ selected = [],
1783
+ onSelectedChange,
1784
+ className
1785
+ }) {
1786
+ const allKeys = data.map(rowKey);
1787
+ const { checked, indeterminate } = selectionState(
1788
+ selected.length,
1789
+ allKeys.length
1790
+ );
1791
+ const toggleAll = () => onSelectedChange?.(checked ? [] : allKeys);
1792
+ const toggleRow = (key) => onSelectedChange?.(
1793
+ selected.includes(key) ? selected.filter((k) => k !== key) : [...selected, key]
1794
+ );
1795
+ return /* @__PURE__ */ jsxs(Table, { className, children: [
1796
+ /* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border", children: [
1797
+ selectable && /* @__PURE__ */ jsx(TableCell, { header: true, size, className: "w-px", children: /* @__PURE__ */ jsx(
1798
+ Checkbox,
1799
+ {
1800
+ checked,
1801
+ indeterminate,
1802
+ onChange: toggleAll,
1803
+ "aria-label": "Select all rows"
1804
+ }
1805
+ ) }),
1806
+ columns.map((col) => /* @__PURE__ */ jsx(TableCell, { header: true, size, children: col.header }, col.key))
1807
+ ] }) }),
1808
+ /* @__PURE__ */ jsx(TableBody, { children: data.map((row) => {
1809
+ const key = rowKey(row);
1810
+ const isSelected = selected.includes(key);
1811
+ return /* @__PURE__ */ jsxs(TableRow, { zebra, selected: isSelected, children: [
1812
+ selectable && /* @__PURE__ */ jsx(TableCell, { size, className: "w-px", children: /* @__PURE__ */ jsx(
1813
+ Checkbox,
1814
+ {
1815
+ checked: isSelected,
1816
+ onChange: () => toggleRow(key),
1817
+ "aria-label": `Select row`
1818
+ }
1819
+ ) }),
1820
+ columns.map((col) => /* @__PURE__ */ jsx(TableCell, { size, children: col.cell(row) }, col.key))
1821
+ ] }, key);
1822
+ }) })
1823
+ ] });
1824
+ }
1825
+ var ToastContext = createContext(null);
1826
+ function useToast() {
1827
+ const ctx = useContext(ToastContext);
1828
+ if (!ctx) throw new Error("useToast must be used within a ToastProvider");
1829
+ return ctx;
1830
+ }
1831
+ var POSITION_CLASS = {
1832
+ "top-left": "top-4 left-4 items-start",
1833
+ "top-center": "top-4 left-1/2 -translate-x-1/2 items-center",
1834
+ "top-right": "top-4 right-4 items-end",
1835
+ "bottom-left": "bottom-4 left-4 items-start",
1836
+ "bottom-center": "bottom-4 left-1/2 -translate-x-1/2 items-center",
1837
+ "bottom-right": "bottom-4 right-4 items-end"
1838
+ };
1839
+ function ToastProvider({
1840
+ children,
1841
+ position = "bottom-center",
1842
+ defaultDuration = 5e3
1843
+ }) {
1844
+ const [toasts, setToasts] = useState([]);
1845
+ const counter = useRef(0);
1846
+ const dismiss = useCallback((id) => {
1847
+ setToasts((list2) => list2.filter((t) => t.id !== id));
1848
+ }, []);
1849
+ const toast = useCallback(
1850
+ (options) => {
1851
+ const id = ++counter.current;
1852
+ setToasts((list2) => [...list2, { ...options, id }]);
1853
+ const duration = options.duration ?? defaultDuration;
1854
+ if (duration > 0 && !options.closable) {
1855
+ setTimeout(() => dismiss(id), duration);
1856
+ }
1857
+ return id;
1858
+ },
1859
+ [defaultDuration, dismiss]
1860
+ );
1861
+ return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: { toast, dismiss }, children: [
1862
+ children,
1863
+ typeof document !== "undefined" && createPortal(
1864
+ /* @__PURE__ */ jsx(
1865
+ "div",
1866
+ {
1867
+ className: cn(
1868
+ "pointer-events-none fixed z-[100] flex flex-col gap-2",
1869
+ POSITION_CLASS[position]
1870
+ ),
1871
+ children: toasts.map((t) => /* @__PURE__ */ jsx(
1872
+ Snackbar,
1873
+ {
1874
+ icon: t.icon,
1875
+ onClose: t.closable ? () => dismiss(t.id) : void 0,
1876
+ className: "pointer-events-auto",
1877
+ children: t.message
1878
+ },
1879
+ t.id
1880
+ ))
1881
+ }
1882
+ ),
1883
+ document.body
1884
+ )
1885
+ ] });
1886
+ }
1887
+ function Snackbar({
1888
+ icon,
1889
+ onClose,
1890
+ children,
1891
+ className,
1892
+ ...props
1893
+ }) {
1894
+ return /* @__PURE__ */ jsxs(
1895
+ "div",
1896
+ {
1897
+ role: "status",
1898
+ "aria-live": "polite",
1899
+ className: cn(
1900
+ "flex min-w-48 max-w-sm items-center gap-3 rounded-[8px] bg-surface px-4 py-3 text-sm text-text shadow-lg",
1901
+ className
1902
+ ),
1903
+ ...props,
1904
+ children: [
1905
+ icon && /* @__PURE__ */ jsx("span", { className: "shrink-0 [&_svg]:size-5", children: icon }),
1906
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children }),
1907
+ onClose && /* @__PURE__ */ jsx(
1908
+ "button",
1909
+ {
1910
+ type: "button",
1911
+ "aria-label": "Dismiss",
1912
+ onClick: onClose,
1913
+ className: "-mr-1 inline-flex size-6 shrink-0 items-center justify-center rounded-full text-text-muted hover:bg-hover hover:text-text",
1914
+ children: /* @__PURE__ */ jsx(
1915
+ "svg",
1916
+ {
1917
+ viewBox: "0 0 24 24",
1918
+ className: "size-4",
1919
+ fill: "none",
1920
+ stroke: "currentColor",
1921
+ strokeWidth: "2",
1922
+ strokeLinecap: "round",
1923
+ "aria-hidden": true,
1924
+ children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6L6 18" })
1925
+ }
1926
+ )
1927
+ }
1928
+ )
1929
+ ]
1930
+ }
1931
+ );
1932
+ }
1933
+ var bubble = cva(
1934
+ "pointer-events-none absolute z-50 w-max max-w-xs rounded-[4px] bg-surface px-3 py-3 text-xs leading-4 text-text opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100",
1935
+ {
1936
+ variants: {
1937
+ placement: {
1938
+ top: "bottom-full left-1/2 mb-2 -translate-x-1/2",
1939
+ "top-start": "bottom-full left-0 mb-2",
1940
+ "top-end": "bottom-full right-0 mb-2",
1941
+ bottom: "top-full left-1/2 mt-2 -translate-x-1/2",
1942
+ "bottom-start": "top-full left-0 mt-2",
1943
+ "bottom-end": "top-full right-0 mt-2",
1944
+ left: "right-full top-1/2 mr-2 -translate-y-1/2",
1945
+ right: "left-full top-1/2 ml-2 -translate-y-1/2"
1946
+ },
1947
+ shadow: {
1948
+ true: "shadow-[0_0_1px_rgba(0,0,0,0.4),0_6px_6px_-6px_rgba(0,0,0,0.16)]",
1949
+ false: ""
1950
+ }
1951
+ },
1952
+ defaultVariants: { placement: "top", shadow: true }
1953
+ }
1954
+ );
1955
+ var arrow = cva("absolute size-2 rotate-45 bg-surface", {
1956
+ variants: {
1957
+ placement: {
1958
+ top: "bottom-[-4px] left-1/2 -translate-x-1/2",
1959
+ "top-start": "bottom-[-4px] left-3",
1960
+ "top-end": "bottom-[-4px] right-3",
1961
+ bottom: "top-[-4px] left-1/2 -translate-x-1/2",
1962
+ "bottom-start": "top-[-4px] left-3",
1963
+ "bottom-end": "top-[-4px] right-3",
1964
+ left: "right-[-4px] top-1/2 -translate-y-1/2",
1965
+ right: "left-[-4px] top-1/2 -translate-y-1/2"
1966
+ }
1967
+ },
1968
+ defaultVariants: { placement: "top" }
1969
+ });
1970
+ function Tooltip({
1971
+ content,
1972
+ placement = "top",
1973
+ shadow = true,
1974
+ pointer = true,
1975
+ children,
1976
+ className
1977
+ }) {
1978
+ return /* @__PURE__ */ jsxs("span", { className: cn("group relative inline-flex", className), children: [
1979
+ children,
1980
+ /* @__PURE__ */ jsxs("span", { role: "tooltip", className: bubble({ placement, shadow }), children: [
1981
+ content,
1982
+ pointer && /* @__PURE__ */ jsx("span", { className: arrow({ placement }), "aria-hidden": true })
1983
+ ] })
1984
+ ] });
1985
+ }
1986
+ var SidebarContext = createContext({
1987
+ collapsed: false
1988
+ });
1989
+ function Sidebar({
1990
+ collapsed = false,
1991
+ className,
1992
+ children,
1993
+ ...props
1994
+ }) {
1995
+ return /* @__PURE__ */ jsx(SidebarContext.Provider, { value: { collapsed }, children: /* @__PURE__ */ jsx(
1996
+ "nav",
1997
+ {
1998
+ "data-collapsed": collapsed || void 0,
1999
+ className: cn(
2000
+ "flex h-full flex-col gap-4 bg-surface text-text transition-[width] duration-200",
2001
+ collapsed ? "w-16 p-2" : "w-[280px] p-6",
2002
+ className
2003
+ ),
2004
+ ...props,
2005
+ children
2006
+ }
2007
+ ) });
2008
+ }
2009
+ function SidebarSection({
2010
+ title,
2011
+ divider,
2012
+ className,
2013
+ children,
2014
+ ...props
2015
+ }) {
2016
+ const { collapsed } = useContext(SidebarContext);
2017
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
2018
+ divider && /* @__PURE__ */ jsx("hr", { className: "border-t border-border" }),
2019
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1", className), ...props, children: [
2020
+ title && !collapsed && /* @__PURE__ */ jsx("span", { className: "px-2 text-sm text-text-muted", children: title }),
2021
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children })
2022
+ ] })
2023
+ ] });
2024
+ }
2025
+ function SidebarItem({
2026
+ icon,
2027
+ active,
2028
+ label,
2029
+ href,
2030
+ className,
2031
+ ...props
2032
+ }) {
2033
+ const { collapsed } = useContext(SidebarContext);
2034
+ const row = /* @__PURE__ */ jsxs(
2035
+ "a",
2036
+ {
2037
+ href,
2038
+ "aria-current": active ? "page" : void 0,
2039
+ className: cn(
2040
+ "flex items-center gap-3 rounded-[8px] px-2 py-2 text-sm transition-colors outline-none focus-visible:outline-2 focus-visible:outline-primary",
2041
+ collapsed && "justify-center",
2042
+ active ? "bg-primary-tint font-bold text-primary" : "text-text hover:bg-hover",
2043
+ className
2044
+ ),
2045
+ ...props,
2046
+ children: [
2047
+ icon && /* @__PURE__ */ jsx("span", { className: "flex size-6 shrink-0 items-center justify-center [&_svg]:size-6", children: icon }),
2048
+ !collapsed && /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: label })
2049
+ ]
2050
+ }
2051
+ );
2052
+ if (collapsed) {
2053
+ return /* @__PURE__ */ jsx(
2054
+ Tooltip,
2055
+ {
2056
+ content: label,
2057
+ placement: "right",
2058
+ className: "w-full [&>a]:w-full",
2059
+ children: row
2060
+ }
2061
+ );
2062
+ }
2063
+ return row;
2064
+ }
2065
+ function Calendar({ className, classNames, ...props }) {
2066
+ return /* @__PURE__ */ jsx(
2067
+ DayPicker,
2068
+ {
2069
+ className: cn(
2070
+ "rounded-[12px] bg-surface p-4 text-text [--rdp-day-width:40px] [--rdp-day-height:40px]",
2071
+ className
2072
+ ),
2073
+ classNames: {
2074
+ months: "relative flex flex-col gap-4 sm:flex-row",
2075
+ month: "flex flex-col gap-2",
2076
+ month_caption: "relative flex h-10 items-center justify-center",
2077
+ caption_label: "text-sm font-semibold text-text",
2078
+ nav: "absolute inset-x-0 top-0 flex h-10 items-center justify-between px-1",
2079
+ button_previous: "inline-flex size-8 items-center justify-center rounded-[8px] text-text-muted transition-colors hover:bg-hover hover:text-text disabled:opacity-40",
2080
+ button_next: "inline-flex size-8 items-center justify-center rounded-[8px] text-text-muted transition-colors hover:bg-hover hover:text-text disabled:opacity-40",
2081
+ month_grid: "border-collapse",
2082
+ weekdays: "flex",
2083
+ weekday: "flex size-10 items-center justify-center text-xs font-semibold text-text-muted",
2084
+ week: "flex",
2085
+ day: "p-0 text-center text-sm",
2086
+ day_button: "inline-flex size-10 items-center justify-center rounded-full text-text transition-colors hover:bg-hover focus-visible:outline-2 focus-visible:outline-primary aria-selected:font-semibold",
2087
+ today: "font-semibold text-primary",
2088
+ selected: "[&_button]:bg-text [&_button]:text-background [&_button]:hover:bg-text",
2089
+ range_start: "rounded-l-full bg-primary-tint [&_button]:bg-text [&_button]:text-background",
2090
+ range_end: "rounded-r-full bg-primary-tint [&_button]:bg-text [&_button]:text-background",
2091
+ range_middle: "bg-primary-tint [&_button]:rounded-none [&_button]:bg-transparent [&_button]:text-text [&_button]:hover:bg-primary-tint",
2092
+ outside: "text-text-muted opacity-50",
2093
+ disabled: "text-text-muted opacity-40",
2094
+ hidden: "invisible",
2095
+ ...classNames
2096
+ },
2097
+ components: {
2098
+ Chevron: ({ orientation, className: cls }) => /* @__PURE__ */ jsx(ChevronIcon, { orientation, className: cls })
2099
+ },
2100
+ ...props
2101
+ }
2102
+ );
2103
+ }
2104
+ function ChevronIcon({
2105
+ orientation,
2106
+ className
2107
+ }) {
2108
+ const d = orientation === "left" ? "m15 18-6-6 6-6" : orientation === "right" ? "m9 18 6-6-6-6" : orientation === "up" ? "m18 15-6-6-6 6" : "m6 9 6 6 6-6";
2109
+ return /* @__PURE__ */ jsx(
2110
+ "svg",
2111
+ {
2112
+ className: cn("size-5", className),
2113
+ viewBox: "0 0 24 24",
2114
+ fill: "none",
2115
+ stroke: "currentColor",
2116
+ strokeWidth: "2",
2117
+ strokeLinecap: "round",
2118
+ strokeLinejoin: "round",
2119
+ "aria-hidden": true,
2120
+ children: /* @__PURE__ */ jsx("path", { d })
2121
+ }
2122
+ );
2123
+ }
2124
+ var pad = (n) => String(n).padStart(2, "0");
2125
+ function TimePicker({
2126
+ value,
2127
+ onChange,
2128
+ minuteStep = 1,
2129
+ className
2130
+ }) {
2131
+ const hours = useMemo(() => Array.from({ length: 24 }, (_, i) => i), []);
2132
+ const minutes = useMemo(
2133
+ () => Array.from(
2134
+ { length: Math.ceil(60 / minuteStep) },
2135
+ (_, i) => i * minuteStep
2136
+ ),
2137
+ [minuteStep]
2138
+ );
2139
+ const set = (next) => onChange?.({
2140
+ hours: next.hours ?? value?.hours ?? 0,
2141
+ minutes: next.minutes ?? value?.minutes ?? 0
2142
+ });
2143
+ return /* @__PURE__ */ jsxs(
2144
+ "div",
2145
+ {
2146
+ className: cn(
2147
+ "flex h-[280px] gap-px overflow-hidden rounded-[16px] bg-background p-1",
2148
+ className
2149
+ ),
2150
+ children: [
2151
+ /* @__PURE__ */ jsx(
2152
+ Column,
2153
+ {
2154
+ items: hours,
2155
+ selected: value?.hours,
2156
+ onSelect: (h) => set({ hours: h }),
2157
+ label: "Hours"
2158
+ }
2159
+ ),
2160
+ /* @__PURE__ */ jsx(
2161
+ Column,
2162
+ {
2163
+ items: minutes,
2164
+ selected: value?.minutes,
2165
+ onSelect: (m) => set({ minutes: m }),
2166
+ label: "Minutes"
2167
+ }
2168
+ )
2169
+ ]
2170
+ }
2171
+ );
2172
+ }
2173
+ function Column({
2174
+ items,
2175
+ selected,
2176
+ onSelect,
2177
+ label
2178
+ }) {
2179
+ return /* @__PURE__ */ jsx(
2180
+ "ul",
2181
+ {
2182
+ "aria-label": label,
2183
+ className: "flex w-12 flex-col gap-1 overflow-y-auto scroll-smooth py-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
2184
+ children: items.map((n) => {
2185
+ const active = selected === n;
2186
+ return /* @__PURE__ */ jsx("li", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
2187
+ "button",
2188
+ {
2189
+ type: "button",
2190
+ "aria-pressed": active,
2191
+ onClick: () => onSelect(n),
2192
+ className: cn(
2193
+ "flex size-9 items-center justify-center rounded-full text-xs font-semibold transition-colors",
2194
+ active ? "bg-text text-background" : "text-text hover:bg-hover"
2195
+ ),
2196
+ children: pad(n)
2197
+ }
2198
+ ) }, n);
2199
+ })
2200
+ }
2201
+ );
2202
+ }
2203
+ var startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
2204
+ var addDays = (d, n) => {
2205
+ const r = startOfDay(d);
2206
+ r.setDate(r.getDate() + n);
2207
+ return r;
2208
+ };
2209
+ var startOfWeek = (d) => addDays(d, -((d.getDay() + 6) % 7));
2210
+ var startOfMonth = (d) => new Date(d.getFullYear(), d.getMonth(), 1);
2211
+ var endOfMonth = (d) => new Date(d.getFullYear(), d.getMonth() + 1, 0);
2212
+ function presetRanges(now) {
2213
+ const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
2214
+ return {
2215
+ today: { from: startOfDay(now), to: startOfDay(now) },
2216
+ yesterday: { from: addDays(now, -1), to: addDays(now, -1) },
2217
+ thisWeek: { from: startOfWeek(now), to: startOfDay(now) },
2218
+ lastWeek: {
2219
+ from: addDays(startOfWeek(now), -7),
2220
+ to: addDays(startOfWeek(now), -1)
2221
+ },
2222
+ thisMonth: { from: startOfMonth(now), to: startOfDay(now) },
2223
+ lastMonth: { from: startOfMonth(lastMonth), to: endOfMonth(lastMonth) }
2224
+ };
2225
+ }
2226
+ var defaultPresets = [
2227
+ { label: "Today", range: () => presetRanges(/* @__PURE__ */ new Date()).today },
2228
+ { label: "Yesterday", range: () => presetRanges(/* @__PURE__ */ new Date()).yesterday },
2229
+ { label: "This week", range: () => presetRanges(/* @__PURE__ */ new Date()).thisWeek },
2230
+ { label: "Last week", range: () => presetRanges(/* @__PURE__ */ new Date()).lastWeek },
2231
+ { label: "This month", range: () => presetRanges(/* @__PURE__ */ new Date()).thisMonth },
2232
+ { label: "Last month", range: () => presetRanges(/* @__PURE__ */ new Date()).lastMonth }
2233
+ ];
2234
+ function DateRangePicker({
2235
+ value,
2236
+ onApply,
2237
+ presets = defaultPresets,
2238
+ numberOfMonths = 2,
2239
+ className
2240
+ }) {
2241
+ const [range, setRange] = useState(value);
2242
+ return /* @__PURE__ */ jsxs(
2243
+ "div",
2244
+ {
2245
+ className: cn(
2246
+ "inline-flex flex-col overflow-hidden rounded-[12px] bg-surface shadow-[0_0_1px_rgba(0,0,0,0.4),0_6px_6px_-6px_rgba(0,0,0,0.16)]",
2247
+ className
2248
+ ),
2249
+ children: [
2250
+ /* @__PURE__ */ jsxs("div", { className: "flex", children: [
2251
+ /* @__PURE__ */ jsx("ul", { className: "flex w-40 shrink-0 flex-col gap-1 border-r border-border p-2", children: presets.map((preset, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2252
+ "button",
2253
+ {
2254
+ type: "button",
2255
+ onClick: () => setRange(preset.range()),
2256
+ className: "w-full rounded-[8px] px-3 py-2 text-left text-sm text-text transition-colors hover:bg-hover",
2257
+ children: preset.label
2258
+ }
2259
+ ) }, i)) }),
2260
+ /* @__PURE__ */ jsx(
2261
+ Calendar,
2262
+ {
2263
+ mode: "range",
2264
+ numberOfMonths,
2265
+ selected: range,
2266
+ onSelect: setRange,
2267
+ className: "p-3 shadow-none"
2268
+ }
2269
+ )
2270
+ ] }),
2271
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end border-t border-border p-3", children: /* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => onApply?.(range), children: "Apply" }) })
2272
+ ]
2273
+ }
2274
+ );
2275
+ }
2276
+ var quarters = [1, 2, 3, 4];
2277
+ var monthsOf = (q) => ["Jan\u2013Mar", "Apr\u2013Jun", "Jul\u2013Sep", "Oct\u2013Dec"][q - 1];
2278
+ function QuarterPicker({
2279
+ value,
2280
+ onChange,
2281
+ year = value?.year ?? (/* @__PURE__ */ new Date()).getFullYear(),
2282
+ onYearChange,
2283
+ className
2284
+ }) {
2285
+ return /* @__PURE__ */ jsxs(
2286
+ "div",
2287
+ {
2288
+ className: cn(
2289
+ "w-[280px] rounded-[12px] bg-surface p-4 text-text",
2290
+ className
2291
+ ),
2292
+ children: [
2293
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex h-10 items-center justify-between", children: [
2294
+ /* @__PURE__ */ jsx(
2295
+ NavButton,
2296
+ {
2297
+ label: "Previous year",
2298
+ onClick: () => onYearChange?.(year - 1),
2299
+ children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" })
2300
+ }
2301
+ ),
2302
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: year }),
2303
+ /* @__PURE__ */ jsx(NavButton, { label: "Next year", onClick: () => onYearChange?.(year + 1), children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) })
2304
+ ] }),
2305
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: quarters.map((q) => {
2306
+ const active = value?.year === year && value?.quarter === q;
2307
+ return /* @__PURE__ */ jsxs(
2308
+ "button",
2309
+ {
2310
+ type: "button",
2311
+ "aria-pressed": active,
2312
+ onClick: () => onChange?.({ year, quarter: q }),
2313
+ className: cn(
2314
+ "flex flex-col items-center justify-center rounded-[8px] py-4 transition-colors",
2315
+ active ? "bg-text text-background" : "hover:bg-hover"
2316
+ ),
2317
+ children: [
2318
+ /* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold", children: [
2319
+ "Q",
2320
+ q
2321
+ ] }),
2322
+ /* @__PURE__ */ jsx(
2323
+ "span",
2324
+ {
2325
+ className: cn(
2326
+ "text-xs",
2327
+ active ? "text-background/70" : "text-text-muted"
2328
+ ),
2329
+ children: monthsOf(q)
2330
+ }
2331
+ )
2332
+ ]
2333
+ },
2334
+ q
2335
+ );
2336
+ }) })
2337
+ ]
2338
+ }
2339
+ );
2340
+ }
2341
+ function NavButton({
2342
+ label,
2343
+ onClick,
2344
+ children
2345
+ }) {
2346
+ return /* @__PURE__ */ jsx(
2347
+ "button",
2348
+ {
2349
+ type: "button",
2350
+ "aria-label": label,
2351
+ onClick,
2352
+ className: "inline-flex size-8 items-center justify-center rounded-[8px] text-text-muted transition-colors hover:bg-hover hover:text-text",
2353
+ children: /* @__PURE__ */ jsx(
2354
+ "svg",
2355
+ {
2356
+ className: "size-5",
2357
+ viewBox: "0 0 24 24",
2358
+ fill: "none",
2359
+ stroke: "currentColor",
2360
+ strokeWidth: "2",
2361
+ strokeLinecap: "round",
2362
+ strokeLinejoin: "round",
2363
+ "aria-hidden": true,
2364
+ children
2365
+ }
2366
+ )
2367
+ }
2368
+ );
2369
+ }
2370
+ var drawer = cva(
2371
+ "fixed bg-surface p-1 shadow-lg backdrop:bg-overlay open:flex flex-col m-0",
2372
+ {
2373
+ variants: {
2374
+ side: {
2375
+ right: "inset-y-0 right-0 h-full w-[448px] max-w-full rounded-l-[8px]",
2376
+ left: "inset-y-0 left-0 h-full w-[448px] max-w-full rounded-r-[8px]",
2377
+ top: "inset-x-0 top-0 h-auto max-h-full w-full rounded-b-[8px]",
2378
+ bottom: "inset-x-0 bottom-0 h-auto max-h-full w-full rounded-t-[8px]"
2379
+ }
2380
+ },
2381
+ defaultVariants: { side: "right" }
2382
+ }
2383
+ );
2384
+ function Drawer({
2385
+ open,
2386
+ onClose,
2387
+ side = "right",
2388
+ className,
2389
+ children,
2390
+ ...props
2391
+ }) {
2392
+ const ref = useRef(null);
2393
+ useEffect(() => {
2394
+ const dialog = ref.current;
2395
+ if (!dialog) return;
2396
+ if (open && !dialog.open) dialog.showModal();
2397
+ else if (!open && dialog.open) dialog.close();
2398
+ }, [open]);
2399
+ return /* @__PURE__ */ jsx(
2400
+ "dialog",
2401
+ {
2402
+ ref,
2403
+ className: cn(drawer({ side }), className),
2404
+ onClose,
2405
+ onClick: (e) => {
2406
+ if (e.target === ref.current) onClose?.();
2407
+ },
2408
+ ...props,
2409
+ children: /* @__PURE__ */ jsx(
2410
+ "div",
2411
+ {
2412
+ className: "flex size-full flex-col overflow-auto",
2413
+ onClick: (e) => e.stopPropagation(),
2414
+ children
2415
+ }
2416
+ )
2417
+ }
2418
+ );
2419
+ }
2420
+ var modal = cva(
2421
+ "m-auto w-full rounded-[12px] bg-surface p-0 shadow-lg backdrop:bg-overlay open:flex flex-col",
2422
+ {
2423
+ variants: {
2424
+ size: {
2425
+ sm: "max-w-sm",
2426
+ md: "max-w-md",
2427
+ lg: "max-w-4xl",
2428
+ xl: "max-w-7xl"
2429
+ }
2430
+ },
2431
+ defaultVariants: { size: "md" }
2432
+ }
2433
+ );
2434
+ function Modal({
2435
+ open,
2436
+ onClose,
2437
+ size = "md",
2438
+ title,
2439
+ className,
2440
+ children,
2441
+ ...props
2442
+ }) {
2443
+ const ref = useRef(null);
2444
+ useEffect(() => {
2445
+ const dialog = ref.current;
2446
+ if (!dialog) return;
2447
+ if (open && !dialog.open) dialog.showModal();
2448
+ else if (!open && dialog.open) dialog.close();
2449
+ }, [open]);
2450
+ return /* @__PURE__ */ jsx(
2451
+ "dialog",
2452
+ {
2453
+ ref,
2454
+ className: cn(modal({ size }), className),
2455
+ onClose,
2456
+ onClick: (e) => {
2457
+ if (e.target === ref.current) onClose?.();
2458
+ },
2459
+ ...props,
2460
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", onClick: (e) => e.stopPropagation(), children: [
2461
+ (title || onClose) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 border-b border-border px-6 py-4", children: [
2462
+ title ? /* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-text", children: title }) : /* @__PURE__ */ jsx("span", {}),
2463
+ onClose && /* @__PURE__ */ jsx(
2464
+ "button",
2465
+ {
2466
+ type: "button",
2467
+ "aria-label": "Close",
2468
+ onClick: onClose,
2469
+ className: "-mr-2 inline-flex size-8 shrink-0 items-center justify-center rounded-full text-text-muted hover:bg-hover hover:text-text focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2",
2470
+ children: /* @__PURE__ */ jsx(
2471
+ "svg",
2472
+ {
2473
+ viewBox: "0 0 24 24",
2474
+ className: "size-5",
2475
+ fill: "none",
2476
+ stroke: "currentColor",
2477
+ strokeWidth: "2",
2478
+ strokeLinecap: "round",
2479
+ "aria-hidden": true,
2480
+ children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6L6 18" })
2481
+ }
2482
+ )
2483
+ }
2484
+ )
2485
+ ] }),
2486
+ /* @__PURE__ */ jsx("div", { className: "p-6", children })
2487
+ ] })
2488
+ }
2489
+ );
2490
+ }
2491
+ var DOTS = "\u2026";
2492
+ function paginationRange(current, total, siblings = 1) {
2493
+ const totalShown = siblings * 2 + 5;
2494
+ if (total <= totalShown) {
2495
+ return Array.from({ length: total }, (_, i) => i + 1);
2496
+ }
2497
+ const left = Math.max(current - siblings, 1);
2498
+ const right = Math.min(current + siblings, total);
2499
+ const showLeftDots = left > 2;
2500
+ const showRightDots = right < total - 1;
2501
+ if (!showLeftDots && showRightDots) {
2502
+ const len = siblings * 2 + 3;
2503
+ return [...Array.from({ length: len }, (_, i) => i + 1), DOTS, total];
2504
+ }
2505
+ if (showLeftDots && !showRightDots) {
2506
+ const len = siblings * 2 + 3;
2507
+ return [
2508
+ 1,
2509
+ DOTS,
2510
+ ...Array.from({ length: len }, (_, i) => total - len + 1 + i)
2511
+ ];
2512
+ }
2513
+ return [
2514
+ 1,
2515
+ DOTS,
2516
+ ...Array.from({ length: right - left + 1 }, (_, i) => left + i),
2517
+ DOTS,
2518
+ total
2519
+ ];
2520
+ }
2521
+ function Pagination({
2522
+ page,
2523
+ total,
2524
+ siblings = 1,
2525
+ onPageChange,
2526
+ className,
2527
+ ...props
2528
+ }) {
2529
+ const range = paginationRange(page, total, siblings);
2530
+ return /* @__PURE__ */ jsxs(
2531
+ "nav",
2532
+ {
2533
+ "aria-label": "Pagination",
2534
+ className: cn("flex items-center gap-1", className),
2535
+ ...props,
2536
+ children: [
2537
+ /* @__PURE__ */ jsx(
2538
+ PageButton,
2539
+ {
2540
+ "aria-label": "Previous page",
2541
+ disabled: page <= 1,
2542
+ onClick: () => onPageChange(page - 1),
2543
+ children: /* @__PURE__ */ jsx(Chevron, { dir: "left" })
2544
+ }
2545
+ ),
2546
+ range.map(
2547
+ (item, i) => item === DOTS ? /* @__PURE__ */ jsx("span", { className: "px-2 text-sm text-text-muted", children: DOTS }, `dots-${i}`) : /* @__PURE__ */ jsx(
2548
+ PageButton,
2549
+ {
2550
+ "aria-label": `Page ${item}`,
2551
+ "aria-current": item === page ? "page" : void 0,
2552
+ selected: item === page,
2553
+ onClick: () => onPageChange(item),
2554
+ children: item
2555
+ },
2556
+ item
2557
+ )
2558
+ ),
2559
+ /* @__PURE__ */ jsx(
2560
+ PageButton,
2561
+ {
2562
+ "aria-label": "Next page",
2563
+ disabled: page >= total,
2564
+ onClick: () => onPageChange(page + 1),
2565
+ children: /* @__PURE__ */ jsx(Chevron, { dir: "right" })
2566
+ }
2567
+ )
2568
+ ]
2569
+ }
2570
+ );
2571
+ }
2572
+ function PageButton({
2573
+ selected,
2574
+ className,
2575
+ ...props
2576
+ }) {
2577
+ return /* @__PURE__ */ jsx(
2578
+ "button",
2579
+ {
2580
+ type: "button",
2581
+ className: cn(
2582
+ "inline-flex size-8 items-center justify-center rounded-[8px] text-sm transition-colors",
2583
+ "focus-visible:outline-2 focus-visible:outline-text focus-visible:outline-offset-2",
2584
+ "disabled:pointer-events-none disabled:opacity-50",
2585
+ selected ? "bg-primary font-bold text-white" : "text-text hover:bg-hover",
2586
+ className
2587
+ ),
2588
+ ...props
2589
+ }
2590
+ );
2591
+ }
2592
+ function Chevron({ dir }) {
2593
+ return /* @__PURE__ */ jsx(
2594
+ "svg",
2595
+ {
2596
+ className: cn("size-4", dir === "left" && "rotate-180"),
2597
+ viewBox: "0 0 16 16",
2598
+ fill: "none",
2599
+ stroke: "currentColor",
2600
+ strokeWidth: "1.5",
2601
+ strokeLinecap: "round",
2602
+ strokeLinejoin: "round",
2603
+ "aria-hidden": true,
2604
+ children: /* @__PURE__ */ jsx("path", { d: "m6 4 4 4-4 4" })
2605
+ }
2606
+ );
2607
+ }
2608
+ function clampPercent(value) {
2609
+ return Math.max(0, Math.min(100, value));
2610
+ }
2611
+ var LINEAR_THICKNESS = {
2612
+ "6xs": "h-0.5",
2613
+ "5xs": "h-1",
2614
+ "4xs": "h-2",
2615
+ "3xs": "h-3",
2616
+ "2xs": "h-4"
2617
+ };
2618
+ var CIRCULAR_SIZE = {
2619
+ "2xs": 16,
2620
+ xs: 24,
2621
+ sm: 32,
2622
+ md: 40,
2623
+ lg: 48
2624
+ };
2625
+ function Progress({
2626
+ value,
2627
+ className,
2628
+ label = "Progress",
2629
+ ...rest
2630
+ }) {
2631
+ const pct = clampPercent(value);
2632
+ const common = {
2633
+ role: "progressbar",
2634
+ "aria-label": label,
2635
+ "aria-valuenow": Math.round(pct),
2636
+ "aria-valuemin": 0,
2637
+ "aria-valuemax": 100
2638
+ };
2639
+ if (rest.variant === "circular") {
2640
+ const px = CIRCULAR_SIZE[rest.size ?? "md"];
2641
+ const stroke = Math.max(2, Math.round(px / 8));
2642
+ const r = (px - stroke) / 2;
2643
+ const circumference = 2 * Math.PI * r;
2644
+ return /* @__PURE__ */ jsxs(
2645
+ "svg",
2646
+ {
2647
+ ...common,
2648
+ width: px,
2649
+ height: px,
2650
+ viewBox: `0 0 ${px} ${px}`,
2651
+ className: cn("-rotate-90", className),
2652
+ children: [
2653
+ /* @__PURE__ */ jsx(
2654
+ "circle",
2655
+ {
2656
+ cx: px / 2,
2657
+ cy: px / 2,
2658
+ r,
2659
+ fill: "none",
2660
+ strokeWidth: stroke,
2661
+ className: "stroke-background"
2662
+ }
2663
+ ),
2664
+ /* @__PURE__ */ jsx(
2665
+ "circle",
2666
+ {
2667
+ cx: px / 2,
2668
+ cy: px / 2,
2669
+ r,
2670
+ fill: "none",
2671
+ strokeWidth: stroke,
2672
+ strokeLinecap: "round",
2673
+ strokeDasharray: circumference,
2674
+ strokeDashoffset: circumference * (1 - pct / 100),
2675
+ className: "stroke-primary transition-[stroke-dashoffset]"
2676
+ }
2677
+ )
2678
+ ]
2679
+ }
2680
+ );
2681
+ }
2682
+ const thickness = LINEAR_THICKNESS[rest.size ?? "4xs"];
2683
+ return /* @__PURE__ */ jsx(
2684
+ "div",
2685
+ {
2686
+ ...common,
2687
+ className: cn(
2688
+ "w-full overflow-hidden rounded-full bg-background",
2689
+ thickness,
2690
+ className
2691
+ ),
2692
+ children: /* @__PURE__ */ jsx(
2693
+ "div",
2694
+ {
2695
+ className: "h-full rounded-full bg-primary transition-[width]",
2696
+ style: { width: `${pct}%` }
2697
+ }
2698
+ )
2699
+ }
2700
+ );
2701
+ }
2702
+ var DropdownContext = createContext(null);
2703
+ function Dropdown({
2704
+ trigger,
2705
+ children,
2706
+ align = "start",
2707
+ className
2708
+ }) {
2709
+ const [open, setOpen] = useState(false);
2710
+ return /* @__PURE__ */ jsx(DropdownContext.Provider, { value: { close: () => setOpen(false) }, children: /* @__PURE__ */ jsx(
2711
+ Popover,
2712
+ {
2713
+ trigger,
2714
+ open,
2715
+ onOpenChange: setOpen,
2716
+ align,
2717
+ className: cn("min-w-40", className),
2718
+ children: /* @__PURE__ */ jsx("div", { role: "menu", className: "flex flex-col", children })
2719
+ }
2720
+ ) });
2721
+ }
2722
+ function DropdownItem({
2723
+ leading,
2724
+ trailing,
2725
+ description,
2726
+ selected,
2727
+ children,
2728
+ className,
2729
+ onClick,
2730
+ disabled,
2731
+ ...props
2732
+ }) {
2733
+ const ctx = useContext(DropdownContext);
2734
+ const end = trailing ?? (selected ? /* @__PURE__ */ jsx(CheckMark, {}) : null);
2735
+ return /* @__PURE__ */ jsxs(
2736
+ "button",
2737
+ {
2738
+ type: "button",
2739
+ role: "menuitem",
2740
+ "aria-current": selected || void 0,
2741
+ disabled,
2742
+ onClick: (e) => {
2743
+ onClick?.(e);
2744
+ ctx?.close();
2745
+ },
2746
+ className: cn(
2747
+ "flex w-full gap-2 rounded-[8px] px-3 py-2 text-left text-sm text-text",
2748
+ description ? "items-start" : "items-center",
2749
+ "hover:bg-hover focus-visible:bg-hover focus-visible:outline-none",
2750
+ "disabled:pointer-events-none disabled:opacity-50",
2751
+ className
2752
+ ),
2753
+ ...props,
2754
+ children: [
2755
+ leading && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: leading }),
2756
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-1 flex-col", children: [
2757
+ /* @__PURE__ */ jsx("span", { children }),
2758
+ description && /* @__PURE__ */ jsx("span", { className: "text-xs text-text-muted", children: description })
2759
+ ] }),
2760
+ end && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: end })
2761
+ ]
2762
+ }
2763
+ );
2764
+ }
2765
+ function CheckMark() {
2766
+ return /* @__PURE__ */ jsx(
2767
+ "svg",
2768
+ {
2769
+ className: "size-4 text-primary",
2770
+ viewBox: "0 0 24 24",
2771
+ fill: "none",
2772
+ stroke: "currentColor",
2773
+ strokeWidth: "2",
2774
+ strokeLinecap: "round",
2775
+ strokeLinejoin: "round",
2776
+ "aria-hidden": true,
2777
+ children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" })
2778
+ }
2779
+ );
2780
+ }
2781
+ function filterOptions(options, query) {
2782
+ const q = query.trim().toLowerCase();
2783
+ if (!q) return options;
2784
+ return options.filter((o) => o.label.toLowerCase().includes(q));
2785
+ }
2786
+ function Combobox(props) {
2787
+ const {
2788
+ options,
2789
+ placeholder = "Select\u2026",
2790
+ disabled,
2791
+ className,
2792
+ emptyMessage = "No results"
2793
+ } = props;
2794
+ const [open, setOpen] = useState(false);
2795
+ const [query, setQuery] = useState("");
2796
+ const [activeIndex, setActiveIndex] = useState(0);
2797
+ const filtered = useMemo(
2798
+ () => filterOptions(options, query),
2799
+ [options, query]
2800
+ );
2801
+ const selectedValues = props.multiple ? props.value : props.value ? [props.value] : [];
2802
+ const labelFor = (v) => options.find((o) => o.value === v)?.label ?? v;
2803
+ const isSelected = (v) => selectedValues.includes(v);
2804
+ const commit = (v) => {
2805
+ if (props.multiple) {
2806
+ const set = new Set(props.value);
2807
+ if (set.has(v)) set.delete(v);
2808
+ else set.add(v);
2809
+ props.onChange([...set]);
2810
+ setQuery("");
2811
+ } else {
2812
+ props.onChange(v);
2813
+ setQuery("");
2814
+ setOpen(false);
2815
+ }
2816
+ };
2817
+ const onKeyDown = (e) => {
2818
+ if (e.key === "ArrowDown") {
2819
+ e.preventDefault();
2820
+ setOpen(true);
2821
+ setActiveIndex((i) => Math.min(i + 1, filtered.length - 1));
2822
+ } else if (e.key === "ArrowUp") {
2823
+ e.preventDefault();
2824
+ setActiveIndex((i) => Math.max(i - 1, 0));
2825
+ } else if (e.key === "Enter") {
2826
+ e.preventDefault();
2827
+ const opt = filtered[activeIndex];
2828
+ if (opt) commit(opt.value);
2829
+ } else if (e.key === "Escape") {
2830
+ setOpen(false);
2831
+ } else if (e.key === "Backspace" && !query && props.multiple && props.value.length) {
2832
+ props.onChange(props.value.slice(0, -1));
2833
+ }
2834
+ };
2835
+ const singleSelectedLabel = !props.multiple && props.value ? labelFor(props.value) : "";
2836
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative w-full", className), children: [
2837
+ /* @__PURE__ */ jsxs(
2838
+ "div",
2839
+ {
2840
+ className: cn(
2841
+ "flex min-h-10 w-full flex-wrap items-center gap-1 rounded-[8px] border border-border bg-surface px-2 py-1 text-sm",
2842
+ "focus-within:border-[1.5px] focus-within:border-primary",
2843
+ disabled && "pointer-events-none opacity-50"
2844
+ ),
2845
+ onClick: () => setOpen(true),
2846
+ children: [
2847
+ props.multiple && props.value.map((v) => /* @__PURE__ */ jsx(
2848
+ Chip,
2849
+ {
2850
+ size: "sm",
2851
+ onDismiss: () => props.onChange(props.value.filter((x) => x !== v)),
2852
+ children: labelFor(v)
2853
+ },
2854
+ v
2855
+ )),
2856
+ /* @__PURE__ */ jsx(
2857
+ "input",
2858
+ {
2859
+ className: "min-w-[60px] flex-1 bg-transparent text-text outline-none placeholder:text-text-muted",
2860
+ value: query || (!open ? singleSelectedLabel : ""),
2861
+ placeholder: selectedValues.length ? "" : placeholder,
2862
+ disabled,
2863
+ role: "combobox",
2864
+ "aria-expanded": open,
2865
+ "aria-autocomplete": "list",
2866
+ onChange: (e) => {
2867
+ setQuery(e.target.value);
2868
+ setOpen(true);
2869
+ setActiveIndex(0);
2870
+ },
2871
+ onFocus: () => setOpen(true),
2872
+ onBlur: () => setTimeout(() => setOpen(false), 120),
2873
+ onKeyDown
2874
+ }
2875
+ ),
2876
+ /* @__PURE__ */ jsx(
2877
+ ChevronDown4,
2878
+ {
2879
+ className: cn(
2880
+ "size-5 shrink-0 text-text-muted transition-transform",
2881
+ open && "rotate-180"
2882
+ )
2883
+ }
2884
+ )
2885
+ ]
2886
+ }
2887
+ ),
2888
+ open && /* @__PURE__ */ jsx(
2889
+ "ul",
2890
+ {
2891
+ role: "listbox",
2892
+ className: "absolute z-50 mt-1 max-h-60 w-full overflow-auto rounded-[12px] border border-border bg-surface p-1 shadow-lg",
2893
+ children: filtered.length === 0 ? /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-sm text-text-muted", children: emptyMessage }) : filtered.map((opt, i) => /* @__PURE__ */ jsxs(
2894
+ "li",
2895
+ {
2896
+ role: "option",
2897
+ "aria-selected": isSelected(opt.value),
2898
+ onMouseEnter: () => setActiveIndex(i),
2899
+ onMouseDown: (e) => {
2900
+ e.preventDefault();
2901
+ commit(opt.value);
2902
+ },
2903
+ className: cn(
2904
+ "flex cursor-pointer items-center justify-between rounded-[8px] px-3 py-2 text-sm text-text",
2905
+ i === activeIndex && "bg-hover"
2906
+ ),
2907
+ children: [
2908
+ opt.label,
2909
+ isSelected(opt.value) && /* @__PURE__ */ jsx(Check, { className: "size-4 text-primary" })
2910
+ ]
2911
+ },
2912
+ opt.value
2913
+ ))
2914
+ }
2915
+ )
2916
+ ] });
2917
+ }
2918
+ function ChevronDown4({ className }) {
2919
+ return /* @__PURE__ */ jsx(
2920
+ "svg",
2921
+ {
2922
+ className,
2923
+ viewBox: "0 0 24 24",
2924
+ fill: "none",
2925
+ stroke: "currentColor",
2926
+ strokeWidth: "2",
2927
+ strokeLinecap: "round",
2928
+ strokeLinejoin: "round",
2929
+ "aria-hidden": true,
2930
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
2931
+ }
2932
+ );
2933
+ }
2934
+ function Check({ className }) {
2935
+ return /* @__PURE__ */ jsx(
2936
+ "svg",
2937
+ {
2938
+ className,
2939
+ viewBox: "0 0 24 24",
2940
+ fill: "none",
2941
+ stroke: "currentColor",
2942
+ strokeWidth: "2",
2943
+ strokeLinecap: "round",
2944
+ strokeLinejoin: "round",
2945
+ "aria-hidden": true,
2946
+ children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" })
2947
+ }
2948
+ );
2949
+ }
2950
+
2951
+ export { Accordion, Alert, AuthCode, Avatar, Breadcrumbs, Button, Calendar, Carousel, Checkbox, Chip, Combobox, Container, DataTable, DateRangePicker, Drawer, Dropdown, DropdownItem, EmptyState, GroupField, Loader, Modal, Pagination, Popover, Progress, QuarterPicker, Radio, Search, SegmentedControl, Select, Sidebar, SidebarItem, SidebarSection, Snackbar, Switch, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableHead, TableRow, Tabs, Tag, TextInput, TextInputGroup, Textarea, TimePicker, ToastProvider, Tooltip, buttonVariants, clampPercent, cn, defaultPresets, filterOptions, paginationRange, presetRanges, selectionState, useToast };