@timbal-ai/timbal-react 1.2.0 → 1.3.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/ui.cjs CHANGED
@@ -52,8 +52,10 @@ __export(ui_exports, {
52
52
  AspectRatio: () => AspectRatio,
53
53
  Avatar: () => Avatar,
54
54
  AvatarFallback: () => AvatarFallback,
55
+ AvatarGroup: () => AvatarGroup,
55
56
  AvatarImage: () => AvatarImage,
56
57
  Badge: () => Badge,
58
+ Banner: () => Banner,
57
59
  Breadcrumb: () => Breadcrumb,
58
60
  BreadcrumbEllipsis: () => BreadcrumbEllipsis,
59
61
  BreadcrumbItem: () => BreadcrumbItem,
@@ -78,6 +80,7 @@ __export(ui_exports, {
78
80
  ChartTooltip: () => ChartTooltip,
79
81
  ChartTooltipContent: () => ChartTooltipContent,
80
82
  Checkbox: () => Checkbox,
83
+ CircularProgress: () => CircularProgress,
81
84
  Collapsible: () => Collapsible,
82
85
  CollapsibleContent: () => CollapsibleContent,
83
86
  CollapsibleTrigger: () => CollapsibleTrigger,
@@ -116,6 +119,7 @@ __export(ui_exports, {
116
119
  ContextMenuSubContent: () => ContextMenuSubContent,
117
120
  ContextMenuSubTrigger: () => ContextMenuSubTrigger,
118
121
  ContextMenuTrigger: () => ContextMenuTrigger,
122
+ CopyButton: () => CopyButton,
119
123
  DatePicker: () => DatePicker,
120
124
  DatePickerButton: () => DatePickerButton,
121
125
  DatePickerCalendar: () => DatePickerCalendar,
@@ -191,6 +195,7 @@ __export(ui_exports, {
191
195
  NavigationMenuList: () => NavigationMenuList,
192
196
  NavigationMenuTrigger: () => NavigationMenuTrigger,
193
197
  NavigationMenuViewport: () => NavigationMenuViewport,
198
+ NumberField: () => NumberField,
194
199
  Pagination: () => Pagination,
195
200
  PaginationContent: () => PaginationContent,
196
201
  PaginationEllipsis: () => PaginationEllipsis,
@@ -206,6 +211,7 @@ __export(ui_exports, {
206
211
  Progress: () => Progress,
207
212
  RadioGroup: () => RadioGroup,
208
213
  RadioGroupItem: () => RadioGroupItem,
214
+ Rating: () => Rating,
209
215
  ScrollArea: () => ScrollArea,
210
216
  ScrollBar: () => ScrollBar,
211
217
  Select: () => Select,
@@ -230,7 +236,9 @@ __export(ui_exports, {
230
236
  Shimmer: () => Shimmer,
231
237
  Skeleton: () => Skeleton,
232
238
  Slider: () => Slider,
239
+ Snippet: () => Snippet,
233
240
  Spinner: () => Spinner,
241
+ Stepper: () => Stepper,
234
242
  Switch: () => Switch,
235
243
  Table: () => Table,
236
244
  TableBody: () => TableBody,
@@ -240,8 +248,10 @@ __export(ui_exports, {
240
248
  TableHead: () => TableHead,
241
249
  TableHeader: () => TableHeader,
242
250
  TableRow: () => TableRow,
251
+ TagInput: () => TagInput,
243
252
  Textarea: () => Textarea,
244
253
  TimbalV2Button: () => TimbalV2Button,
254
+ Timeline: () => Timeline,
245
255
  Toast: () => Toast,
246
256
  ToastAction: () => ToastAction,
247
257
  ToastClose: () => ToastClose,
@@ -481,12 +491,12 @@ var TimbalV2Button = React.forwardRef(function TimbalV2Button2({
481
491
  ...props
482
492
  }, ref) {
483
493
  const isDisabled = disabled || isLoading;
484
- const sizeClass = isIconOnly ? TIMBAL_V2_SIZE_ICON[size] : TIMBAL_V2_SIZE_HEIGHT[size];
494
+ const sizeClass2 = isIconOnly ? TIMBAL_V2_SIZE_ICON[size] : TIMBAL_V2_SIZE_HEIGHT[size];
485
495
  const radiusClass = "rounded-full";
486
496
  const sharedRootClass = cn(
487
497
  "relative box-border inline-flex items-center justify-center gap-2 whitespace-nowrap border-0 text-sm font-normal shadow-none transition duration-200 ease-in-out",
488
498
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60 focus-visible:ring-offset-1 focus-visible:ring-offset-background",
489
- sizeClass,
499
+ sizeClass2,
490
500
  radiusClass,
491
501
  TIMBAL_V2_BORDER[variant],
492
502
  TIMBAL_V2_SHADOW[variant],
@@ -2425,56 +2435,71 @@ function Calendar({
2425
2435
  showOutsideDays = true,
2426
2436
  ...props
2427
2437
  }) {
2438
+ const defaults = (0, import_react_day_picker.getDefaultClassNames)();
2428
2439
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2429
2440
  import_react_day_picker.DayPicker,
2430
2441
  {
2431
2442
  showOutsideDays,
2432
- className: cn("p-3", className),
2443
+ className: cn("p-4", className),
2433
2444
  classNames: {
2434
- months: "flex flex-col gap-4 sm:flex-row sm:gap-4",
2435
- month: "flex flex-col gap-4",
2436
- month_caption: "relative flex items-center justify-center pt-1",
2437
- caption_label: "text-sm font-medium",
2438
- nav: "flex items-center gap-1",
2439
- button_previous: cn(
2440
- buttonVariants({ variant: "outline" }),
2441
- "absolute left-1 size-7 bg-transparent p-0 opacity-70 hover:opacity-100"
2445
+ root: cn("w-fit", defaults.root),
2446
+ months: cn("relative flex flex-col gap-4 sm:flex-row", defaults.months),
2447
+ month: cn("flex w-full flex-col gap-3", defaults.month),
2448
+ nav: cn(
2449
+ "absolute inset-x-0 top-0 flex items-center justify-between",
2450
+ defaults.nav
2442
2451
  ),
2443
- button_next: cn(
2444
- buttonVariants({ variant: "outline" }),
2445
- "absolute right-1 size-7 bg-transparent p-0 opacity-70 hover:opacity-100"
2452
+ button_previous: cn(navButtonClass, defaults.button_previous),
2453
+ button_next: cn(navButtonClass, defaults.button_next),
2454
+ month_caption: cn(
2455
+ "flex h-10 items-center justify-center",
2456
+ defaults.month_caption
2457
+ ),
2458
+ caption_label: cn("text-sm font-semibold", defaults.caption_label),
2459
+ dropdowns: cn(
2460
+ "flex h-10 items-center justify-center gap-1.5 text-sm font-semibold",
2461
+ defaults.dropdowns
2462
+ ),
2463
+ dropdown_root: cn(
2464
+ "relative rounded-md border border-border focus-within:ring-2 focus-within:ring-foreground/10",
2465
+ defaults.dropdown_root
2466
+ ),
2467
+ dropdown: cn("absolute inset-0 bg-popover opacity-0", defaults.dropdown),
2468
+ month_grid: cn("border-separate border-spacing-y-1", defaults.month_grid),
2469
+ weekdays: cn(defaults.weekdays),
2470
+ weekday: cn(
2471
+ "size-10 pb-2 text-xs font-medium text-muted-foreground",
2472
+ defaults.weekday
2473
+ ),
2474
+ week: cn(defaults.week),
2475
+ week_number_header: cn("size-10", defaults.week_number_header),
2476
+ week_number: cn(
2477
+ "text-xs text-muted-foreground",
2478
+ defaults.week_number
2446
2479
  ),
2447
- month_grid: "w-full border-collapse",
2448
- weekdays: "flex",
2449
- weekday: "w-9 rounded-md text-[0.8rem] font-normal text-muted-foreground",
2450
- week: "mt-2 flex w-full",
2451
2480
  day: cn(
2452
- "relative size-9 p-0 text-center text-sm",
2453
- "[&:has([aria-selected].day-range-end)]:rounded-r-md",
2454
- "[&:has([aria-selected].day-outside)]:bg-accent/50",
2455
- "[&:has([aria-selected])]:bg-accent",
2456
- "first:[&:has([aria-selected])]:rounded-l-md",
2457
- "last:[&:has([aria-selected])]:rounded-r-md",
2458
- "focus-within:relative focus-within:z-10"
2481
+ "relative size-10 p-0 text-center text-sm focus-within:relative focus-within:z-10",
2482
+ defaults.day
2459
2483
  ),
2460
- day_button: cn(
2461
- buttonVariants({ variant: "ghost" }),
2462
- "size-9 p-0 font-normal aria-selected:opacity-100"
2484
+ range_start: cn("rounded-l-md", defaults.range_start),
2485
+ range_middle: cn("rounded-none", defaults.range_middle),
2486
+ range_end: cn("rounded-r-md", defaults.range_end),
2487
+ today: cn(
2488
+ "[&>button]:font-semibold [&>button:not([data-selected-single=true]):not([data-range-middle=true])]:text-primary",
2489
+ defaults.today
2463
2490
  ),
2464
- range_start: "day-range-start rounded-l-md",
2465
- range_middle: "day-range-middle aria-selected:bg-accent",
2466
- range_end: "day-range-end rounded-r-md",
2467
- selected: "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
2468
- today: "bg-accent text-accent-foreground",
2469
- outside: "day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
2470
- disabled: "text-muted-foreground opacity-50",
2471
- hidden: "invisible",
2491
+ outside: cn(
2492
+ "text-muted-foreground/60 aria-selected:text-muted-foreground",
2493
+ defaults.outside
2494
+ ),
2495
+ disabled: cn("text-muted-foreground opacity-50", defaults.disabled),
2496
+ hidden: cn("invisible", defaults.hidden),
2472
2497
  ...classNames
2473
2498
  },
2474
2499
  components: {
2475
- Chevron: ({ orientation, className: className2, ...chevronProps }) => {
2476
- const Icon = orientation === "left" ? import_lucide_react11.ChevronLeftIcon : import_lucide_react11.ChevronRightIcon;
2477
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { className: cn("size-4", className2), ...chevronProps });
2500
+ Chevron: ({ orientation, className: chevronClass, ...chevronProps }) => {
2501
+ const Icon = orientation === "left" ? import_lucide_react11.ChevronLeftIcon : orientation === "right" ? import_lucide_react11.ChevronRightIcon : import_lucide_react11.ChevronDownIcon;
2502
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { className: cn("size-4", chevronClass), ...chevronProps });
2478
2503
  },
2479
2504
  DayButton: CalendarDayButton
2480
2505
  },
@@ -2482,6 +2507,12 @@ function Calendar({
2482
2507
  }
2483
2508
  );
2484
2509
  }
2510
+ var navButtonClass = cn(
2511
+ "inline-flex size-8 items-center justify-center rounded-md text-muted-foreground transition-colors",
2512
+ "hover:bg-accent hover:text-accent-foreground",
2513
+ "disabled:pointer-events-none disabled:opacity-40",
2514
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10"
2515
+ );
2485
2516
  function CalendarDayButton({
2486
2517
  className,
2487
2518
  day,
@@ -2492,25 +2523,26 @@ function CalendarDayButton({
2492
2523
  React3.useEffect(() => {
2493
2524
  if (modifiers.focused) ref.current?.focus();
2494
2525
  }, [modifiers.focused]);
2526
+ const isSingle = modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle;
2495
2527
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2496
2528
  "button",
2497
2529
  {
2498
2530
  ref,
2499
2531
  type: "button",
2500
2532
  "data-day": day.date.toLocaleDateString(),
2501
- "data-selected-single": modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle,
2502
- "data-range-start": modifiers.range_start,
2503
- "data-range-end": modifiers.range_end,
2504
- "data-range-middle": modifiers.range_middle,
2533
+ "data-selected-single": isSingle || void 0,
2534
+ "data-range-start": modifiers.range_start || void 0,
2535
+ "data-range-end": modifiers.range_end || void 0,
2536
+ "data-range-middle": modifiers.range_middle || void 0,
2505
2537
  className: cn(
2506
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors",
2538
+ "inline-flex size-full items-center justify-center rounded-md text-sm font-normal transition-colors",
2507
2539
  "hover:bg-accent hover:text-accent-foreground",
2508
- "focus-visible:ring-2 focus-visible:ring-foreground/10 focus-visible:outline-none",
2540
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15",
2509
2541
  "disabled:pointer-events-none disabled:opacity-50",
2510
- "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground",
2511
- "data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground",
2512
- "data-[range-start=true]:rounded-l-md data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground",
2513
- "data-[range-end=true]:rounded-r-md data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground",
2542
+ "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[selected-single=true]:hover:bg-primary",
2543
+ "data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground",
2544
+ "data-[range-start=true]:rounded-l-md data-[range-start=true]:rounded-r-none data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground",
2545
+ "data-[range-end=true]:rounded-r-md data-[range-end=true]:rounded-l-none data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground",
2514
2546
  className
2515
2547
  ),
2516
2548
  ...props
@@ -4578,6 +4610,673 @@ var PillSegmentedTabs = ({
4578
4610
  )) });
4579
4611
  };
4580
4612
  var MemoPillSegmentedTabs = (0, import_react3.memo)(PillSegmentedTabs);
4613
+
4614
+ // src/ui/avatar-group.tsx
4615
+ var React7 = __toESM(require("react"), 1);
4616
+ var import_jsx_runtime52 = require("react/jsx-runtime");
4617
+ var spacingClass = {
4618
+ sm: "-space-x-2",
4619
+ md: "-space-x-3"
4620
+ };
4621
+ function AvatarGroup({
4622
+ className,
4623
+ children,
4624
+ max,
4625
+ spacing = "sm",
4626
+ ...props
4627
+ }) {
4628
+ const items = React7.Children.toArray(children);
4629
+ const overflow = typeof max === "number" ? items.length - max : 0;
4630
+ const visible = typeof max === "number" ? items.slice(0, max) : items;
4631
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
4632
+ "div",
4633
+ {
4634
+ "data-slot": "avatar-group",
4635
+ className: cn(
4636
+ "flex items-center",
4637
+ spacingClass[spacing],
4638
+ "[&>*]:rounded-full [&>*]:ring-2 [&>*]:ring-background",
4639
+ className
4640
+ ),
4641
+ ...props,
4642
+ children: [
4643
+ visible,
4644
+ overflow > 0 ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
4645
+ "span",
4646
+ {
4647
+ "aria-label": `${overflow} more`,
4648
+ className: "inline-flex size-8 items-center justify-center rounded-full bg-muted text-xs font-medium text-muted-foreground ring-2 ring-background",
4649
+ children: [
4650
+ "+",
4651
+ overflow
4652
+ ]
4653
+ }
4654
+ ) : null
4655
+ ]
4656
+ }
4657
+ );
4658
+ }
4659
+
4660
+ // src/ui/stepper.tsx
4661
+ var import_lucide_react19 = require("lucide-react");
4662
+ var import_jsx_runtime53 = require("react/jsx-runtime");
4663
+ function Stepper({
4664
+ steps,
4665
+ current,
4666
+ orientation = "horizontal",
4667
+ className,
4668
+ ...props
4669
+ }) {
4670
+ const isVertical = orientation === "vertical";
4671
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
4672
+ "ol",
4673
+ {
4674
+ "data-slot": "stepper",
4675
+ className: cn(
4676
+ "flex",
4677
+ isVertical ? "flex-col gap-0" : "items-start gap-2",
4678
+ className
4679
+ ),
4680
+ ...props,
4681
+ children: steps.map((step, index) => {
4682
+ const complete = index < current;
4683
+ const active = index === current;
4684
+ const last = index === steps.length - 1;
4685
+ const connectorFilled = index < current;
4686
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
4687
+ "li",
4688
+ {
4689
+ "aria-current": active ? "step" : void 0,
4690
+ className: cn(
4691
+ "flex min-w-0",
4692
+ isVertical ? "gap-3" : "flex-1 flex-col gap-1.5",
4693
+ last && !isVertical && "flex-none"
4694
+ ),
4695
+ children: [
4696
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: cn("flex items-center gap-2", isVertical && "flex-col"), children: [
4697
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
4698
+ "span",
4699
+ {
4700
+ className: cn(
4701
+ "inline-flex size-7 shrink-0 items-center justify-center rounded-full border text-xs font-medium transition-colors",
4702
+ complete && "border-primary bg-primary text-primary-foreground",
4703
+ active && "border-primary text-primary",
4704
+ !complete && !active && "border-border text-muted-foreground"
4705
+ ),
4706
+ children: complete ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react19.CheckIcon, { className: "size-3.5", "aria-hidden": true }) : index + 1
4707
+ }
4708
+ ),
4709
+ !last ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
4710
+ "span",
4711
+ {
4712
+ "aria-hidden": true,
4713
+ className: cn(
4714
+ isVertical ? "w-px flex-1" : "h-px flex-1",
4715
+ connectorFilled ? "bg-primary" : "bg-border",
4716
+ isVertical && "min-h-6"
4717
+ )
4718
+ }
4719
+ ) : null
4720
+ ] }),
4721
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: cn("min-w-0", isVertical && "pb-4"), children: [
4722
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
4723
+ "p",
4724
+ {
4725
+ className: cn(
4726
+ "truncate text-sm font-medium",
4727
+ active ? "text-foreground" : "text-muted-foreground"
4728
+ ),
4729
+ children: step.label
4730
+ }
4731
+ ),
4732
+ step.description ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "truncate text-xs text-muted-foreground", children: step.description }) : null
4733
+ ] })
4734
+ ]
4735
+ },
4736
+ step.id
4737
+ );
4738
+ })
4739
+ }
4740
+ );
4741
+ }
4742
+
4743
+ // src/ui/timeline.tsx
4744
+ var import_jsx_runtime54 = require("react/jsx-runtime");
4745
+ var dotToneClass = {
4746
+ default: "border-border bg-card",
4747
+ primary: "border-primary bg-primary",
4748
+ success: "border-emerald-500 bg-emerald-500",
4749
+ warn: "border-amber-500 bg-amber-500",
4750
+ danger: "border-destructive bg-destructive"
4751
+ };
4752
+ function Timeline({ items, className, ...props }) {
4753
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("ol", { "data-slot": "timeline", className: cn("flex flex-col", className), ...props, children: items.map((item, index) => {
4754
+ const last = index === items.length - 1;
4755
+ const tone = item.tone ?? "default";
4756
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("li", { className: "relative flex gap-3 pb-5 last:pb-0", children: [
4757
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex flex-col items-center", children: [
4758
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
4759
+ "span",
4760
+ {
4761
+ className: cn(
4762
+ "z-[1] mt-0.5 inline-flex size-3 shrink-0 items-center justify-center rounded-full border-2",
4763
+ dotToneClass[tone],
4764
+ item.icon && "size-6"
4765
+ ),
4766
+ children: item.icon
4767
+ }
4768
+ ),
4769
+ !last ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { "aria-hidden": true, className: "w-px flex-1 bg-border" }) : null
4770
+ ] }),
4771
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "min-w-0 flex-1 pb-0.5", children: [
4772
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
4773
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("p", { className: "text-sm font-medium text-foreground", children: item.title }),
4774
+ item.meta ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground tabular-nums", children: item.meta }) : null
4775
+ ] }),
4776
+ item.description ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("p", { className: "mt-0.5 text-sm text-muted-foreground", children: item.description }) : null
4777
+ ] })
4778
+ ] }, item.id);
4779
+ }) });
4780
+ }
4781
+
4782
+ // src/ui/rating.tsx
4783
+ var React8 = __toESM(require("react"), 1);
4784
+ var import_lucide_react20 = require("lucide-react");
4785
+ var import_jsx_runtime55 = require("react/jsx-runtime");
4786
+ var sizeClass = { sm: "size-4", md: "size-5", lg: "size-6" };
4787
+ function Rating({
4788
+ value: valueProp,
4789
+ defaultValue = 0,
4790
+ onChange,
4791
+ max = 5,
4792
+ readOnly = false,
4793
+ disabled = false,
4794
+ size = "md",
4795
+ label = "Rating",
4796
+ className
4797
+ }) {
4798
+ const [uncontrolled, setUncontrolled] = React8.useState(defaultValue);
4799
+ const isControlled = valueProp !== void 0;
4800
+ const value = isControlled ? valueProp : uncontrolled;
4801
+ const [hover, setHover] = React8.useState(null);
4802
+ const interactive = !readOnly && !disabled;
4803
+ const shown = hover ?? value;
4804
+ const set = (next) => {
4805
+ if (!interactive) return;
4806
+ if (!isControlled) setUncontrolled(next);
4807
+ onChange?.(next);
4808
+ };
4809
+ if (!interactive) {
4810
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4811
+ "span",
4812
+ {
4813
+ "data-slot": "rating",
4814
+ role: "img",
4815
+ "aria-label": `${label}: ${value} of ${max}`,
4816
+ className: cn("inline-flex items-center gap-0.5", disabled && "opacity-50", className),
4817
+ children: Array.from({ length: max }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4818
+ import_lucide_react20.StarIcon,
4819
+ {
4820
+ "aria-hidden": true,
4821
+ className: cn(
4822
+ sizeClass[size],
4823
+ i < value ? "fill-amber-400 text-amber-400" : "fill-transparent text-muted-foreground/40"
4824
+ )
4825
+ },
4826
+ i
4827
+ ))
4828
+ }
4829
+ );
4830
+ }
4831
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4832
+ "span",
4833
+ {
4834
+ "data-slot": "rating",
4835
+ role: "radiogroup",
4836
+ "aria-label": label,
4837
+ className: cn("inline-flex items-center gap-0.5", className),
4838
+ onMouseLeave: () => setHover(null),
4839
+ children: Array.from({ length: max }, (_, i) => {
4840
+ const unit = i + 1;
4841
+ const filled = unit <= shown;
4842
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4843
+ "button",
4844
+ {
4845
+ type: "button",
4846
+ role: "radio",
4847
+ "aria-checked": unit === value,
4848
+ "aria-label": `${unit} ${unit === 1 ? "star" : "stars"}`,
4849
+ onClick: () => set(unit === value ? 0 : unit),
4850
+ onMouseEnter: () => setHover(unit),
4851
+ onFocus: () => setHover(unit),
4852
+ onBlur: () => setHover(null),
4853
+ className: "rounded-sm p-0.5 transition-transform hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15",
4854
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4855
+ import_lucide_react20.StarIcon,
4856
+ {
4857
+ className: cn(
4858
+ sizeClass[size],
4859
+ "transition-colors",
4860
+ filled ? "fill-amber-400 text-amber-400" : "fill-transparent text-muted-foreground/40"
4861
+ )
4862
+ }
4863
+ )
4864
+ },
4865
+ i
4866
+ );
4867
+ })
4868
+ }
4869
+ );
4870
+ }
4871
+
4872
+ // src/ui/number-field.tsx
4873
+ var React9 = __toESM(require("react"), 1);
4874
+ var import_lucide_react21 = require("lucide-react");
4875
+ var import_jsx_runtime56 = require("react/jsx-runtime");
4876
+ var heightClass = { sm: "h-9", default: "h-10" };
4877
+ var stepButtonClass = "inline-flex aspect-square h-full items-center justify-center text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/15";
4878
+ function clamp(n, min, max) {
4879
+ if (typeof min === "number" && n < min) return min;
4880
+ if (typeof max === "number" && n > max) return max;
4881
+ return n;
4882
+ }
4883
+ function NumberField({
4884
+ value: valueProp,
4885
+ defaultValue = 0,
4886
+ onValueChange,
4887
+ min,
4888
+ max,
4889
+ step = 1,
4890
+ size = "default",
4891
+ disabled,
4892
+ ariaLabel,
4893
+ className,
4894
+ ...inputProps
4895
+ }) {
4896
+ const [uncontrolled, setUncontrolled] = React9.useState(defaultValue);
4897
+ const isControlled = valueProp !== void 0;
4898
+ const value = isControlled ? valueProp : uncontrolled;
4899
+ const commit = (next) => {
4900
+ const clamped = clamp(next, min, max);
4901
+ if (!isControlled) setUncontrolled(clamped);
4902
+ onValueChange?.(clamped);
4903
+ };
4904
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
4905
+ "div",
4906
+ {
4907
+ "data-slot": "number-field",
4908
+ className: cn(
4909
+ controlSurfaceClass,
4910
+ "inline-flex w-full items-stretch overflow-hidden rounded-lg p-0",
4911
+ heightClass[size],
4912
+ disabled && "opacity-50",
4913
+ className
4914
+ ),
4915
+ children: [
4916
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
4917
+ "button",
4918
+ {
4919
+ type: "button",
4920
+ tabIndex: -1,
4921
+ "aria-hidden": true,
4922
+ disabled: disabled || typeof min === "number" && value <= min,
4923
+ onClick: () => commit(value - step),
4924
+ className: cn(stepButtonClass, "border-r border-border"),
4925
+ children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_lucide_react21.MinusIcon, { className: "size-4" })
4926
+ }
4927
+ ),
4928
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
4929
+ "input",
4930
+ {
4931
+ type: "number",
4932
+ inputMode: "decimal",
4933
+ role: "spinbutton",
4934
+ "aria-label": ariaLabel,
4935
+ "aria-valuenow": value,
4936
+ "aria-valuemin": min,
4937
+ "aria-valuemax": max,
4938
+ value: Number.isNaN(value) ? "" : value,
4939
+ disabled,
4940
+ onChange: (e) => {
4941
+ const next = e.target.valueAsNumber;
4942
+ if (Number.isNaN(next)) {
4943
+ if (!isControlled) setUncontrolled(Number.NaN);
4944
+ return;
4945
+ }
4946
+ commit(next);
4947
+ },
4948
+ className: "w-full min-w-0 [appearance:textfield] bg-transparent px-2 text-center text-sm text-foreground outline-none disabled:cursor-not-allowed [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
4949
+ ...inputProps
4950
+ }
4951
+ ),
4952
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
4953
+ "button",
4954
+ {
4955
+ type: "button",
4956
+ tabIndex: -1,
4957
+ "aria-hidden": true,
4958
+ disabled: disabled || typeof max === "number" && value >= max,
4959
+ onClick: () => commit(value + step),
4960
+ className: cn(stepButtonClass, "border-l border-border"),
4961
+ children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_lucide_react21.PlusIcon, { className: "size-4" })
4962
+ }
4963
+ )
4964
+ ]
4965
+ }
4966
+ );
4967
+ }
4968
+
4969
+ // src/ui/tag-input.tsx
4970
+ var React10 = __toESM(require("react"), 1);
4971
+ var import_lucide_react22 = require("lucide-react");
4972
+ var import_jsx_runtime57 = require("react/jsx-runtime");
4973
+ function TagInput({
4974
+ value: valueProp,
4975
+ defaultValue = [],
4976
+ onChange,
4977
+ placeholder,
4978
+ separators = ["Enter", ","],
4979
+ dedupe = true,
4980
+ max,
4981
+ disabled,
4982
+ ariaLabel,
4983
+ className,
4984
+ inputClassName
4985
+ }) {
4986
+ const [uncontrolled, setUncontrolled] = React10.useState(defaultValue);
4987
+ const isControlled = valueProp !== void 0;
4988
+ const tags = isControlled ? valueProp : uncontrolled;
4989
+ const [draft, setDraft] = React10.useState("");
4990
+ const setTags = (next) => {
4991
+ if (!isControlled) setUncontrolled(next);
4992
+ onChange?.(next);
4993
+ };
4994
+ const addTag = (raw) => {
4995
+ const tag = raw.trim();
4996
+ if (!tag) return;
4997
+ if (typeof max === "number" && tags.length >= max) return;
4998
+ if (dedupe && tags.some((t) => t.toLowerCase() === tag.toLowerCase())) {
4999
+ setDraft("");
5000
+ return;
5001
+ }
5002
+ setTags([...tags, tag]);
5003
+ setDraft("");
5004
+ };
5005
+ const removeAt = (index) => {
5006
+ setTags(tags.filter((_, i) => i !== index));
5007
+ };
5008
+ const handleKeyDown = (event) => {
5009
+ if (separators.includes(event.key)) {
5010
+ event.preventDefault();
5011
+ addTag(draft);
5012
+ } else if (event.key === "Backspace" && draft === "" && tags.length > 0) {
5013
+ removeAt(tags.length - 1);
5014
+ }
5015
+ };
5016
+ return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
5017
+ "div",
5018
+ {
5019
+ "data-slot": "tag-input",
5020
+ className: cn(
5021
+ controlSurfaceClass,
5022
+ "flex min-h-10 w-full flex-wrap items-center gap-1.5 rounded-lg px-2 py-1.5",
5023
+ disabled && "pointer-events-none opacity-50",
5024
+ className
5025
+ ),
5026
+ children: [
5027
+ tags.map((tag, index) => /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
5028
+ "span",
5029
+ {
5030
+ className: "inline-flex items-center gap-1 rounded-md bg-muted py-0.5 pl-2 pr-1 text-xs font-medium text-foreground",
5031
+ children: [
5032
+ tag,
5033
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
5034
+ "button",
5035
+ {
5036
+ type: "button",
5037
+ "aria-label": `Remove ${tag}`,
5038
+ onClick: () => removeAt(index),
5039
+ className: "inline-flex size-4 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-foreground/10 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20",
5040
+ children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_lucide_react22.XIcon, { className: "size-3", "aria-hidden": true })
5041
+ }
5042
+ )
5043
+ ]
5044
+ },
5045
+ `${tag}-${index}`
5046
+ )),
5047
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
5048
+ "input",
5049
+ {
5050
+ type: "text",
5051
+ "aria-label": ariaLabel ?? placeholder ?? "Add tag",
5052
+ value: draft,
5053
+ disabled,
5054
+ placeholder: tags.length === 0 ? placeholder : void 0,
5055
+ onChange: (e) => setDraft(e.target.value),
5056
+ onKeyDown: handleKeyDown,
5057
+ onBlur: () => addTag(draft),
5058
+ className: cn(
5059
+ "min-w-[6rem] flex-1 bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground/70",
5060
+ inputClassName
5061
+ )
5062
+ }
5063
+ )
5064
+ ]
5065
+ }
5066
+ );
5067
+ }
5068
+
5069
+ // src/ui/banner.tsx
5070
+ var import_lucide_react23 = require("lucide-react");
5071
+ var import_jsx_runtime58 = require("react/jsx-runtime");
5072
+ var bannerToneClass = {
5073
+ default: "border-border bg-muted/50 text-foreground",
5074
+ primary: "border-primary/20 bg-primary/10 text-foreground",
5075
+ success: "border-emerald-500/25 bg-emerald-500/10 text-foreground [&_[data-banner-icon]]:text-emerald-600 dark:[&_[data-banner-icon]]:text-emerald-400",
5076
+ warn: "border-amber-500/25 bg-amber-500/10 text-foreground [&_[data-banner-icon]]:text-amber-600 dark:[&_[data-banner-icon]]:text-amber-400",
5077
+ danger: "border-destructive/25 bg-destructive/10 text-foreground [&_[data-banner-icon]]:text-destructive"
5078
+ };
5079
+ function Banner({
5080
+ tone = "default",
5081
+ icon,
5082
+ title,
5083
+ actions,
5084
+ onDismiss,
5085
+ className,
5086
+ children,
5087
+ ...props
5088
+ }) {
5089
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
5090
+ "div",
5091
+ {
5092
+ "data-slot": "banner",
5093
+ role: "status",
5094
+ className: cn(
5095
+ "flex w-full items-start gap-3 rounded-lg border px-4 py-3 text-sm",
5096
+ bannerToneClass[tone],
5097
+ className
5098
+ ),
5099
+ ...props,
5100
+ children: [
5101
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { "data-banner-icon": true, className: "mt-0.5 shrink-0 [&_svg]:size-4", children: icon }) : null,
5102
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "min-w-0 flex-1", children: [
5103
+ title ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("p", { className: "font-medium", children: title }) : null,
5104
+ children ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: cn("text-muted-foreground", title && "mt-0.5"), children }) : null
5105
+ ] }),
5106
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions }) : null,
5107
+ onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
5108
+ "button",
5109
+ {
5110
+ type: "button",
5111
+ "aria-label": "Dismiss",
5112
+ onClick: onDismiss,
5113
+ className: "-mr-1 -mt-0.5 inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-foreground/10 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15",
5114
+ children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_lucide_react23.XIcon, { className: "size-4", "aria-hidden": true })
5115
+ }
5116
+ ) : null
5117
+ ]
5118
+ }
5119
+ );
5120
+ }
5121
+
5122
+ // src/ui/copy-button.tsx
5123
+ var React11 = __toESM(require("react"), 1);
5124
+ var import_lucide_react24 = require("lucide-react");
5125
+ var import_jsx_runtime59 = require("react/jsx-runtime");
5126
+ function CopyButton({
5127
+ value,
5128
+ timeout = 1500,
5129
+ onCopied,
5130
+ className,
5131
+ children,
5132
+ onClick,
5133
+ ...props
5134
+ }) {
5135
+ const [copied, setCopied] = React11.useState(false);
5136
+ const timer = React11.useRef(void 0);
5137
+ React11.useEffect(() => () => clearTimeout(timer.current), []);
5138
+ const handleClick = async (event) => {
5139
+ onClick?.(event);
5140
+ if (event.defaultPrevented) return;
5141
+ try {
5142
+ await navigator.clipboard.writeText(value);
5143
+ setCopied(true);
5144
+ onCopied?.(value);
5145
+ clearTimeout(timer.current);
5146
+ timer.current = setTimeout(() => setCopied(false), timeout);
5147
+ } catch {
5148
+ }
5149
+ };
5150
+ const Icon = copied ? import_lucide_react24.CheckIcon : import_lucide_react24.CopyIcon;
5151
+ return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
5152
+ "button",
5153
+ {
5154
+ type: "button",
5155
+ "data-slot": "copy-button",
5156
+ "data-copied": copied || void 0,
5157
+ "aria-label": copied ? "Copied" : "Copy",
5158
+ onClick: handleClick,
5159
+ className: cn(
5160
+ "inline-flex items-center justify-center gap-1.5 rounded-md text-sm font-medium text-muted-foreground transition-colors",
5161
+ "hover:bg-accent hover:text-foreground data-[copied=true]:text-emerald-600 dark:data-[copied=true]:text-emerald-400",
5162
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15",
5163
+ children ? "h-8 px-2" : "size-8",
5164
+ className
5165
+ ),
5166
+ ...props,
5167
+ children: [
5168
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Icon, { className: "size-4 shrink-0", "aria-hidden": true }),
5169
+ children
5170
+ ]
5171
+ }
5172
+ );
5173
+ }
5174
+
5175
+ // src/ui/snippet.tsx
5176
+ var import_jsx_runtime60 = require("react/jsx-runtime");
5177
+ function Snippet({
5178
+ children,
5179
+ symbol,
5180
+ hideCopy = false,
5181
+ className,
5182
+ ...props
5183
+ }) {
5184
+ return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
5185
+ "div",
5186
+ {
5187
+ "data-slot": "snippet",
5188
+ className: cn(
5189
+ "flex items-center gap-2 rounded-lg border border-border bg-muted/40 py-1.5 pl-3 pr-1.5 font-mono text-sm",
5190
+ className
5191
+ ),
5192
+ ...props,
5193
+ children: [
5194
+ symbol ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { "aria-hidden": true, className: "select-none text-muted-foreground", children: symbol }) : null,
5195
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("code", { className: "min-w-0 flex-1 truncate text-foreground", children }),
5196
+ hideCopy ? null : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(CopyButton, { value: children, className: "size-7 shrink-0" })
5197
+ ]
5198
+ }
5199
+ );
5200
+ }
5201
+
5202
+ // src/ui/circular-progress.tsx
5203
+ var import_jsx_runtime61 = require("react/jsx-runtime");
5204
+ var toneClass = {
5205
+ primary: "text-primary",
5206
+ success: "text-emerald-500",
5207
+ warn: "text-amber-500",
5208
+ danger: "text-destructive"
5209
+ };
5210
+ function CircularProgress({
5211
+ value = 0,
5212
+ max = 100,
5213
+ size = 40,
5214
+ thickness = 4,
5215
+ showLabel = false,
5216
+ label,
5217
+ tone = "primary",
5218
+ className,
5219
+ ...props
5220
+ }) {
5221
+ const indeterminate = value === null || value === void 0;
5222
+ const radius = (size - thickness) / 2;
5223
+ const circumference = 2 * Math.PI * radius;
5224
+ const pct = indeterminate ? 0.25 : Math.min(Math.max(value / max, 0), 1);
5225
+ const dashOffset = circumference * (1 - pct);
5226
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
5227
+ "div",
5228
+ {
5229
+ "data-slot": "circular-progress",
5230
+ role: "progressbar",
5231
+ "aria-valuenow": indeterminate ? void 0 : Math.round(pct * 100),
5232
+ "aria-valuemin": 0,
5233
+ "aria-valuemax": 100,
5234
+ className: cn("relative inline-flex shrink-0", className),
5235
+ style: { width: size, height: size },
5236
+ ...props,
5237
+ children: [
5238
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
5239
+ "svg",
5240
+ {
5241
+ width: size,
5242
+ height: size,
5243
+ viewBox: `0 0 ${size} ${size}`,
5244
+ className: cn(toneClass[tone], indeterminate && "animate-spin"),
5245
+ children: [
5246
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
5247
+ "circle",
5248
+ {
5249
+ cx: size / 2,
5250
+ cy: size / 2,
5251
+ r: radius,
5252
+ fill: "none",
5253
+ strokeWidth: thickness,
5254
+ className: "stroke-current opacity-15"
5255
+ }
5256
+ ),
5257
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
5258
+ "circle",
5259
+ {
5260
+ cx: size / 2,
5261
+ cy: size / 2,
5262
+ r: radius,
5263
+ fill: "none",
5264
+ strokeWidth: thickness,
5265
+ strokeLinecap: "round",
5266
+ strokeDasharray: circumference,
5267
+ strokeDashoffset: dashOffset,
5268
+ transform: `rotate(-90 ${size / 2} ${size / 2})`,
5269
+ className: cn("stroke-current", !indeterminate && "transition-[stroke-dashoffset] duration-500")
5270
+ }
5271
+ )
5272
+ ]
5273
+ }
5274
+ ),
5275
+ showLabel && !indeterminate ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "absolute inset-0 flex items-center justify-center text-[0.7rem] font-medium tabular-nums text-foreground", children: label ?? `${Math.round(pct * 100)}%` }) : null
5276
+ ]
5277
+ }
5278
+ );
5279
+ }
4581
5280
  // Annotate the CommonJS export names for ESM import in node:
4582
5281
  0 && (module.exports = {
4583
5282
  AVATAR_PRIMARY_FALLBACK_CLASS,
@@ -4602,8 +5301,10 @@ var MemoPillSegmentedTabs = (0, import_react3.memo)(PillSegmentedTabs);
4602
5301
  AspectRatio,
4603
5302
  Avatar,
4604
5303
  AvatarFallback,
5304
+ AvatarGroup,
4605
5305
  AvatarImage,
4606
5306
  Badge,
5307
+ Banner,
4607
5308
  Breadcrumb,
4608
5309
  BreadcrumbEllipsis,
4609
5310
  BreadcrumbItem,
@@ -4628,6 +5329,7 @@ var MemoPillSegmentedTabs = (0, import_react3.memo)(PillSegmentedTabs);
4628
5329
  ChartTooltip,
4629
5330
  ChartTooltipContent,
4630
5331
  Checkbox,
5332
+ CircularProgress,
4631
5333
  Collapsible,
4632
5334
  CollapsibleContent,
4633
5335
  CollapsibleTrigger,
@@ -4666,6 +5368,7 @@ var MemoPillSegmentedTabs = (0, import_react3.memo)(PillSegmentedTabs);
4666
5368
  ContextMenuSubContent,
4667
5369
  ContextMenuSubTrigger,
4668
5370
  ContextMenuTrigger,
5371
+ CopyButton,
4669
5372
  DatePicker,
4670
5373
  DatePickerButton,
4671
5374
  DatePickerCalendar,
@@ -4741,6 +5444,7 @@ var MemoPillSegmentedTabs = (0, import_react3.memo)(PillSegmentedTabs);
4741
5444
  NavigationMenuList,
4742
5445
  NavigationMenuTrigger,
4743
5446
  NavigationMenuViewport,
5447
+ NumberField,
4744
5448
  Pagination,
4745
5449
  PaginationContent,
4746
5450
  PaginationEllipsis,
@@ -4756,6 +5460,7 @@ var MemoPillSegmentedTabs = (0, import_react3.memo)(PillSegmentedTabs);
4756
5460
  Progress,
4757
5461
  RadioGroup,
4758
5462
  RadioGroupItem,
5463
+ Rating,
4759
5464
  ScrollArea,
4760
5465
  ScrollBar,
4761
5466
  Select,
@@ -4780,7 +5485,9 @@ var MemoPillSegmentedTabs = (0, import_react3.memo)(PillSegmentedTabs);
4780
5485
  Shimmer,
4781
5486
  Skeleton,
4782
5487
  Slider,
5488
+ Snippet,
4783
5489
  Spinner,
5490
+ Stepper,
4784
5491
  Switch,
4785
5492
  Table,
4786
5493
  TableBody,
@@ -4790,8 +5497,10 @@ var MemoPillSegmentedTabs = (0, import_react3.memo)(PillSegmentedTabs);
4790
5497
  TableHead,
4791
5498
  TableHeader,
4792
5499
  TableRow,
5500
+ TagInput,
4793
5501
  Textarea,
4794
5502
  TimbalV2Button,
5503
+ Timeline,
4795
5504
  Toast,
4796
5505
  ToastAction,
4797
5506
  ToastClose,