@aircall/ds 0.9.1 → 0.11.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 CHANGED
@@ -1,13 +1,13 @@
1
1
  import { Accordion as Accordion$1 } from "@base-ui/react/accordion";
2
2
  import { clsx } from "clsx";
3
3
  import { extendTailwindMerge } from "tailwind-merge";
4
- import { ArrowDownToLine, ArrowLeft, ArrowRight, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CircleAlert, CircleAlertIcon, CircleCheck, FLAGS, FlagUs, Info, Loader2Icon, MinusIcon, MoreHorizontalIcon, PanelLeftIcon, Search, Smile, TriangleAlertIcon, XIcon } from "@aircall/react-icons";
4
+ import { ArrowDownToLine, ArrowLeft, ArrowRight, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ChevronsUpDownIcon, CircleAlert, CircleAlertIcon, CircleCheck, FLAGS, FlagUs, Info, Loader2Icon, MinusIcon, MoreHorizontalIcon, PanelLeftIcon, Search, Smile, TriangleAlertIcon, XIcon } from "@aircall/react-icons";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import * as React$1 from "react";
7
7
  import React, { createContext, useContext, useMemo } from "react";
8
- import { AlertDialog as AlertDialog$1 } from "@base-ui/react/alert-dialog";
9
8
  import { Button as Button$1 } from "@base-ui/react/button";
10
9
  import { cva } from "class-variance-authority";
10
+ import { AlertDialog as AlertDialog$1 } from "@base-ui/react/alert-dialog";
11
11
  import { Avatar as Avatar$1 } from "@base-ui/react/avatar";
12
12
  import { mergeProps } from "@base-ui/react/merge-props";
13
13
  import { useRender } from "@base-ui/react/use-render";
@@ -15,6 +15,7 @@ import { Separator as Separator$1 } from "@base-ui/react/separator";
15
15
  import { DayPicker, getDefaultClassNames } from "react-day-picker";
16
16
  import useEmblaCarousel from "embla-carousel-react";
17
17
  import { Checkbox as Checkbox$1 } from "@base-ui/react/checkbox";
18
+ import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
18
19
  import { Collapsible as Collapsible$1 } from "@base-ui/react/collapsible";
19
20
  import { Combobox as Combobox$1 } from "@base-ui/react";
20
21
  import { Command as Command$1 } from "cmdk";
@@ -23,7 +24,6 @@ import { flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-tabl
23
24
  import { Drawer as DrawerPrimitive } from "@base-ui/react/drawer";
24
25
  import { Radio } from "@base-ui/react/radio";
25
26
  import { RadioGroup as RadioGroup$1 } from "@base-ui/react/radio-group";
26
- import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
27
27
  import { Menu } from "@base-ui/react/menu";
28
28
  import EmojiReactPicker, { Categories, EmojiStyle, SuggestionMode, Theme } from "emoji-picker-react";
29
29
  import { Popover as Popover$1 } from "@base-ui/react/popover";
@@ -34,6 +34,7 @@ import { Select as Select$1 } from "@base-ui/react/select";
34
34
  import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
35
35
  import { Slider as Slider$1 } from "@base-ui/react/slider";
36
36
  import { Toaster as Toaster$1, toast } from "sonner";
37
+ import { useControllableState } from "@aircall/hooks";
37
38
  import { Switch as Switch$1 } from "@base-ui/react/switch";
38
39
  import { Tabs as Tabs$1 } from "@base-ui/react/tabs";
39
40
  import { Toggle as Toggle$1 } from "@base-ui/react/toggle";
@@ -157,7 +158,7 @@ const buttonVariants = cva("group/button inline-flex shrink-0 cursor-pointer ite
157
158
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
158
159
  outline: "border-border bg-background hover:bg-muted hover:text-accent-foreground aria-expanded:bg-muted aria-expanded:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
159
160
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
160
- ghost: "hover:bg-muted hover:text-accent-foreground aria-expanded:bg-muted aria-expanded:text-accent-foreground dark:hover:bg-muted/50",
161
+ ghost: "hover:bg-muted hover:text-accent-foreground aria-expanded:bg-muted aria-expanded:text-accent-foreground dark:hover:bg-input/50",
161
162
  destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
162
163
  link: "text-primary underline underline-offset-4"
163
164
  },
@@ -177,9 +178,10 @@ const buttonVariants = cva("group/button inline-flex shrink-0 cursor-pointer ite
177
178
  block: false
178
179
  }
179
180
  });
180
- const Button = React$1.forwardRef(({ className, variant = "default", size = "default", block = false, ...props }, ref) => {
181
+ const Button = React$1.forwardRef((componentProps, forwardRef) => {
182
+ const { className, variant = "default", size = "default", block = false, ...props } = componentProps;
181
183
  return /* @__PURE__ */ jsx(Button$1, {
182
- ref,
184
+ ref: forwardRef,
183
185
  "data-slot": "button",
184
186
  "data-variant": variant,
185
187
  className: cn(buttonVariants({
@@ -193,6 +195,60 @@ const Button = React$1.forwardRef(({ className, variant = "default", size = "def
193
195
  });
194
196
  Button.displayName = "Button";
195
197
 
198
+ //#endregion
199
+ //#region src/components/action-bar.tsx
200
+ /** Fixed height of the ActionBar (px). Single source of truth — the bar uses it
201
+ * for its own height and consumers (e.g. DataTable) reserve space with it. */
202
+ const ActionBarHeight = 48;
203
+ /**
204
+ * Lets a parent (e.g. `DataTable`) supply the selected `count` and an `onClear`
205
+ * handler so an `<ActionBar>` rendered inside it doesn't have to be passed them.
206
+ * Explicit `count`/`onClear` props on `ActionBar` always take precedence, so the
207
+ * component still works standalone. Mirrors React Spectrum's `ActionBarContext`.
208
+ */
209
+ const ActionBarContext = React$1.createContext(null);
210
+ const ActionBarButton = React$1.forwardRef(({ size = "default", ...props }, ref) => /* @__PURE__ */ jsx(Button, {
211
+ ref,
212
+ variant: "ghost",
213
+ size,
214
+ ...props
215
+ }));
216
+ ActionBarButton.displayName = "ActionBarButton";
217
+ const ActionBar = React$1.forwardRef(({ className, count, onClear, children, label, clearLabel = "Clear selection", style, ...props }, ref) => {
218
+ const ctx = React$1.useContext(ActionBarContext);
219
+ const resolvedCount = count ?? ctx?.count ?? 0;
220
+ const resolvedOnClear = onClear ?? ctx?.onClear;
221
+ return /* @__PURE__ */ jsxs("div", {
222
+ ref,
223
+ role: "toolbar",
224
+ "aria-label": "Selection actions",
225
+ "data-slot": "action-bar",
226
+ className: cn("relative flex w-full items-center gap-2 rounded-md border border-border bg-popover px-2 text-popover-foreground shadow-lg", className),
227
+ style: {
228
+ ...style,
229
+ height: ActionBarHeight
230
+ },
231
+ ...props,
232
+ children: [
233
+ resolvedOnClear ? /* @__PURE__ */ jsx(ActionBarButton, {
234
+ size: "icon",
235
+ onClick: resolvedOnClear,
236
+ "aria-label": clearLabel,
237
+ children: /* @__PURE__ */ jsx(XIcon, {})
238
+ }) : null,
239
+ /* @__PURE__ */ jsx("span", {
240
+ className: "flex-1 truncate text-sm",
241
+ children: label ? label(resolvedCount) : `${resolvedCount} items selected.`
242
+ }),
243
+ children ? /* @__PURE__ */ jsx("div", {
244
+ className: "flex shrink-0 items-center gap-2",
245
+ children
246
+ }) : null
247
+ ]
248
+ });
249
+ });
250
+ ActionBar.displayName = "ActionBar";
251
+
196
252
  //#endregion
197
253
  //#region src/components/alert-dialog.tsx
198
254
  function AlertDialog({ ...props }) {
@@ -346,7 +402,7 @@ function AvatarImage({ className, ...props }) {
346
402
  function AvatarFallback({ className, ...props }) {
347
403
  return /* @__PURE__ */ jsx(Avatar$1.Fallback, {
348
404
  "data-slot": "avatar-fallback",
349
- className: cn("flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=xs]/avatar:text-xs group-data-[size=sm]/avatar:text-xs group-data-[size=xl]/avatar:text-base", className),
405
+ className: cn("flex size-full items-center justify-center rounded-full bg-background text-sm text-foreground group-data-[size=xs]/avatar:text-xs group-data-[size=sm]/avatar:text-xs group-data-[size=xl]/avatar:text-base", className),
350
406
  ...props
351
407
  });
352
408
  }
@@ -400,56 +456,305 @@ CounterBadge.displayName = "CounterBadge";
400
456
 
401
457
  //#endregion
402
458
  //#region src/components/badge.tsx
403
- const badgeVariants = cva("group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none", {
459
+ const COLOR_OVERLAY = "bg-[linear-gradient(0deg,var(--badge-color-overlay)_0%,var(--badge-color-overlay)_100%)]";
460
+ const badgeVariants = cva("group/badge inline-flex w-fit max-w-36 shrink-0 items-center justify-center gap-1 overflow-hidden rounded-md border border-transparent font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:shrink-0 [&>svg]:pointer-events-none", {
404
461
  variants: {
405
- variant: {
406
- default: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
407
- secondary: "bg-charcoal-500/30 text-foreground [a]:hover:bg-charcoal-500/20",
408
- outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground"
409
- },
410
462
  size: {
411
- sm: "h-5 px-2 py-0.5 text-xs [&>svg]:size-3!",
412
- lg: "h-7 px-3 py-1 text-sm [&>svg]:size-4!"
463
+ default: "h-5 px-2 py-0.5 text-xs [&>svg]:size-3!",
464
+ lg: "h-6 px-2 text-xs [&>svg]:size-4!"
465
+ },
466
+ color: {
467
+ charcoal: COLOR_OVERLAY,
468
+ red: COLOR_OVERLAY,
469
+ green: COLOR_OVERLAY,
470
+ blue: COLOR_OVERLAY,
471
+ purple: COLOR_OVERLAY,
472
+ yellow: COLOR_OVERLAY,
473
+ pink: COLOR_OVERLAY
474
+ },
475
+ tone: {
476
+ dark: "",
477
+ "medium-dark": "",
478
+ "medium-light": "",
479
+ light: ""
413
480
  }
414
481
  },
482
+ compoundVariants: [
483
+ {
484
+ color: "charcoal",
485
+ tone: "dark",
486
+ class: "bg-charcoal-900 text-charcoal-50"
487
+ },
488
+ {
489
+ color: "charcoal",
490
+ tone: "medium-dark",
491
+ class: "bg-charcoal-600 text-white"
492
+ },
493
+ {
494
+ color: "charcoal",
495
+ tone: "medium-light",
496
+ class: "bg-charcoal-300 text-charcoal-950"
497
+ },
498
+ {
499
+ color: "charcoal",
500
+ tone: "light",
501
+ class: "bg-charcoal-200 text-charcoal-950"
502
+ },
503
+ {
504
+ color: "red",
505
+ tone: "dark",
506
+ class: "bg-red-700 text-red-50"
507
+ },
508
+ {
509
+ color: "red",
510
+ tone: "medium-dark",
511
+ class: "bg-red-400 text-red-950"
512
+ },
513
+ {
514
+ color: "red",
515
+ tone: "medium-light",
516
+ class: "bg-red-300 text-red-950"
517
+ },
518
+ {
519
+ color: "red",
520
+ tone: "light",
521
+ class: "bg-red-200 text-red-800"
522
+ },
523
+ {
524
+ color: "green",
525
+ tone: "dark",
526
+ class: "bg-green-800 text-green-50"
527
+ },
528
+ {
529
+ color: "green",
530
+ tone: "medium-dark",
531
+ class: "bg-green-600 text-white"
532
+ },
533
+ {
534
+ color: "green",
535
+ tone: "medium-light",
536
+ class: "bg-green-300 text-green-950"
537
+ },
538
+ {
539
+ color: "green",
540
+ tone: "light",
541
+ class: "bg-green-200 text-green-950"
542
+ },
543
+ {
544
+ color: "blue",
545
+ tone: "dark",
546
+ class: "bg-blue-800 text-blue-50"
547
+ },
548
+ {
549
+ color: "blue",
550
+ tone: "medium-dark",
551
+ class: "bg-blue-600 text-white"
552
+ },
553
+ {
554
+ color: "blue",
555
+ tone: "medium-light",
556
+ class: "bg-blue-300 text-blue-950"
557
+ },
558
+ {
559
+ color: "blue",
560
+ tone: "light",
561
+ class: "bg-blue-200 text-blue-900"
562
+ },
563
+ {
564
+ color: "purple",
565
+ tone: "dark",
566
+ class: "bg-purple-800 text-purple-50"
567
+ },
568
+ {
569
+ color: "purple",
570
+ tone: "medium-dark",
571
+ class: "bg-purple-600 text-white"
572
+ },
573
+ {
574
+ color: "purple",
575
+ tone: "medium-light",
576
+ class: "bg-purple-300 text-purple-950"
577
+ },
578
+ {
579
+ color: "purple",
580
+ tone: "light",
581
+ class: "bg-purple-200 text-purple-900"
582
+ },
583
+ {
584
+ color: "yellow",
585
+ tone: "dark",
586
+ class: "bg-yellow-500 text-yellow-950"
587
+ },
588
+ {
589
+ color: "yellow",
590
+ tone: "medium-dark",
591
+ class: "bg-yellow-400 text-yellow-950"
592
+ },
593
+ {
594
+ color: "yellow",
595
+ tone: "medium-light",
596
+ class: "bg-yellow-300 text-black"
597
+ },
598
+ {
599
+ color: "yellow",
600
+ tone: "light",
601
+ class: "bg-yellow-200 text-yellow-900"
602
+ },
603
+ {
604
+ color: "pink",
605
+ tone: "dark",
606
+ class: "bg-pink-700 text-pink-50"
607
+ },
608
+ {
609
+ color: "pink",
610
+ tone: "medium-dark",
611
+ class: "bg-pink-500 text-pink-950"
612
+ },
613
+ {
614
+ color: "pink",
615
+ tone: "medium-light",
616
+ class: "bg-pink-300 text-pink-950"
617
+ },
618
+ {
619
+ color: "pink",
620
+ tone: "light",
621
+ class: "bg-pink-200 text-pink-900"
622
+ }
623
+ ],
415
624
  defaultVariants: {
416
- variant: "default",
417
- size: "sm"
625
+ size: "default",
626
+ color: "charcoal",
627
+ tone: "light"
418
628
  }
419
629
  });
420
- const Badge = React$1.forwardRef(({ className, variant = "default", size = "sm", render, ...props }, ref) => {
630
+ /**
631
+ * A standalone label badge. Read-only by default; render it as an anchor or
632
+ * button via `render` to make it hoverable / navigable. Never removable — for a
633
+ * removable token inside an input/combobox, use `Chip`.
634
+ *
635
+ * Pick a constrained DS `color` + `tone` (no freeform hex) — defaults to
636
+ * `charcoal` / `light` — or pass a stored hex via `legacyColor`. Icons are
637
+ * composed as children (auto-sized per `size`).
638
+ */
639
+ const Badge = React$1.forwardRef((componentProps, forwardRef) => {
640
+ const { className, size = "default", color, tone, legacyColor, render, children, ...props } = componentProps;
641
+ const resolved = legacyColor ? resolveTagColor(legacyColor) : {
642
+ color,
643
+ tone
644
+ };
645
+ const content = React$1.Children.map(children, (child) => typeof child === "string" || typeof child === "number" ? /* @__PURE__ */ jsx("span", {
646
+ className: "min-w-0 truncate",
647
+ children: child
648
+ }) : child);
421
649
  return useRender({
422
650
  defaultTagName: "span",
423
651
  props: mergeProps({
424
652
  className: cn(badgeVariants({
425
- variant,
426
- size
653
+ size,
654
+ color: resolved.color,
655
+ tone: resolved.tone
427
656
  }), className),
428
- ref
657
+ ref: forwardRef,
658
+ children: content
429
659
  }, props),
430
660
  render,
431
661
  state: {
432
662
  slot: "badge",
433
- variant,
434
663
  size
435
664
  }
436
665
  });
437
666
  });
438
667
  Badge.displayName = "Badge";
439
- function BadgeGroup({ className, ...props }) {
668
+ const BadgeGroup = React$1.forwardRef((componentProps, forwardRef) => {
669
+ const { className, ...props } = componentProps;
440
670
  return /* @__PURE__ */ jsx("div", {
671
+ ref: forwardRef,
441
672
  "data-slot": "badge-group",
442
673
  className: cn("flex flex-wrap items-center gap-2", className),
443
674
  ...props
444
675
  });
445
- }
446
- function BadgeGroupCount({ className, ...props }) {
676
+ });
677
+ BadgeGroup.displayName = "BadgeGroup";
678
+ const badgeGroupCountVariants = cva("rounded-md", {
679
+ variants: { size: {
680
+ default: "h-5 w-5",
681
+ lg: "h-6 w-6"
682
+ } },
683
+ defaultVariants: { size: "default" }
684
+ });
685
+ const BadgeGroupCount = React$1.forwardRef((componentProps, forwardRef) => {
686
+ const { className, size = "default", ...props } = componentProps;
447
687
  return /* @__PURE__ */ jsx(CounterBadge, {
688
+ ref: forwardRef,
448
689
  "data-slot": "badge-group-count",
449
690
  variant: "ghost",
450
- className,
691
+ className: cn(badgeGroupCountVariants({ size }), className),
451
692
  ...props
452
693
  });
694
+ });
695
+ BadgeGroupCount.displayName = "BadgeGroupCount";
696
+ const LEGACY_TAG_COLOR_MAP = {
697
+ "#003d4b": {
698
+ color: "charcoal",
699
+ tone: "dark"
700
+ },
701
+ "#bb2001": {
702
+ color: "red",
703
+ tone: "dark"
704
+ },
705
+ "#f92c00": {
706
+ color: "red",
707
+ tone: "medium-dark"
708
+ },
709
+ "#0761b5": {
710
+ color: "blue",
711
+ tone: "dark"
712
+ },
713
+ "#008f6c": {
714
+ color: "green",
715
+ tone: "medium-dark"
716
+ },
717
+ "#b285d1": {
718
+ color: "purple",
719
+ tone: "medium-light"
720
+ },
721
+ "#fcbb26": {
722
+ color: "yellow",
723
+ tone: "medium-light"
724
+ },
725
+ "#fe9d88": {
726
+ color: "red",
727
+ tone: "light"
728
+ },
729
+ "#ff844c": {
730
+ color: "red",
731
+ tone: "medium-light"
732
+ },
733
+ "#abccf2": {
734
+ color: "blue",
735
+ tone: "light"
736
+ },
737
+ "#49a0ff": {
738
+ color: "blue",
739
+ tone: "medium-light"
740
+ },
741
+ "#66d188": {
742
+ color: "green",
743
+ tone: "medium-light"
744
+ },
745
+ "#2ac8ff": {
746
+ color: "blue",
747
+ tone: "medium-dark"
748
+ }
749
+ };
750
+ /** Charcoal/dark — used when a stored hex is outside the known legacy palette. */
751
+ const FALLBACK_TAG_COLOR = {
752
+ color: "charcoal",
753
+ tone: "dark"
754
+ };
755
+ /** Resolve a legacy hard-coded tag hex to `color` + `tone`; unknown → charcoal/dark. */
756
+ function resolveTagColor(hex) {
757
+ return LEGACY_TAG_COLOR_MAP[hex.trim().toLowerCase()] ?? FALLBACK_TAG_COLOR;
453
758
  }
454
759
 
455
760
  //#endregion
@@ -789,31 +1094,32 @@ function CalendarDayButton({ className: dayClassName, day, modifiers, locale, ..
789
1094
 
790
1095
  //#endregion
791
1096
  //#region src/components/card.tsx
792
- function Card({ className, ...props }) {
1097
+ function Card({ className, size = "default", ...props }) {
793
1098
  return /* @__PURE__ */ jsx("div", {
794
1099
  "data-slot": "card",
795
- className: cn("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", className),
1100
+ "data-size": size,
1101
+ className: cn("group/card flex flex-col gap-4 overflow-hidden rounded-xl border bg-card py-4 text-sm text-card-foreground has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", className),
796
1102
  ...props
797
1103
  });
798
1104
  }
799
1105
  function CardHeader({ className, ...props }) {
800
1106
  return /* @__PURE__ */ jsx("div", {
801
1107
  "data-slot": "card-header",
802
- className: cn("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", className),
1108
+ className: cn("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3", className),
803
1109
  ...props
804
1110
  });
805
1111
  }
806
1112
  function CardTitle({ className, ...props }) {
807
1113
  return /* @__PURE__ */ jsx("div", {
808
1114
  "data-slot": "card-title",
809
- className: cn("leading-none font-semibold", className),
1115
+ className: cn("text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className),
810
1116
  ...props
811
1117
  });
812
1118
  }
813
1119
  function CardDescription({ className, ...props }) {
814
1120
  return /* @__PURE__ */ jsx("div", {
815
1121
  "data-slot": "card-description",
816
- className: cn("text-muted-foreground text-sm", className),
1122
+ className: cn("text-sm text-muted-foreground", className),
817
1123
  ...props
818
1124
  });
819
1125
  }
@@ -827,14 +1133,14 @@ function CardAction({ className, ...props }) {
827
1133
  function CardContent({ className, ...props }) {
828
1134
  return /* @__PURE__ */ jsx("div", {
829
1135
  "data-slot": "card-content",
830
- className: cn("px-6", className),
1136
+ className: cn("px-4 group-data-[size=sm]/card:px-3", className),
831
1137
  ...props
832
1138
  });
833
1139
  }
834
1140
  function CardFooter({ className, ...props }) {
835
1141
  return /* @__PURE__ */ jsx("div", {
836
1142
  "data-slot": "card-footer",
837
- className: cn("flex items-center px-6 [.border-t]:pt-6", className),
1143
+ className: cn("flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3", className),
838
1144
  ...props
839
1145
  });
840
1146
  }
@@ -937,7 +1243,7 @@ function CarouselPrevious({ className, variant = "outline", size = "sm", ...prop
937
1243
  "data-slot": "carousel-previous",
938
1244
  variant,
939
1245
  size,
940
- className: cn("absolute size-8 rounded-full", orientation === "horizontal" ? "top-1/2 -left-12 -translate-y-1/2" : "-top-12 left-1/2 -translate-x-1/2 rotate-90", className),
1246
+ className: cn("absolute size-8 touch-manipulation rounded-full", orientation === "horizontal" ? "top-1/2 -left-12 -translate-y-1/2" : "-top-12 left-1/2 -translate-x-1/2 rotate-90", className),
941
1247
  disabled: !canScrollPrev,
942
1248
  onClick: scrollPrev,
943
1249
  ...props,
@@ -953,7 +1259,7 @@ function CarouselNext({ className, variant = "outline", size = "sm", ...props })
953
1259
  "data-slot": "carousel-next",
954
1260
  variant,
955
1261
  size,
956
- className: cn("absolute size-8 rounded-full", orientation === "horizontal" ? "top-1/2 -right-12 -translate-y-1/2" : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", className),
1262
+ className: cn("absolute size-8 touch-manipulation rounded-full", orientation === "horizontal" ? "top-1/2 -right-12 -translate-y-1/2" : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", className),
957
1263
  disabled: !canScrollNext,
958
1264
  onClick: scrollNext,
959
1265
  ...props,
@@ -982,6 +1288,211 @@ const Checkbox = React$1.forwardRef(({ className, ...props }, ref) => {
982
1288
  });
983
1289
  Checkbox.displayName = "Checkbox";
984
1290
 
1291
+ //#endregion
1292
+ //#region src/components/chip.tsx
1293
+ const chipGlowVariants = cva("", {
1294
+ variants: {
1295
+ color: {
1296
+ charcoal: "",
1297
+ red: "",
1298
+ green: "",
1299
+ blue: "",
1300
+ purple: "",
1301
+ yellow: "",
1302
+ pink: ""
1303
+ },
1304
+ glow: {
1305
+ true: "drop-shadow-[0px_0px_3px_var(--badge-glow)] animate-badge-glow motion-reduce:animate-none",
1306
+ false: ""
1307
+ }
1308
+ },
1309
+ compoundVariants: [
1310
+ {
1311
+ color: "charcoal",
1312
+ glow: true,
1313
+ class: "border-charcoal-600 [--badge-glow:var(--color-charcoal-600)]"
1314
+ },
1315
+ {
1316
+ color: "red",
1317
+ glow: true,
1318
+ class: "border-red-500 [--badge-glow:var(--color-red-500)]"
1319
+ },
1320
+ {
1321
+ color: "green",
1322
+ glow: true,
1323
+ class: "border-green-600 [--badge-glow:var(--color-green-600)]"
1324
+ },
1325
+ {
1326
+ color: "blue",
1327
+ glow: true,
1328
+ class: "border-blue-600 [--badge-glow:var(--color-blue-600)]"
1329
+ },
1330
+ {
1331
+ color: "purple",
1332
+ glow: true,
1333
+ class: "border-purple-600 [--badge-glow:var(--color-purple-600)]"
1334
+ },
1335
+ {
1336
+ color: "yellow",
1337
+ glow: true,
1338
+ class: "border-yellow-400 [--badge-glow:var(--color-yellow-400)]"
1339
+ },
1340
+ {
1341
+ color: "pink",
1342
+ glow: true,
1343
+ class: "border-pink-600 [--badge-glow:var(--color-pink-600)]"
1344
+ }
1345
+ ],
1346
+ defaultVariants: {
1347
+ color: "charcoal",
1348
+ glow: false
1349
+ }
1350
+ });
1351
+ /**
1352
+ * The trailing remove affordance for a `Chip` — a ghost icon button that
1353
+ * inherits the chip's text color and brightens on hover. Exported so the
1354
+ * combobox can reuse the exact treatment for its Base-UI-bound chips.
1355
+ */
1356
+ const ChipRemove = React$1.forwardRef((componentProps, forwardRef) => {
1357
+ const { className, children, ...props } = componentProps;
1358
+ return /* @__PURE__ */ jsx(Button, {
1359
+ ref: forwardRef,
1360
+ type: "button",
1361
+ variant: "ghost",
1362
+ size: "icon-sm",
1363
+ "data-slot": "chip-remove",
1364
+ className: cn("-ml-1 bg-transparent hover:bg-transparent dark:hover:bg-transparent text-inherit hover:text-inherit opacity-50 hover:opacity-100", className),
1365
+ ...props,
1366
+ children: children ?? /* @__PURE__ */ jsx(XIcon, { className: "pointer-events-none size-3.5" })
1367
+ });
1368
+ });
1369
+ ChipRemove.displayName = "ChipRemove";
1370
+ /**
1371
+ * A selected-value token for inputs / comboboxes. Composes `Badge` at the
1372
+ * fixed 24px size with a forced trailing remove (X) button. The chip body is
1373
+ * non-interactive — only the X reacts to hover. For combobox-bound chips that
1374
+ * must drive Base UI's selection state, see `ComboboxChip`.
1375
+ */
1376
+ const Chip = React$1.forwardRef((componentProps, forwardRef) => {
1377
+ const { className, children, color, tone, legacyColor, glow, ...props } = componentProps;
1378
+ const glowColor = legacyColor ? resolveTagColor(legacyColor).color : color;
1379
+ return /* @__PURE__ */ jsx(Badge, {
1380
+ ref: forwardRef,
1381
+ "data-slot": "chip",
1382
+ size: "lg",
1383
+ color,
1384
+ tone,
1385
+ legacyColor,
1386
+ className: cn("has-data-[slot=chip-remove]:pr-0 has-disabled:pointer-events-none has-disabled:opacity-50", chipGlowVariants({
1387
+ color: glowColor,
1388
+ glow
1389
+ }), className),
1390
+ ...props,
1391
+ children
1392
+ });
1393
+ });
1394
+ Chip.displayName = "Chip";
1395
+
1396
+ //#endregion
1397
+ //#region src/components/scroll-area.tsx
1398
+ function ScrollArea({ className, children, scrollFade = false, scrollbarGutter = false, ...props }) {
1399
+ return /* @__PURE__ */ jsxs(ScrollAreaPrimitive.Root, {
1400
+ className: cn("size-full min-h-0", className),
1401
+ ...props,
1402
+ children: [
1403
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, {
1404
+ className: cn("h-full rounded-[inherit] outline-none transition-shadows focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-y:overscroll-y-contain data-has-overflow-x:overscroll-x-contain", scrollFade && "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] [--fade-size:1.5rem]", scrollbarGutter && "data-has-overflow-y:pe-2.5 data-has-overflow-x:pb-2.5"),
1405
+ "data-slot": "scroll-area-viewport",
1406
+ children
1407
+ }),
1408
+ /* @__PURE__ */ jsx(ScrollBar, { orientation: "vertical" }),
1409
+ /* @__PURE__ */ jsx(ScrollBar, { orientation: "horizontal" }),
1410
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, { "data-slot": "scroll-area-corner" })
1411
+ ]
1412
+ });
1413
+ }
1414
+ function ScrollBar({ className, orientation = "vertical", ...props }) {
1415
+ return /* @__PURE__ */ jsx(ScrollAreaPrimitive.Scrollbar, {
1416
+ className: cn("m-1 flex opacity-0 transition-opacity delay-300 data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:w-1.5 data-[orientation=horizontal]:flex-col data-hovering:opacity-100 data-scrolling:opacity-100 data-hovering:delay-0 data-scrolling:delay-0 data-hovering:duration-100 data-scrolling:duration-100", className),
1417
+ "data-slot": "scroll-area-scrollbar",
1418
+ orientation,
1419
+ ...props,
1420
+ children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.Thumb, {
1421
+ className: "relative flex-1 rounded-full bg-foreground/20",
1422
+ "data-slot": "scroll-area-thumb"
1423
+ })
1424
+ });
1425
+ }
1426
+
1427
+ //#endregion
1428
+ //#region src/components/filter.tsx
1429
+ /**
1430
+ * A removable active-filter token for a filter bar / toolbar — a dark charcoal
1431
+ * `Chip` with a trailing remove (X). Shows each applied filter above a
1432
+ * searchable data view (table / list); clicking the X clears that filter
1433
+ * (`onRemove`). Long values truncate with an ellipsis, capped at the Badge
1434
+ * `max-w-36` (144px). Compose inside a `FilterGroup` with a trailing
1435
+ * `FilterClearAllButton` (see the FilterBar story).
1436
+ */
1437
+ const Filter = React$1.forwardRef((componentProps, forwardRef) => {
1438
+ const { className, children, onRemove, removeLabel = "Remove filter" } = componentProps;
1439
+ return /* @__PURE__ */ jsxs(Chip, {
1440
+ ref: forwardRef,
1441
+ color: "charcoal",
1442
+ tone: "medium-dark",
1443
+ className,
1444
+ children: [children, /* @__PURE__ */ jsx(ChipRemove, {
1445
+ "aria-label": removeLabel,
1446
+ onClick: onRemove
1447
+ })]
1448
+ });
1449
+ });
1450
+ Filter.displayName = "Filter";
1451
+ /**
1452
+ * Layout container for a row of `Filter` chips (and a trailing
1453
+ * `FilterClearAllButton`). Always a single, non-wrapping row inside a
1454
+ * horizontal `ScrollArea` (inset scroll + edge fade) — it does not wrap onto
1455
+ * multiple lines.
1456
+ */
1457
+ const FilterGroup = React$1.forwardRef((componentProps, forwardRef) => {
1458
+ const { className, children, ...props } = componentProps;
1459
+ return /* @__PURE__ */ jsx("div", {
1460
+ "data-slot": "filter-group",
1461
+ className,
1462
+ children: /* @__PURE__ */ jsx(ScrollArea, {
1463
+ "data-slot": "filter-group-scroll-area",
1464
+ scrollFade: true,
1465
+ children: /* @__PURE__ */ jsx("div", {
1466
+ ref: forwardRef,
1467
+ "data-slot": "filter-group-container",
1468
+ className: "flex w-max items-center gap-2",
1469
+ ...props,
1470
+ children
1471
+ })
1472
+ })
1473
+ });
1474
+ });
1475
+ FilterGroup.displayName = "FilterGroup";
1476
+ /**
1477
+ * The trailing "Clear all filters" action for a `FilterGroup` — a ghost,
1478
+ * small `Button` with the styling pinned. Provide `onClick`; the label
1479
+ * defaults to "Clear all filters" (pass translated `children` to override).
1480
+ */
1481
+ const FilterClearAllButton = React$1.forwardRef((componentProps, forwardRef) => {
1482
+ const { className, children = "Clear all filters", ...props } = componentProps;
1483
+ return /* @__PURE__ */ jsx(Button, {
1484
+ ref: forwardRef,
1485
+ type: "button",
1486
+ variant: "ghost",
1487
+ size: "sm",
1488
+ "data-slot": "filter-clear-all",
1489
+ className,
1490
+ ...props,
1491
+ children
1492
+ });
1493
+ });
1494
+ FilterClearAllButton.displayName = "FilterClearAllButton";
1495
+
985
1496
  //#endregion
986
1497
  //#region src/components/collapsible.tsx
987
1498
  function Collapsible({ ...props }) {
@@ -1097,15 +1608,16 @@ function InputGroupTextarea({ className, ...props }) {
1097
1608
  //#endregion
1098
1609
  //#region src/components/combobox.tsx
1099
1610
  const Combobox = Combobox$1.Root;
1100
- function ComboboxValue({ ...props }) {
1611
+ function ComboboxValue(componentProps) {
1101
1612
  return /* @__PURE__ */ jsx(Combobox$1.Value, {
1102
1613
  "data-slot": "combobox-value",
1103
- ...props
1614
+ ...componentProps
1104
1615
  });
1105
1616
  }
1106
- const ComboboxTrigger = React$1.forwardRef(({ className, children, ...props }, ref) => {
1617
+ const ComboboxTrigger = React$1.forwardRef((componentProps, forwardRef) => {
1618
+ const { className, children, ...props } = componentProps;
1107
1619
  return /* @__PURE__ */ jsxs(Combobox$1.Trigger, {
1108
- ref,
1620
+ ref: forwardRef,
1109
1621
  "data-slot": "combobox-trigger",
1110
1622
  className: cn("[&_svg:not([class*='size-'])]:size-4", className),
1111
1623
  ...props,
@@ -1113,8 +1625,10 @@ const ComboboxTrigger = React$1.forwardRef(({ className, children, ...props }, r
1113
1625
  });
1114
1626
  });
1115
1627
  ComboboxTrigger.displayName = "ComboboxTrigger";
1116
- function ComboboxClear({ className, ...props }) {
1628
+ const ComboboxClear = React$1.forwardRef((componentProps, forwardRef) => {
1629
+ const { className, ...props } = componentProps;
1117
1630
  return /* @__PURE__ */ jsx(Combobox$1.Clear, {
1631
+ ref: forwardRef,
1118
1632
  "data-slot": "combobox-clear",
1119
1633
  render: /* @__PURE__ */ jsx(InputGroupButton, {
1120
1634
  variant: "ghost",
@@ -1124,10 +1638,12 @@ function ComboboxClear({ className, ...props }) {
1124
1638
  ...props,
1125
1639
  children: /* @__PURE__ */ jsx(XIcon, { className: "pointer-events-none" })
1126
1640
  });
1127
- }
1128
- const ComboboxInput = React$1.forwardRef(({ className, children, disabled = false, showTrigger = true, showClear = false, ...props }, ref) => {
1641
+ });
1642
+ ComboboxClear.displayName = "ComboboxClear";
1643
+ const ComboboxInput = React$1.forwardRef((componentProps, forwardRef) => {
1644
+ const { className, children, disabled = false, showTrigger = true, showClear = false, ...props } = componentProps;
1129
1645
  return /* @__PURE__ */ jsxs(InputGroup, {
1130
- ref,
1646
+ ref: forwardRef,
1131
1647
  className: cn("w-auto", className),
1132
1648
  children: [
1133
1649
  /* @__PURE__ */ jsx(Combobox$1.Input, {
@@ -1150,7 +1666,8 @@ const ComboboxInput = React$1.forwardRef(({ className, children, disabled = fals
1150
1666
  });
1151
1667
  });
1152
1668
  ComboboxInput.displayName = "ComboboxInput";
1153
- function ComboboxContent({ className, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, anchor, ...props }) {
1669
+ const ComboboxContent = React$1.forwardRef((componentProps, forwardRef) => {
1670
+ const { className, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, anchor, ...props } = componentProps;
1154
1671
  return /* @__PURE__ */ jsx(Combobox$1.Portal, { children: /* @__PURE__ */ jsx(Combobox$1.Positioner, {
1155
1672
  side,
1156
1673
  sideOffset,
@@ -1159,22 +1676,29 @@ function ComboboxContent({ className, side = "bottom", sideOffset = 6, align = "
1159
1676
  anchor,
1160
1677
  className: "isolate z-50",
1161
1678
  children: /* @__PURE__ */ jsx(Combobox$1.Popup, {
1679
+ ref: forwardRef,
1162
1680
  "data-slot": "combobox-content",
1163
1681
  "data-chips": !!anchor,
1164
1682
  className: cn("group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
1165
1683
  ...props
1166
1684
  })
1167
1685
  }) });
1168
- }
1169
- function ComboboxList({ className, ...props }) {
1686
+ });
1687
+ ComboboxContent.displayName = "ComboboxContent";
1688
+ const ComboboxList = React$1.forwardRef((componentProps, forwardRef) => {
1689
+ const { className, ...props } = componentProps;
1170
1690
  return /* @__PURE__ */ jsx(Combobox$1.List, {
1691
+ ref: forwardRef,
1171
1692
  "data-slot": "combobox-list",
1172
1693
  className: cn("no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto overscroll-contain p-1 outline-hidden data-empty:p-0", className),
1173
1694
  ...props
1174
1695
  });
1175
- }
1176
- function ComboboxItem({ className, children, ...props }) {
1696
+ });
1697
+ ComboboxList.displayName = "ComboboxList";
1698
+ const ComboboxItem = React$1.forwardRef((componentProps, forwardRef) => {
1699
+ const { className, children, ...props } = componentProps;
1177
1700
  return /* @__PURE__ */ jsxs(Combobox$1.Item, {
1701
+ ref: forwardRef,
1178
1702
  "data-slot": "combobox-item",
1179
1703
  className: cn("relative flex w-full cursor-default items-center gap-2 rounded-md p-2 pr-8 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className),
1180
1704
  ...props,
@@ -1184,73 +1708,88 @@ function ComboboxItem({ className, children, ...props }) {
1184
1708
  children: /* @__PURE__ */ jsx(CheckIcon, { className: "pointer-events-none" })
1185
1709
  })]
1186
1710
  });
1187
- }
1188
- function ComboboxGroup({ className, ...props }) {
1711
+ });
1712
+ ComboboxItem.displayName = "ComboboxItem";
1713
+ const ComboboxGroup = React$1.forwardRef((componentProps, forwardRef) => {
1714
+ const { className, ...props } = componentProps;
1189
1715
  return /* @__PURE__ */ jsx(Combobox$1.Group, {
1716
+ ref: forwardRef,
1190
1717
  "data-slot": "combobox-group",
1191
1718
  className: cn(className),
1192
1719
  ...props
1193
1720
  });
1194
- }
1195
- function ComboboxLabel({ className, ...props }) {
1721
+ });
1722
+ ComboboxGroup.displayName = "ComboboxGroup";
1723
+ const ComboboxLabel = React$1.forwardRef((componentProps, forwardRef) => {
1724
+ const { className, ...props } = componentProps;
1196
1725
  return /* @__PURE__ */ jsx(Combobox$1.GroupLabel, {
1726
+ ref: forwardRef,
1197
1727
  "data-slot": "combobox-label",
1198
1728
  className: cn("p-2 text-xs text-muted-foreground", className),
1199
1729
  ...props
1200
1730
  });
1201
- }
1202
- function ComboboxCollection({ ...props }) {
1731
+ });
1732
+ ComboboxLabel.displayName = "ComboboxLabel";
1733
+ function ComboboxCollection(componentProps) {
1203
1734
  return /* @__PURE__ */ jsx(Combobox$1.Collection, {
1204
1735
  "data-slot": "combobox-collection",
1205
- ...props
1736
+ ...componentProps
1206
1737
  });
1207
1738
  }
1208
- function ComboboxEmpty({ className, ...props }) {
1739
+ const ComboboxEmpty = React$1.forwardRef((componentProps, forwardRef) => {
1740
+ const { className, ...props } = componentProps;
1209
1741
  return /* @__PURE__ */ jsx(Combobox$1.Empty, {
1742
+ ref: forwardRef,
1210
1743
  "data-slot": "combobox-empty",
1211
1744
  className: cn("hidden w-full justify-center py-2 text-center text-sm text-muted-foreground group-data-empty/combobox-content:flex", className),
1212
1745
  ...props
1213
1746
  });
1214
- }
1215
- function ComboboxSeparator({ className, ...props }) {
1747
+ });
1748
+ ComboboxEmpty.displayName = "ComboboxEmpty";
1749
+ const ComboboxSeparator = React$1.forwardRef((componentProps, forwardRef) => {
1750
+ const { className, ...props } = componentProps;
1216
1751
  return /* @__PURE__ */ jsx(Combobox$1.Separator, {
1752
+ ref: forwardRef,
1217
1753
  "data-slot": "combobox-separator",
1218
1754
  className: cn("-mx-1 my-1 h-px bg-border", className),
1219
1755
  ...props
1220
1756
  });
1221
- }
1222
- const ComboboxChips = React$1.forwardRef(({ className, ...props }, ref) => {
1757
+ });
1758
+ ComboboxSeparator.displayName = "ComboboxSeparator";
1759
+ const ComboboxChips = React$1.forwardRef((componentProps, forwardRef) => {
1760
+ const { className, ...props } = componentProps;
1223
1761
  return /* @__PURE__ */ jsx(Combobox$1.Chips, {
1224
- ref,
1762
+ ref: forwardRef,
1225
1763
  "data-slot": "combobox-chips",
1226
1764
  className: cn("flex min-h-10 flex-wrap items-center gap-1 rounded-md border border-input bg-transparent bg-clip-padding py-2 pl-3 pr-2 text-sm transition-colors focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40", className),
1227
1765
  ...props
1228
1766
  });
1229
1767
  });
1230
1768
  ComboboxChips.displayName = "ComboboxChips";
1231
- function ComboboxChip({ className, children, showRemove = true, ...props }) {
1769
+ const ComboboxChip = React$1.forwardRef(function ComboboxChip(componentProps, forwardRef) {
1770
+ const { className, children, showRemove = true, color, tone, legacyColor, ...props } = componentProps;
1232
1771
  return /* @__PURE__ */ jsxs(Combobox$1.Chip, {
1233
- "data-slot": "combobox-chip",
1234
- className: cn("flex h-6 w-fit items-center justify-center gap-1 rounded-md bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0", className),
1772
+ ref: forwardRef,
1773
+ render: /* @__PURE__ */ jsx(Chip, {
1774
+ color,
1775
+ tone,
1776
+ legacyColor
1777
+ }),
1778
+ className,
1235
1779
  ...props,
1236
- children: [children, showRemove && /* @__PURE__ */ jsx(Combobox$1.ChipRemove, {
1237
- render: /* @__PURE__ */ jsx(Button, {
1238
- variant: "ghost",
1239
- size: "icon-sm"
1240
- }),
1241
- className: "-ml-1 opacity-50 hover:opacity-100",
1242
- "data-slot": "combobox-chip-remove",
1243
- children: /* @__PURE__ */ jsx(XIcon, { className: "pointer-events-none" })
1244
- })]
1780
+ children: [children, showRemove && /* @__PURE__ */ jsx(Combobox$1.ChipRemove, { render: /* @__PURE__ */ jsx(ChipRemove, {}) })]
1245
1781
  });
1246
- }
1247
- function ComboboxChipsInput({ className, ...props }) {
1782
+ });
1783
+ const ComboboxChipsInput = React$1.forwardRef(function ComboboxChipsInput(componentProps, forwardRef) {
1784
+ const { className, ...props } = componentProps;
1248
1785
  return /* @__PURE__ */ jsx(Combobox$1.Input, {
1786
+ ref: forwardRef,
1249
1787
  "data-slot": "combobox-chip-input",
1250
1788
  className: cn("min-w-16 flex-1 outline-none", className),
1251
1789
  ...props
1252
1790
  });
1253
- }
1791
+ });
1792
+ ComboboxChipsInput.displayName = "ComboboxChipsInput";
1254
1793
  function useComboboxAnchor() {
1255
1794
  return React$1.useRef(null);
1256
1795
  }
@@ -1433,15 +1972,47 @@ const CountryFlag = ({ countryIsoCode, size = "xl", className, ...otherProps })
1433
1972
  });
1434
1973
  };
1435
1974
 
1975
+ //#endregion
1976
+ //#region src/components/skeleton.tsx
1977
+ function Skeleton({ className, ...props }) {
1978
+ return /* @__PURE__ */ jsx("div", {
1979
+ "data-slot": "skeleton",
1980
+ className: cn("animate-pulse rounded-md bg-muted", className),
1981
+ ...props
1982
+ });
1983
+ }
1984
+
1985
+ //#endregion
1986
+ //#region src/components/spinner.tsx
1987
+ const spinnerVariants = cva("animate-spin text-current", {
1988
+ variants: { size: {
1989
+ default: "size-4",
1990
+ sm: "size-3",
1991
+ lg: "size-5",
1992
+ xl: "size-6"
1993
+ } },
1994
+ defaultVariants: { size: "default" }
1995
+ });
1996
+ function Spinner({ className, size, ...props }) {
1997
+ return /* @__PURE__ */ jsx(Loader2Icon, {
1998
+ "data-slot": "spinner",
1999
+ role: "status",
2000
+ "aria-label": "Loading",
2001
+ className: cn(spinnerVariants({ size }), className),
2002
+ ...props
2003
+ });
2004
+ }
2005
+
1436
2006
  //#endregion
1437
2007
  //#region src/components/table.tsx
1438
- const Table = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", {
2008
+ const Table = React$1.forwardRef(({ className, containerClassName, containerStyle, ...props }, ref) => /* @__PURE__ */ jsx("div", {
1439
2009
  "data-slot": "table-container",
1440
- className: "relative w-full overflow-x-auto",
2010
+ style: containerStyle,
2011
+ className: cn("relative w-full overflow-x-auto rounded-lg border border-border", containerClassName),
1441
2012
  children: /* @__PURE__ */ jsx("table", {
1442
2013
  ref,
1443
2014
  "data-slot": "table",
1444
- className: cn("w-full caption-bottom text-sm", className),
2015
+ className: cn("w-full caption-bottom text-sm [&_tbody_tr:last-child]:border-b-0", className),
1445
2016
  ...props
1446
2017
  })
1447
2018
  }));
@@ -1449,7 +2020,7 @@ Table.displayName = "Table";
1449
2020
  const TableHeader = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", {
1450
2021
  ref,
1451
2022
  "data-slot": "table-header",
1452
- className: cn("[&_tr]:border-b", className),
2023
+ className: cn("[&_tr]:border-b [&_tr]:hover:bg-transparent", className),
1453
2024
  ...props
1454
2025
  }));
1455
2026
  TableHeader.displayName = "TableHeader";
@@ -1463,14 +2034,14 @@ TableBody.displayName = "TableBody";
1463
2034
  const TableFooter = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tfoot", {
1464
2035
  ref,
1465
2036
  "data-slot": "table-footer",
1466
- className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className),
2037
+ className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0 [&>tr]:hover:bg-transparent", className),
1467
2038
  ...props
1468
2039
  }));
1469
2040
  TableFooter.displayName = "TableFooter";
1470
2041
  const TableRow = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tr", {
1471
2042
  ref,
1472
2043
  "data-slot": "table-row",
1473
- className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className),
2044
+ className: cn("border-b transition-colors hover:bg-accent/30 data-[state=selected]:bg-accent/50", className),
1474
2045
  ...props
1475
2046
  }));
1476
2047
  TableRow.displayName = "TableRow";
@@ -1488,63 +2059,249 @@ const TableCell = React$1.forwardRef(({ className, ...props }, ref) => /* @__PUR
1488
2059
  ...props
1489
2060
  }));
1490
2061
  TableCell.displayName = "TableCell";
1491
- const TableCaption = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("caption", {
1492
- ref,
1493
- "data-slot": "table-caption",
1494
- className: cn("mt-4 text-sm text-muted-foreground", className),
1495
- ...props
1496
- }));
1497
- TableCaption.displayName = "TableCaption";
1498
2062
 
1499
2063
  //#endregion
1500
2064
  //#region src/components/data-table.tsx
1501
- function DataTable({ columns, data, tableOptions, noResultsMessage = "No results." }) {
2065
+ const SELECT_COLUMN_ID = "__select";
2066
+ const DEFAULT_SKELETON_COUNT = 5;
2067
+ function DataTable({ columns, data, getRowId, loadingState = "idle", skeletonCount = DEFAULT_SKELETON_COUNT, emptyState, sorting, onSortingChange, enableRowSelection, rowSelection, onRowSelectionChange, actionBar, onFetchMore, hasMore = false, enableColumnResizing = false, fillHeight = false, onRowClick, className }) {
2068
+ const sortingEnabled = !!onSortingChange;
2069
+ const selectionEnabled = !!enableRowSelection;
2070
+ const finalColumns = React$1.useMemo(() => {
2071
+ if (!selectionEnabled) return columns;
2072
+ return [makeSelectColumn(), ...columns];
2073
+ }, [columns, selectionEnabled]);
1502
2074
  const table = useReactTable({
1503
2075
  data,
1504
- columns,
2076
+ columns: finalColumns,
1505
2077
  getCoreRowModel: getCoreRowModel(),
1506
- ...tableOptions
2078
+ getRowId,
2079
+ enableSorting: sortingEnabled,
2080
+ manualSorting: sortingEnabled,
2081
+ enableRowSelection,
2082
+ enableColumnResizing,
2083
+ columnResizeMode: "onChange",
2084
+ onSortingChange,
2085
+ onRowSelectionChange,
2086
+ state: {
2087
+ ...sorting !== void 0 && { sorting },
2088
+ ...rowSelection !== void 0 && { rowSelection }
2089
+ }
1507
2090
  });
1508
- return /* @__PURE__ */ jsxs(Table, { children: [/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()) }, header.id)) }, headerGroup.id)) }), /* @__PURE__ */ jsx(TableBody, { children: table.getRowModel().rows.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(TableRow, {
1509
- "data-state": row.getIsSelected() && "selected",
1510
- children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
1511
- }, row.id)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
1512
- colSpan: columns.length,
1513
- className: "h-24 text-center",
1514
- children: noResultsMessage
1515
- }) }) })] });
1516
- }
1517
-
1518
- //#endregion
1519
- //#region src/components/scroll-area.tsx
1520
- function ScrollArea({ className, children, scrollFade = false, scrollbarGutter = false, ...props }) {
1521
- return /* @__PURE__ */ jsxs(ScrollAreaPrimitive.Root, {
1522
- className: cn("size-full min-h-0", className),
1523
- ...props,
2091
+ const rows = table.getRowModel().rows;
2092
+ const selectedCount = table.getSelectedRowModel().rows.length;
2093
+ const hasSelection = selectedCount > 0;
2094
+ const hasFooter = finalColumns.some((col) => col.footer != null);
2095
+ const isInitialLoading = loadingState === "loading";
2096
+ const isRefreshing = loadingState === "sorting" || loadingState === "filtering";
2097
+ const isLoadingMore = loadingState === "loadingMore";
2098
+ const showEmptyState = !isInitialLoading && rows.length === 0 && !!emptyState;
2099
+ const anySelected = hasSelection;
2100
+ const rowClickable = selectionEnabled || !!onRowClick;
2101
+ const handleRowActivate = (row) => {
2102
+ if (selectionEnabled && (anySelected || !onRowClick)) {
2103
+ if (row.getCanSelect()) row.toggleSelected();
2104
+ } else if (onRowClick) onRowClick(row.original);
2105
+ };
2106
+ const colWidth = (size) => enableColumnResizing ? { width: size } : void 0;
2107
+ const showActionBar = !!actionBar && hasSelection;
2108
+ const reservedBottom = showActionBar ? ActionBarHeight + 16 : void 0;
2109
+ const sentinelRef = useFetchMoreSentinel(onFetchMore, fillHeight, data.length);
2110
+ return /* @__PURE__ */ jsxs("div", {
2111
+ className: cn("relative w-full", fillHeight && "flex min-h-0 flex-1 flex-col", className),
2112
+ "aria-busy": isInitialLoading || isRefreshing || void 0,
1524
2113
  children: [
1525
- /* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, {
1526
- className: cn("h-full rounded-[inherit] outline-none transition-shadows focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-y:overscroll-y-contain data-has-overflow-x:overscroll-x-contain", scrollFade && "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] [--fade-size:1.5rem]", scrollbarGutter && "data-has-overflow-y:pe-2.5 data-has-overflow-x:pb-2.5"),
1527
- "data-slot": "scroll-area-viewport",
1528
- children
2114
+ /* @__PURE__ */ jsxs(Table, {
2115
+ className: cn(enableColumnResizing && "table-fixed", fillHeight && "[&_[data-slot=table-head]]:sticky [&_[data-slot=table-head]]:top-0 [&_[data-slot=table-head]]:z-10 [&_[data-slot=table-head]]:bg-background", fillHeight && showEmptyState && "h-full"),
2116
+ containerClassName: fillHeight ? "min-h-0 flex-1 overflow-y-auto" : void 0,
2117
+ containerStyle: reservedBottom ? {
2118
+ paddingBottom: reservedBottom,
2119
+ scrollPaddingBottom: reservedBottom
2120
+ } : void 0,
2121
+ style: enableColumnResizing ? { width: `max(100%, ${table.getTotalSize()}px)` } : void 0,
2122
+ children: [
2123
+ /* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, {
2124
+ className: enableColumnResizing ? "group/header-row" : void 0,
2125
+ children: headerGroup.headers.map((header) => /* @__PURE__ */ jsxs(TableHead, {
2126
+ style: colWidth(header.getSize()),
2127
+ className: enableColumnResizing ? "relative" : void 0,
2128
+ children: [header.isPlaceholder ? null : header.column.getCanSort() ? /* @__PURE__ */ jsx(SortableHeader, { header }) : flexRender(header.column.columnDef.header, header.getContext()), enableColumnResizing && header.column.getCanResize() ? /* @__PURE__ */ jsx(ResizeHandle, { header }) : null]
2129
+ }, header.id))
2130
+ }, headerGroup.id)) }),
2131
+ /* @__PURE__ */ jsx(TableBody, { children: isInitialLoading ? /* @__PURE__ */ jsx(SkeletonRows, {
2132
+ columns: finalColumns,
2133
+ count: skeletonCount
2134
+ }) : rows.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
2135
+ rows.map((row) => /* @__PURE__ */ jsx(TableRow, {
2136
+ "data-state": row.getIsSelected() ? "selected" : void 0,
2137
+ onClick: rowClickable ? () => handleRowActivate(row) : void 0,
2138
+ className: rowClickable ? "cursor-pointer" : void 0,
2139
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, {
2140
+ style: colWidth(cell.column.getSize()),
2141
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
2142
+ }, cell.id))
2143
+ }, row.id)),
2144
+ isLoadingMore ? /* @__PURE__ */ jsx(TableRow, {
2145
+ className: "hover:bg-transparent",
2146
+ children: /* @__PURE__ */ jsx(TableCell, {
2147
+ colSpan: finalColumns.length,
2148
+ className: "py-3",
2149
+ children: /* @__PURE__ */ jsx("div", {
2150
+ className: "flex items-center justify-center text-muted-foreground",
2151
+ children: /* @__PURE__ */ jsx(Spinner, {})
2152
+ })
2153
+ })
2154
+ }) : null,
2155
+ onFetchMore && hasMore && !isLoadingMore ? /* @__PURE__ */ jsx("tr", {
2156
+ "aria-hidden": true,
2157
+ ref: sentinelRef,
2158
+ children: /* @__PURE__ */ jsx("td", {
2159
+ colSpan: finalColumns.length,
2160
+ className: "h-px"
2161
+ })
2162
+ }) : null
2163
+ ] }) : emptyState ? /* @__PURE__ */ jsx(TableRow, {
2164
+ className: "hover:bg-transparent",
2165
+ children: /* @__PURE__ */ jsx(TableCell, {
2166
+ colSpan: finalColumns.length,
2167
+ className: "h-full p-0",
2168
+ children: /* @__PURE__ */ jsx("div", {
2169
+ className: cn("flex h-full items-center justify-center", !fillHeight && "min-h-60"),
2170
+ children: emptyState
2171
+ })
2172
+ })
2173
+ }) : null }),
2174
+ hasFooter && !isInitialLoading ? /* @__PURE__ */ jsx(TableFooter, { children: table.getFooterGroups().map((footerGroup) => /* @__PURE__ */ jsx(TableRow, { children: footerGroup.headers.map((header) => /* @__PURE__ */ jsx(TableCell, {
2175
+ style: colWidth(header.getSize()),
2176
+ children: header.isPlaceholder ? null : flexRender(header.column.columnDef.footer, header.getContext())
2177
+ }, header.id)) }, footerGroup.id)) }) : null
2178
+ ]
1529
2179
  }),
1530
- /* @__PURE__ */ jsx(ScrollBar, { orientation: "vertical" }),
1531
- /* @__PURE__ */ jsx(ScrollBar, { orientation: "horizontal" }),
1532
- /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, { "data-slot": "scroll-area-corner" })
2180
+ isRefreshing ? /* @__PURE__ */ jsx("div", {
2181
+ "data-slot": "table-loading-overlay",
2182
+ className: "bg-background/60 absolute inset-0 z-10 flex items-center justify-center",
2183
+ children: /* @__PURE__ */ jsx(Spinner, {
2184
+ size: "lg",
2185
+ className: "text-muted-foreground"
2186
+ })
2187
+ }) : null,
2188
+ showActionBar ? /* @__PURE__ */ jsx("div", {
2189
+ className: "pointer-events-none absolute inset-x-0 bottom-0 z-20 flex justify-center p-2",
2190
+ children: /* @__PURE__ */ jsx("div", {
2191
+ className: "animate-in fade-in slide-in-from-bottom-4 pointer-events-auto w-full duration-200 ease-out",
2192
+ children: /* @__PURE__ */ jsx(ActionBarContext.Provider, {
2193
+ value: {
2194
+ count: selectedCount,
2195
+ onClear: () => table.resetRowSelection()
2196
+ },
2197
+ children: actionBar
2198
+ })
2199
+ })
2200
+ }) : null
1533
2201
  ]
1534
2202
  });
1535
2203
  }
1536
- function ScrollBar({ className, orientation = "vertical", ...props }) {
1537
- return /* @__PURE__ */ jsx(ScrollAreaPrimitive.Scrollbar, {
1538
- className: cn("m-1 flex opacity-0 transition-opacity delay-300 data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:w-1.5 data-[orientation=horizontal]:flex-col data-hovering:opacity-100 data-scrolling:opacity-100 data-hovering:delay-0 data-scrolling:delay-0 data-hovering:duration-100 data-scrolling:duration-100", className),
1539
- "data-slot": "scroll-area-scrollbar",
1540
- orientation,
1541
- ...props,
1542
- children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.Thumb, {
1543
- className: "relative flex-1 rounded-full bg-foreground/20",
1544
- "data-slot": "scroll-area-thumb"
1545
- })
2204
+ function makeSelectColumn() {
2205
+ return {
2206
+ id: SELECT_COLUMN_ID,
2207
+ header: ({ table }) => {
2208
+ const allSelected = table.getIsAllRowsSelected();
2209
+ const someSelected = table.getIsSomeRowsSelected();
2210
+ return /* @__PURE__ */ jsx(Checkbox, {
2211
+ checked: allSelected,
2212
+ indeterminate: !allSelected && someSelected,
2213
+ onCheckedChange: (value) => table.toggleAllRowsSelected(value === true),
2214
+ "aria-label": "Select all rows"
2215
+ });
2216
+ },
2217
+ cell: ({ row }) => /* @__PURE__ */ jsx("span", {
2218
+ className: "contents",
2219
+ onClick: (e) => e.stopPropagation(),
2220
+ children: /* @__PURE__ */ jsx(Checkbox, {
2221
+ checked: row.getIsSelected(),
2222
+ disabled: !row.getCanSelect(),
2223
+ onCheckedChange: (value) => row.toggleSelected(value === true),
2224
+ "aria-label": "Select row"
2225
+ })
2226
+ }),
2227
+ enableSorting: false,
2228
+ enableHiding: false,
2229
+ enableResizing: false,
2230
+ size: 40
2231
+ };
2232
+ }
2233
+ function ResizeHandle({ header }) {
2234
+ const isResizing = header.column.getIsResizing();
2235
+ return /* @__PURE__ */ jsx("span", {
2236
+ "aria-hidden": true,
2237
+ onMouseDown: header.getResizeHandler(),
2238
+ onTouchStart: header.getResizeHandler(),
2239
+ onClick: (e) => e.stopPropagation(),
2240
+ className: cn("absolute top-0 right-0 z-10 h-full w-1 cursor-col-resize touch-none select-none bg-border opacity-0 transition-opacity group-hover/header-row:opacity-100 hover:opacity-100", isResizing && "bg-primary opacity-100")
1546
2241
  });
1547
2242
  }
2243
+ function SortableHeader({ header }) {
2244
+ const direction = header.column.getIsSorted();
2245
+ const Icon = direction === "asc" ? ChevronUpIcon : direction === "desc" ? ChevronDownIcon : ChevronsUpDownIcon;
2246
+ return /* @__PURE__ */ jsxs(Button, {
2247
+ variant: "ghost",
2248
+ size: "sm",
2249
+ className: "-ml-2 h-7 data-[icon=inline-end]:pr-2",
2250
+ onClick: header.column.getToggleSortingHandler(),
2251
+ children: [flexRender(header.column.columnDef.header, header.getContext()), /* @__PURE__ */ jsx(Icon, {
2252
+ "data-icon": "inline-end",
2253
+ className: cn("size-3.5", direction === false && "opacity-50")
2254
+ })]
2255
+ });
2256
+ }
2257
+ function SkeletonRows({ columns, count }) {
2258
+ return /* @__PURE__ */ jsx(Fragment, { children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx(TableRow, { children: columns.map((col, ci) => /* @__PURE__ */ jsx(TableCell, {
2259
+ "data-test": "loading-cell",
2260
+ children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full max-w-full" })
2261
+ }, col.id ?? `col-${ci}`)) }, `skeleton-${i}`)) });
2262
+ }
2263
+ /**
2264
+ * Returns a callback ref for the scroll sentinel. The observer is (re)attached
2265
+ * whenever the sentinel node mounts — so it works even if the sentinel only
2266
+ * appears after the first page loads (the sentinel's presence is the gate). It
2267
+ * fires `onFetchMore` once per mount and disconnects immediately, so a single
2268
+ * page never triggers duplicate fetches; the sentinel remounts for the next page.
2269
+ */
2270
+ function useFetchMoreSentinel(onFetchMore, fillHeight, resetKey) {
2271
+ const onFetchMoreRef = React$1.useRef(onFetchMore);
2272
+ onFetchMoreRef.current = onFetchMore;
2273
+ const observerRef = React$1.useRef(null);
2274
+ const nodeRef = React$1.useRef(null);
2275
+ const armedRef = React$1.useRef(true);
2276
+ const observe = React$1.useCallback(() => {
2277
+ observerRef.current?.disconnect();
2278
+ observerRef.current = null;
2279
+ const node = nodeRef.current;
2280
+ if (!node) return;
2281
+ const root = fillHeight ? node.closest("[data-slot=\"table-container\"]") : null;
2282
+ const observer = new IntersectionObserver((entries) => {
2283
+ if (entries[entries.length - 1]?.isIntersecting) {
2284
+ if (armedRef.current) {
2285
+ armedRef.current = false;
2286
+ onFetchMoreRef.current?.();
2287
+ }
2288
+ } else armedRef.current = true;
2289
+ }, {
2290
+ root,
2291
+ rootMargin: "200px"
2292
+ });
2293
+ observer.observe(node);
2294
+ observerRef.current = observer;
2295
+ }, [fillHeight]);
2296
+ React$1.useEffect(() => {
2297
+ armedRef.current = true;
2298
+ observe();
2299
+ }, [resetKey, observe]);
2300
+ return React$1.useCallback((node) => {
2301
+ nodeRef.current = node;
2302
+ observe();
2303
+ }, [observe]);
2304
+ }
1548
2305
 
1549
2306
  //#endregion
1550
2307
  //#region src/components/drawer.tsx
@@ -3150,16 +3907,6 @@ function SheetDescription({ className, ...props }) {
3150
3907
  });
3151
3908
  }
3152
3909
 
3153
- //#endregion
3154
- //#region src/components/skeleton.tsx
3155
- function Skeleton({ className, ...props }) {
3156
- return /* @__PURE__ */ jsx("div", {
3157
- "data-slot": "skeleton",
3158
- className: cn("animate-pulse rounded-md bg-muted", className),
3159
- ...props
3160
- });
3161
- }
3162
-
3163
3910
  //#endregion
3164
3911
  //#region src/components/tooltip.tsx
3165
3912
  function TooltipProvider({ delay = 0, ...props }) {
@@ -3670,38 +4417,115 @@ const Toaster = ({ ...props }) => {
3670
4417
  };
3671
4418
 
3672
4419
  //#endregion
3673
- //#region src/components/spinner.tsx
3674
- const spinnerVariants = cva("animate-spin text-current", {
3675
- variants: { size: {
3676
- default: "size-4",
3677
- sm: "size-3",
3678
- lg: "size-5",
3679
- xl: "size-6"
3680
- } },
3681
- defaultVariants: { size: "default" }
4420
+ //#region src/components/stepper.tsx
4421
+ const StepperContext = React$1.createContext(void 0);
4422
+ function useStepper() {
4423
+ const ctx = React$1.useContext(StepperContext);
4424
+ if (!ctx) throw new Error("useStepper must be used within a Stepper");
4425
+ return ctx;
4426
+ }
4427
+ const Stepper = React$1.forwardRef((componentProps, forwardRef) => {
4428
+ const { steps, defaultValue, className, children, value, onValueChange, ...props } = componentProps;
4429
+ const [activeId = "", setActiveId] = useControllableState({
4430
+ prop: value,
4431
+ defaultProp: defaultValue ?? steps[0]?.id,
4432
+ onChange: onValueChange
4433
+ });
4434
+ const goTo = React$1.useCallback((id) => setActiveId(id), [setActiveId]);
4435
+ const getIndex = React$1.useCallback((id) => steps.findIndex((s) => s.id === id), [steps]);
4436
+ const contextValue = React$1.useMemo(() => ({
4437
+ activeId,
4438
+ goTo,
4439
+ getIndex,
4440
+ steps
4441
+ }), [
4442
+ activeId,
4443
+ goTo,
4444
+ getIndex,
4445
+ steps
4446
+ ]);
4447
+ return /* @__PURE__ */ jsx(StepperContext.Provider, {
4448
+ value: contextValue,
4449
+ children: /* @__PURE__ */ jsx("div", {
4450
+ ref: forwardRef,
4451
+ "data-slot": "stepper",
4452
+ className: cn("w-full", className),
4453
+ ...props,
4454
+ children
4455
+ })
4456
+ });
3682
4457
  });
3683
- function Spinner({ className, size, ...props }) {
3684
- return /* @__PURE__ */ jsxs("svg", {
3685
- "data-slot": "spinner",
3686
- xmlns: "http://www.w3.org/2000/svg",
3687
- fill: "none",
3688
- viewBox: "0 0 24 24",
3689
- className: cn(spinnerVariants({ size }), className),
4458
+ Stepper.displayName = "Stepper";
4459
+ const StepperProgress = React$1.forwardRef((componentProps, forwardRef) => {
4460
+ const { steps: stepIds, className, ...props } = componentProps;
4461
+ const { activeId, getIndex, steps } = useStepper();
4462
+ const ids = stepIds ?? steps.map((s) => s.id);
4463
+ const currentIndex = getIndex(activeId);
4464
+ return /* @__PURE__ */ jsx("div", {
4465
+ ref: forwardRef,
4466
+ "data-slot": "stepper-progress",
4467
+ "aria-hidden": "true",
4468
+ className: cn("flex w-full items-center gap-1.5", className),
3690
4469
  ...props,
3691
- children: [/* @__PURE__ */ jsx("circle", {
3692
- className: "opacity-25",
3693
- cx: "12",
3694
- cy: "12",
3695
- r: "10",
3696
- stroke: "currentColor",
3697
- strokeWidth: "4"
3698
- }), /* @__PURE__ */ jsx("path", {
3699
- className: "opacity-75",
3700
- fill: "currentColor",
3701
- d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
3702
- })]
4470
+ children: ids.map((id) => {
4471
+ const idx = getIndex(id);
4472
+ const state = idx < currentIndex ? "completed" : idx === currentIndex ? "active" : "inactive";
4473
+ return /* @__PURE__ */ jsx("span", {
4474
+ "data-slot": "stepper-progress-segment",
4475
+ "data-state": state,
4476
+ className: "bg-muted h-1.5 flex-1 rounded-full transition-colors duration-300 data-[state=active]:bg-primary data-[state=completed]:bg-primary"
4477
+ }, id);
4478
+ })
3703
4479
  });
3704
- }
4480
+ });
4481
+ StepperProgress.displayName = "StepperProgress";
4482
+ const StepperCounter = React$1.forwardRef((componentProps, forwardRef) => {
4483
+ const { steps: stepIds, className, children, ...props } = componentProps;
4484
+ const { activeId, steps } = useStepper();
4485
+ const ids = stepIds ?? steps.map((s) => s.id);
4486
+ const pos = Math.max(0, ids.indexOf(activeId));
4487
+ return /* @__PURE__ */ jsxs("p", {
4488
+ ref: forwardRef,
4489
+ "data-slot": "stepper-counter",
4490
+ className: cn("text-muted-foreground text-xs", "[[data-slot=stepper-progress]+&]:pt-1", className),
4491
+ ...props,
4492
+ children: [
4493
+ "Step ",
4494
+ pos + 1,
4495
+ " of ",
4496
+ ids.length,
4497
+ children
4498
+ ]
4499
+ });
4500
+ });
4501
+ StepperCounter.displayName = "StepperCounter";
4502
+ const StepperPanel = React$1.forwardRef((componentProps, forwardRef) => {
4503
+ const { className, ...props } = componentProps;
4504
+ const { activeId } = useStepper();
4505
+ return /* @__PURE__ */ jsx("div", {
4506
+ ref: forwardRef,
4507
+ "data-slot": "stepper-panel",
4508
+ "data-state": activeId,
4509
+ className: cn("w-full", className),
4510
+ ...props
4511
+ });
4512
+ });
4513
+ StepperPanel.displayName = "StepperPanel";
4514
+ const StepperContent = React$1.forwardRef((componentProps, forwardRef) => {
4515
+ const { value, forceMount, className, ...props } = componentProps;
4516
+ const { activeId } = useStepper();
4517
+ const isActive = value === activeId;
4518
+ if (!forceMount && !isActive) return null;
4519
+ return /* @__PURE__ */ jsx("div", {
4520
+ ref: forwardRef,
4521
+ "data-slot": "stepper-content",
4522
+ "data-state": activeId,
4523
+ className: cn("w-full", className, !isActive && forceMount && "hidden"),
4524
+ hidden: !isActive && forceMount,
4525
+ ...props
4526
+ });
4527
+ });
4528
+ StepperContent.displayName = "StepperContent";
3705
4529
 
3706
4530
  //#endregion
3707
4531
  //#region src/components/switch.tsx
@@ -3903,4 +4727,4 @@ function useAudioGauge(options = {}) {
3903
4727
  }
3904
4728
 
3905
4729
  //#endregion
3906
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertAction, AlertAction as BannerAction, AlertDescription, AlertDescription as BannerDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AlertTitle as BannerTitle, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge, BadgeGroup, BadgeGroupCount, Banner, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, CounterBadge, CountryFlag, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerBackdrop, DrawerBar, DrawerClose, DrawerContent, DrawerCreateHandle, DrawerDescription, DrawerFooter, DrawerHeader, DrawerMenu, DrawerMenuCheckboxItem, DrawerMenuGroup, DrawerMenuGroupLabel, DrawerMenuItem, DrawerMenuRadioGroup, DrawerMenuRadioItem, DrawerMenuSeparator, DrawerMenuTrigger, DrawerPanel, DrawerPopup, DrawerPortal, DrawerPrimitive, DrawerSwipeArea, DrawerTitle, DrawerTrigger, DrawerViewport, DropdownMenu, DropdownMenuAddon, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Dropzone, DropzoneActions, DropzoneContent, DropzoneDescription, DropzoneIcon, DropzoneTitle, DropzoneTrigger, EmojiPicker, EmojiPickerCategories, EmojiPickerContent, EmojiPickerTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FAB, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Gauge, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Label, List, ListCol, ListRow, NotificationQueueProvider, NotificationSlot, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue, RadioGroup, RadioGroupItem, ScrollArea, ScrollAreaPrimitive, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemeProvider, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alertVariants, badgeVariants, bannerVariants, buttonGroupVariants, buttonVariants, cn, counterBadgeVariants, fabVariants, selectTriggerVariants, spinnerVariants, toast, toggleVariants, useAudioGauge, useComboboxAnchor, useNotification, useNotificationQueue, useSidebar, useTheme };
4730
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActionBar, ActionBarButton, ActionBarContext, ActionBarHeight, Alert, AlertAction, AlertAction as BannerAction, AlertDescription, AlertDescription as BannerDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AlertTitle as BannerTitle, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge, BadgeGroup, BadgeGroupCount, Banner, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, Chip, ChipRemove, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator, ComboboxTrigger, ComboboxValue, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, CounterBadge, CountryFlag, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerBackdrop, DrawerBar, DrawerClose, DrawerContent, DrawerCreateHandle, DrawerDescription, DrawerFooter, DrawerHeader, DrawerMenu, DrawerMenuCheckboxItem, DrawerMenuGroup, DrawerMenuGroupLabel, DrawerMenuItem, DrawerMenuRadioGroup, DrawerMenuRadioItem, DrawerMenuSeparator, DrawerMenuTrigger, DrawerPanel, DrawerPopup, DrawerPortal, DrawerPrimitive, DrawerSwipeArea, DrawerTitle, DrawerTrigger, DrawerViewport, DropdownMenu, DropdownMenuAddon, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Dropzone, DropzoneActions, DropzoneContent, DropzoneDescription, DropzoneIcon, DropzoneTitle, DropzoneTrigger, EmojiPicker, EmojiPickerCategories, EmojiPickerContent, EmojiPickerTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FAB, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Filter, FilterClearAllButton, FilterGroup, Gauge, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Label, List, ListCol, ListRow, NotificationQueueProvider, NotificationSlot, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue, RadioGroup, RadioGroupItem, ScrollArea, ScrollAreaPrimitive, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Stepper, StepperContent, StepperCounter, StepperPanel, StepperProgress, Switch, Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemeProvider, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alertVariants, bannerVariants, buttonGroupVariants, buttonVariants, cn, counterBadgeVariants, fabVariants, selectTriggerVariants, spinnerVariants, toast, toggleVariants, useAudioGauge, useComboboxAnchor, useNotification, useNotificationQueue, useSidebar, useStepper, useTheme };