@aircall/ds 0.6.0 → 0.9.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,8 +1,7 @@
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 * as Icons from "@aircall/react-icons";
5
- import { ArrowLeft, ArrowRight, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CircleAlertIcon, CircleCheck, Info, Loader2Icon, MinusIcon, MoreHorizontalIcon, PanelLeftIcon, Search, Smile, TriangleAlertIcon, XIcon } from "@aircall/react-icons";
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";
6
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
6
  import * as React$1 from "react";
8
7
  import React, { createContext, useContext, useMemo } from "react";
@@ -20,7 +19,6 @@ import { Collapsible as Collapsible$1 } from "@base-ui/react/collapsible";
20
19
  import { Combobox as Combobox$1 } from "@base-ui/react";
21
20
  import { Command as Command$1 } from "cmdk";
22
21
  import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
23
- import { getCountryISO3 } from "@aircall/numbers";
24
22
  import { flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
25
23
  import { Drawer as DrawerPrimitive } from "@base-ui/react/drawer";
26
24
  import { Radio } from "@base-ui/react/radio";
@@ -35,7 +33,6 @@ import { Progress as Progress$1 } from "@base-ui/react/progress";
35
33
  import { Select as Select$1 } from "@base-ui/react/select";
36
34
  import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
37
35
  import { Slider as Slider$1 } from "@base-ui/react/slider";
38
- import { Slot } from "@radix-ui/react-slot";
39
36
  import { Toaster as Toaster$1, toast } from "sonner";
40
37
  import { Switch as Switch$1 } from "@base-ui/react/switch";
41
38
  import { Tabs as Tabs$1 } from "@base-ui/react/tabs";
@@ -43,7 +40,62 @@ import { Toggle as Toggle$1 } from "@base-ui/react/toggle";
43
40
  import { ToggleGroup as ToggleGroup$1 } from "@base-ui/react/toggle-group";
44
41
 
45
42
  //#region src/lib/utils.ts
46
- const twMerge = extendTailwindMerge({ extend: { theme: { text: ["tiny"] } } });
43
+ const DS_COLORS = [
44
+ "background",
45
+ "foreground",
46
+ "card",
47
+ "card-foreground",
48
+ "popover",
49
+ "popover-foreground",
50
+ "primary",
51
+ "primary-foreground",
52
+ "secondary",
53
+ "secondary-foreground",
54
+ "info",
55
+ "info-foreground",
56
+ "info-background",
57
+ "success",
58
+ "success-foreground",
59
+ "success-background",
60
+ "warning",
61
+ "warning-foreground",
62
+ "warning-background",
63
+ "destructive",
64
+ "destructive-foreground",
65
+ "destructive-background",
66
+ "muted",
67
+ "muted-foreground",
68
+ "accent",
69
+ "accent-foreground",
70
+ "border",
71
+ "input",
72
+ "ring",
73
+ "chart-1",
74
+ "chart-2",
75
+ "chart-3",
76
+ "chart-4",
77
+ "chart-5",
78
+ "sidebar",
79
+ "sidebar-foreground",
80
+ "sidebar-primary",
81
+ "sidebar-primary-foreground",
82
+ "sidebar-accent",
83
+ "sidebar-accent-foreground",
84
+ "sidebar-border",
85
+ "sidebar-ring"
86
+ ];
87
+ const BLOCKS_COLORS = [
88
+ "page-background",
89
+ "navbar-foreground",
90
+ "navbar-accent",
91
+ "navbar-accent-foreground",
92
+ "navbar-icon-bg"
93
+ ];
94
+ const twMerge = extendTailwindMerge({ extend: { theme: {
95
+ color: [...DS_COLORS, ...BLOCKS_COLORS],
96
+ text: ["tiny"],
97
+ animate: ["caret-blink"]
98
+ } } });
47
99
  function cn(...inputs) {
48
100
  return twMerge(clsx(inputs));
49
101
  }
@@ -99,14 +151,14 @@ function AccordionContent({ className, children, ...props }) {
99
151
 
100
152
  //#endregion
101
153
  //#region src/components/button.tsx
102
- const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
154
+ const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px active:not-aria-[haspopup]:opacity-60 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
103
155
  variants: {
104
156
  variant: {
105
- default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
106
- outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
157
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
158
+ 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",
107
159
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
108
- ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
109
- destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
160
+ ghost: "hover:bg-muted hover:text-accent-foreground aria-expanded:bg-muted aria-expanded:text-accent-foreground dark:hover:bg-muted/50",
161
+ 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",
110
162
  link: "text-primary underline underline-offset-4"
111
163
  },
112
164
  size: {
@@ -322,7 +374,7 @@ function AvatarGroupCount({ className, ...props }) {
322
374
 
323
375
  //#endregion
324
376
  //#region src/components/counter-badge.tsx
325
- const counterBadgeVariants = cva("group/counter-badge inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full border border-transparent px-1 text-xs font-medium whitespace-nowrap transition-[color,box-shadow]", {
377
+ const counterBadgeVariants = cva("group/counter-badge inline-flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full border border-transparent px-1 text-xs font-medium leading-none whitespace-nowrap transition-[color,box-shadow]", {
326
378
  variants: { variant: {
327
379
  default: "bg-destructive text-primary-foreground hover:bg-destructive/80 focus-visible:border-ring focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-ring/50",
328
380
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 focus-visible:border-ring focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-ring/50",
@@ -430,6 +482,162 @@ function Banner({ className, variant, borderDirection, ...props }) {
430
482
  });
431
483
  }
432
484
 
485
+ //#endregion
486
+ //#region src/components/notification-queue.tsx
487
+ const PRIORITY_WEIGHT = {
488
+ error: 1e3,
489
+ warning: 100,
490
+ success: 50,
491
+ info: 10,
492
+ promotional: 1
493
+ };
494
+ const NotificationQueueContext = React$1.createContext(null);
495
+ function useNotificationQueueContext() {
496
+ const ctx = React$1.useContext(NotificationQueueContext);
497
+ if (!ctx) throw new Error("useNotification* hooks must be used inside <NotificationQueueProvider>");
498
+ return ctx;
499
+ }
500
+ function reducer(state, action) {
501
+ switch (action.type) {
502
+ case "REGISTER": {
503
+ const idx = state.entries.findIndex((e) => e.id === action.entry.id && e.slot === action.entry.slot);
504
+ if (idx >= 0) {
505
+ const updated = [...state.entries];
506
+ updated[idx] = {
507
+ ...action.entry,
508
+ registeredAt: state.entries[idx].registeredAt
509
+ };
510
+ const dismissed = new Set(state.dismissed);
511
+ dismissed.delete(`${action.entry.slot}::${action.entry.id}`);
512
+ return {
513
+ ...state,
514
+ entries: updated,
515
+ dismissed
516
+ };
517
+ }
518
+ return {
519
+ ...state,
520
+ entries: [...state.entries, action.entry]
521
+ };
522
+ }
523
+ case "UNREGISTER": {
524
+ const key = `${action.slot}::${action.id}`;
525
+ const dismissed = new Set(state.dismissed);
526
+ dismissed.delete(key);
527
+ return {
528
+ ...state,
529
+ entries: state.entries.filter((e) => !(e.id === action.id && e.slot === action.slot)),
530
+ dismissed
531
+ };
532
+ }
533
+ case "DISMISS": {
534
+ const key = `${action.slot}::${action.id}`;
535
+ if (state.dismissed.has(key)) return state;
536
+ return {
537
+ ...state,
538
+ dismissed: new Set([...state.dismissed, key])
539
+ };
540
+ }
541
+ }
542
+ }
543
+ function getSlotQueue(state, slot) {
544
+ return state.entries.filter((e) => e.slot === slot && !state.dismissed.has(`${slot}::${e.id}`)).sort((a, b) => {
545
+ const diff = PRIORITY_WEIGHT[b.priority] - PRIORITY_WEIGHT[a.priority];
546
+ return diff !== 0 ? diff : b.registeredAt - a.registeredAt;
547
+ });
548
+ }
549
+ function NotificationQueueProvider({ children }) {
550
+ const [state, dispatch] = React$1.useReducer(reducer, {
551
+ entries: [],
552
+ dismissed: /* @__PURE__ */ new Set()
553
+ });
554
+ const value = React$1.useMemo(() => ({
555
+ state,
556
+ dispatch
557
+ }), [state]);
558
+ return /* @__PURE__ */ jsx(NotificationQueueContext.Provider, {
559
+ value,
560
+ children
561
+ });
562
+ }
563
+ function useNotificationQueue() {
564
+ const { dispatch } = useNotificationQueueContext();
565
+ return {
566
+ push: React$1.useCallback(({ id, slot, priority, render }) => {
567
+ const dismiss = () => dispatch({
568
+ type: "DISMISS",
569
+ id,
570
+ slot
571
+ });
572
+ dispatch({
573
+ type: "REGISTER",
574
+ entry: {
575
+ id,
576
+ slot,
577
+ priority,
578
+ registeredAt: Date.now(),
579
+ contentRef: { current: render({ dismiss }) }
580
+ }
581
+ });
582
+ return { dismiss };
583
+ }, [dispatch]),
584
+ dismiss: React$1.useCallback((id, slot) => dispatch({
585
+ type: "DISMISS",
586
+ id,
587
+ slot
588
+ }), [dispatch])
589
+ };
590
+ }
591
+ function useNotification({ id, slot, priority, render }) {
592
+ const { dispatch } = useNotificationQueueContext();
593
+ const dismiss = React$1.useCallback(() => dispatch({
594
+ type: "DISMISS",
595
+ id,
596
+ slot
597
+ }), [
598
+ id,
599
+ slot,
600
+ dispatch
601
+ ]);
602
+ const renderRef = React$1.useRef(render);
603
+ renderRef.current = render;
604
+ const contentRef = React$1.useRef(null);
605
+ contentRef.current = renderRef.current({ dismiss });
606
+ React$1.useEffect(() => {
607
+ dispatch({
608
+ type: "REGISTER",
609
+ entry: {
610
+ id,
611
+ slot,
612
+ priority,
613
+ registeredAt: Date.now(),
614
+ contentRef
615
+ }
616
+ });
617
+ return () => dispatch({
618
+ type: "UNREGISTER",
619
+ id,
620
+ slot
621
+ });
622
+ }, [
623
+ id,
624
+ slot,
625
+ priority,
626
+ dispatch
627
+ ]);
628
+ return { dismiss };
629
+ }
630
+ function NotificationSlot({ slot, className }) {
631
+ const { state } = useNotificationQueueContext();
632
+ const active = getSlotQueue(state, slot)[0];
633
+ if (!active) return null;
634
+ return /* @__PURE__ */ jsx("div", {
635
+ "data-slot": "notification-slot",
636
+ className: cn(className),
637
+ children: active.contentRef.current
638
+ });
639
+ }
640
+
433
641
  //#endregion
434
642
  //#region src/components/separator.tsx
435
643
  function Separator({ className, orientation = "horizontal", ...props }) {
@@ -1212,30 +1420,18 @@ function CommandShortcut({ className, ...props }) {
1212
1420
 
1213
1421
  //#endregion
1214
1422
  //#region src/components/country-flag.tsx
1215
- const additionalCountryCodeMappings = {
1216
- BQ: "ABW",
1217
- BES: "ABW",
1218
- CW: "ABW",
1219
- SX: "ABW"
1220
- };
1221
1423
  const sizeMap = {
1222
- xs: "size-2",
1223
1424
  sm: "size-3",
1224
1425
  md: "size-4",
1225
- lg: "size-4",
1426
+ lg: "size-5",
1226
1427
  xl: "size-6"
1227
1428
  };
1228
1429
  const CountryFlag = ({ countryIsoCode, size = "xl", className, ...otherProps }) => {
1229
- const CountryIcons = Icons;
1230
- const properCountryCode = (countryIsoCode.length === 3 ? countryIsoCode : getCountryISO3(countryIsoCode)) || additionalCountryCodeMappings[countryIsoCode];
1231
- return /* @__PURE__ */ jsx(properCountryCode ? CountryIcons["Flag" + capitalizeFirstLetter(properCountryCode.toLowerCase())] : CountryIcons["FlagAbw"], {
1430
+ return /* @__PURE__ */ jsx(FLAGS[countryIsoCode.toLowerCase()] ?? FlagUs, {
1232
1431
  className: cn("flex-none", sizeMap[size], className),
1233
1432
  ...otherProps
1234
1433
  });
1235
1434
  };
1236
- function capitalizeFirstLetter(val) {
1237
- return String(val).charAt(0).toUpperCase() + String(val).slice(1);
1238
- }
1239
1435
 
1240
1436
  //#endregion
1241
1437
  //#region src/components/table.tsx
@@ -1666,7 +1862,7 @@ function DropdownMenuContent({ align = "start", alignOffset = 0, side = "bottom"
1666
1862
  sideOffset,
1667
1863
  children: /* @__PURE__ */ jsx(Menu.Popup, {
1668
1864
  "data-slot": "dropdown-menu-content",
1669
- className: cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none 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-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className),
1865
+ className: cn("z-50 max-h-(--available-height) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none 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-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className),
1670
1866
  ...props
1671
1867
  })
1672
1868
  })
@@ -1691,7 +1887,7 @@ function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
1691
1887
  "data-slot": "dropdown-menu-item",
1692
1888
  "data-inset": inset,
1693
1889
  "data-variant": variant,
1694
- className: cn("group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive", className),
1890
+ className: cn("group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive", className),
1695
1891
  ...props
1696
1892
  });
1697
1893
  }
@@ -1768,6 +1964,348 @@ function DropdownMenuShortcut({ className, ...props }) {
1768
1964
  ...props
1769
1965
  });
1770
1966
  }
1967
+ function DropdownMenuAddon({ className, ...props }) {
1968
+ return /* @__PURE__ */ jsx("span", {
1969
+ "data-slot": "dropdown-menu-addon",
1970
+ className: cn("ml-auto shrink-0 inline-flex items-center", className),
1971
+ ...props
1972
+ });
1973
+ }
1974
+
1975
+ //#endregion
1976
+ //#region src/components/empty.tsx
1977
+ function Empty({ className, ...props }) {
1978
+ return /* @__PURE__ */ jsx("div", {
1979
+ "data-slot": "empty",
1980
+ className: cn("flex min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-lg p-6 text-center text-balance md:p-12", className),
1981
+ ...props
1982
+ });
1983
+ }
1984
+ function EmptyHeader({ className, ...props }) {
1985
+ return /* @__PURE__ */ jsx("div", {
1986
+ "data-slot": "empty-header",
1987
+ className: cn("flex max-w-sm flex-col items-center gap-2 text-center", className),
1988
+ ...props
1989
+ });
1990
+ }
1991
+ const emptyMediaVariants = cva("flex shrink-0 items-center justify-center mb-2 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
1992
+ variants: { variant: {
1993
+ default: "bg-transparent",
1994
+ icon: "bg-muted text-foreground flex size-8 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-4"
1995
+ } },
1996
+ defaultVariants: { variant: "default" }
1997
+ });
1998
+ function EmptyMedia({ className, variant = "default", ...props }) {
1999
+ return /* @__PURE__ */ jsx("div", {
2000
+ "data-slot": "empty-icon",
2001
+ "data-variant": variant,
2002
+ className: cn(emptyMediaVariants({
2003
+ variant,
2004
+ className
2005
+ })),
2006
+ ...props
2007
+ });
2008
+ }
2009
+ function EmptyTitle({ className, ...props }) {
2010
+ return /* @__PURE__ */ jsx("div", {
2011
+ "data-slot": "empty-title",
2012
+ className: cn("text-sm font-medium tracking-tight", className),
2013
+ ...props
2014
+ });
2015
+ }
2016
+ function EmptyDescription({ className, ...props }) {
2017
+ return /* @__PURE__ */ jsx("div", {
2018
+ "data-slot": "empty-description",
2019
+ className: cn("text-muted-foreground [&>a:hover]:text-primary text-sm [&>a]:underline [&>a]:underline-offset-4", className),
2020
+ ...props
2021
+ });
2022
+ }
2023
+ function EmptyContent({ className, ...props }) {
2024
+ return /* @__PURE__ */ jsx("div", {
2025
+ "data-slot": "empty-content",
2026
+ className: cn("flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance", className),
2027
+ ...props
2028
+ });
2029
+ }
2030
+
2031
+ //#endregion
2032
+ //#region src/components/dropzone.tsx
2033
+ const DropzoneContext = React$1.createContext(null);
2034
+ function useDropzoneContext() {
2035
+ const ctx = React$1.useContext(DropzoneContext);
2036
+ if (!ctx) throw new Error("Dropzone subcomponents must be rendered inside <Dropzone>.");
2037
+ return ctx;
2038
+ }
2039
+ const DEFAULT_ERROR_MESSAGES = {
2040
+ "too-many-files": ({ maxFiles }) => maxFiles === 1 ? "Only one file can be uploaded" : `Only ${maxFiles ?? "a few"} files can be uploaded`,
2041
+ "file-too-large": ({ maxSize }) => maxSize ? `File is too large (max ${formatBytes(maxSize)})` : "File is too large",
2042
+ "file-too-small": ({ minSize }) => minSize ? `File is too small (min ${formatBytes(minSize)})` : "File is too small",
2043
+ "file-invalid-type": () => "This file type isn't allowed"
2044
+ };
2045
+ function formatBytes(bytes) {
2046
+ if (bytes < 1024) return `${bytes} B`;
2047
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
2048
+ return `${(bytes / 1024 / 1024).toFixed(0)} MB`;
2049
+ }
2050
+ function parseAccept(accept) {
2051
+ if (!accept) return [];
2052
+ return accept.split(",").map((p) => p.trim().toLowerCase()).filter(Boolean);
2053
+ }
2054
+ function matchesAccept(file, patterns) {
2055
+ if (patterns.length === 0) return true;
2056
+ const fileType = file.type.toLowerCase();
2057
+ const fileName = file.name.toLowerCase();
2058
+ return patterns.some((pattern) => {
2059
+ if (pattern.startsWith(".")) return fileName.endsWith(pattern);
2060
+ if (pattern.endsWith("/*")) return fileType.startsWith(pattern.slice(0, -1));
2061
+ return fileType === pattern;
2062
+ });
2063
+ }
2064
+ function matchesAcceptForDrag(type, patterns) {
2065
+ if (patterns.length === 0) return true;
2066
+ if (!type) return true;
2067
+ const mimePatterns = patterns.filter((p) => !p.startsWith("."));
2068
+ if (mimePatterns.length === 0) return true;
2069
+ const lowerType = type.toLowerCase();
2070
+ return mimePatterns.some((p) => p.endsWith("/*") ? lowerType.startsWith(p.slice(0, -1)) : lowerType === p);
2071
+ }
2072
+ function validateFiles(files, options) {
2073
+ const { acceptPatterns, maxFiles, maxSize, minSize } = options;
2074
+ if (files.length === 0) return [];
2075
+ if (maxFiles !== void 0 && files.length > maxFiles) {
2076
+ const message = DEFAULT_ERROR_MESSAGES["too-many-files"]({ maxFiles });
2077
+ return files.map((file) => ({
2078
+ file,
2079
+ errors: [{
2080
+ code: "too-many-files",
2081
+ message
2082
+ }]
2083
+ }));
2084
+ }
2085
+ const rejections = [];
2086
+ for (const file of files) {
2087
+ const errors = [];
2088
+ if (acceptPatterns.length > 0 && !matchesAccept(file, acceptPatterns)) errors.push({
2089
+ code: "file-invalid-type",
2090
+ message: DEFAULT_ERROR_MESSAGES["file-invalid-type"]({})
2091
+ });
2092
+ if (maxSize !== void 0 && file.size > maxSize) errors.push({
2093
+ code: "file-too-large",
2094
+ message: DEFAULT_ERROR_MESSAGES["file-too-large"]({ maxSize })
2095
+ });
2096
+ if (minSize !== void 0 && file.size < minSize) errors.push({
2097
+ code: "file-too-small",
2098
+ message: DEFAULT_ERROR_MESSAGES["file-too-small"]({ minSize })
2099
+ });
2100
+ if (errors.length > 0) rejections.push({
2101
+ file,
2102
+ errors
2103
+ });
2104
+ }
2105
+ return rejections;
2106
+ }
2107
+ /**
2108
+ * Headless drop-target — handles drag/drop state, validation, and the hover
2109
+ * overlay. Has no visual chrome; wrap content in `<DropzoneContent>` for the
2110
+ * standard dashed-border look, or compose around any other control (textarea,
2111
+ * etc.) for file-drop on an arbitrary surface.
2112
+ */
2113
+ const Dropzone = React$1.forwardRef(({ className, children, onFiles, onError, accept, multiple = false, maxFiles, maxSize, minSize, disabled = false, hoverMessage = "Drop your file here to upload", dragRejectMessage, onDragEnter, onDragLeave, onDragOver, ...props }, ref) => {
2114
+ const inputRef = React$1.useRef(null);
2115
+ const [isDragging, setIsDragging] = React$1.useState(false);
2116
+ const [isDragReject, setIsDragReject] = React$1.useState(false);
2117
+ const dragCounterRef = React$1.useRef(0);
2118
+ const acceptPatterns = React$1.useMemo(() => parseAccept(accept), [accept]);
2119
+ const open = React$1.useCallback(() => {
2120
+ if (disabled) return;
2121
+ inputRef.current?.click();
2122
+ }, [disabled]);
2123
+ const ctx = React$1.useMemo(() => ({
2124
+ inputRef,
2125
+ disabled,
2126
+ open
2127
+ }), [disabled, open]);
2128
+ function processFiles(files) {
2129
+ if (files.length === 0) return;
2130
+ const rejections = validateFiles(files, {
2131
+ acceptPatterns,
2132
+ maxFiles: multiple ? maxFiles : 1,
2133
+ maxSize,
2134
+ minSize
2135
+ });
2136
+ const rejectedSet = new Set(rejections.map((r) => r.file));
2137
+ const accepted = files.filter((f) => !rejectedSet.has(f));
2138
+ if (accepted.length > 0) onFiles?.(accepted);
2139
+ if (rejections.length > 0) onError?.(rejections);
2140
+ }
2141
+ function resetDragState() {
2142
+ dragCounterRef.current = 0;
2143
+ setIsDragging(false);
2144
+ setIsDragReject(false);
2145
+ }
2146
+ function computeIsDragReject(items) {
2147
+ if (dragRejectMessage == null || acceptPatterns.length === 0) return false;
2148
+ return Array.from(items).some((item) => item.kind === "file" && !matchesAcceptForDrag(item.type, acceptPatterns));
2149
+ }
2150
+ function handleDragEnter(event) {
2151
+ event.preventDefault();
2152
+ if (disabled) return;
2153
+ dragCounterRef.current += 1;
2154
+ if (dragCounterRef.current === 1) setIsDragging(true);
2155
+ setIsDragReject(computeIsDragReject(event.dataTransfer.items));
2156
+ onDragEnter?.(event);
2157
+ }
2158
+ function handleDragLeave(event) {
2159
+ event.preventDefault();
2160
+ if (disabled) return;
2161
+ dragCounterRef.current = Math.max(0, dragCounterRef.current - 1);
2162
+ if (dragCounterRef.current === 0) {
2163
+ setIsDragging(false);
2164
+ setIsDragReject(false);
2165
+ }
2166
+ onDragLeave?.(event);
2167
+ }
2168
+ function handleDragOver(event) {
2169
+ event.preventDefault();
2170
+ if (!disabled) event.dataTransfer.dropEffect = "copy";
2171
+ onDragOver?.(event);
2172
+ }
2173
+ function handleDrop(event) {
2174
+ event.preventDefault();
2175
+ resetDragState();
2176
+ if (disabled) return;
2177
+ processFiles(Array.from(event.dataTransfer.files));
2178
+ }
2179
+ const overlayState = !isDragging ? "idle" : isDragReject ? "reject" : "dragging";
2180
+ const overlayMessage = isDragReject ? dragRejectMessage : hoverMessage;
2181
+ return /* @__PURE__ */ jsx(DropzoneContext.Provider, {
2182
+ value: ctx,
2183
+ children: /* @__PURE__ */ jsxs("div", {
2184
+ ref,
2185
+ "data-slot": "dropzone",
2186
+ "data-state": overlayState,
2187
+ "data-disabled": disabled || void 0,
2188
+ className: cn("group/dropzone relative", className),
2189
+ onDragEnter: handleDragEnter,
2190
+ onDragLeave: handleDragLeave,
2191
+ onDragOver: handleDragOver,
2192
+ onDrop: handleDrop,
2193
+ ...props,
2194
+ children: [
2195
+ /* @__PURE__ */ jsx("input", {
2196
+ ref: inputRef,
2197
+ type: "file",
2198
+ accept,
2199
+ multiple,
2200
+ disabled,
2201
+ className: "sr-only",
2202
+ tabIndex: -1,
2203
+ onClick: (event) => event.stopPropagation(),
2204
+ onChange: (event) => {
2205
+ processFiles(Array.from(event.target.files ?? []));
2206
+ event.target.value = "";
2207
+ }
2208
+ }),
2209
+ children,
2210
+ /* @__PURE__ */ jsxs("div", {
2211
+ "aria-hidden": true,
2212
+ className: cn("pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed border-ring bg-accent text-sm font-medium text-foreground backdrop-blur-xl transition-opacity", "opacity-0 group-data-[state=dragging]/dropzone:opacity-100", "group-data-[state=reject]/dropzone:opacity-100 group-data-[state=reject]/dropzone:border-destructive group-data-[state=reject]/dropzone:bg-destructive-background group-data-[state=reject]/dropzone:text-destructive"),
2213
+ children: [
2214
+ /* @__PURE__ */ jsx(ArrowDownToLine, { className: "size-5 group-data-[state=reject]/dropzone:hidden" }),
2215
+ /* @__PURE__ */ jsx(CircleAlert, { className: "hidden size-5 group-data-[state=reject]/dropzone:block" }),
2216
+ overlayMessage != null && /* @__PURE__ */ jsx("p", { children: overlayMessage })
2217
+ ]
2218
+ })
2219
+ ]
2220
+ })
2221
+ });
2222
+ });
2223
+ Dropzone.displayName = "Dropzone";
2224
+ /**
2225
+ * Visual file-picker card — dashed border, centered layout, and click /
2226
+ * Space / Enter open the picker. Omit it (and use `<DropzoneTrigger>`)
2227
+ * when you want file-drop on an existing surface instead.
2228
+ */
2229
+ const DropzoneContent = React$1.forwardRef(({ className, error = false, onClick, onKeyDown, ...props }, ref) => {
2230
+ const { open, disabled } = useDropzoneContext();
2231
+ return /* @__PURE__ */ jsx("div", {
2232
+ ref,
2233
+ role: "button",
2234
+ tabIndex: disabled ? -1 : 0,
2235
+ "aria-disabled": disabled || void 0,
2236
+ "aria-invalid": error || void 0,
2237
+ "data-slot": "dropzone-content",
2238
+ "data-disabled": disabled || void 0,
2239
+ "data-error": error || void 0,
2240
+ className: cn("flex min-w-0 flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-input bg-background p-6 text-center text-balance transition-colors", "outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50", "data-error:border-destructive", "data-disabled:cursor-not-allowed data-disabled:opacity-60 data-disabled:bg-muted", !disabled && "cursor-pointer hover:bg-muted/40", className),
2241
+ onClick: (event) => {
2242
+ if (!disabled) open();
2243
+ onClick?.(event);
2244
+ },
2245
+ onKeyDown: (event) => {
2246
+ if (!disabled && (event.key === "Enter" || event.key === " ")) {
2247
+ event.preventDefault();
2248
+ open();
2249
+ }
2250
+ onKeyDown?.(event);
2251
+ },
2252
+ ...props
2253
+ });
2254
+ });
2255
+ DropzoneContent.displayName = "DropzoneContent";
2256
+ /** `<EmptyMedia>` with `variant="icon"` baked in. */
2257
+ function DropzoneIcon({ variant = "icon", ...props }) {
2258
+ return /* @__PURE__ */ jsx(EmptyMedia, {
2259
+ "data-slot": "dropzone-icon",
2260
+ variant,
2261
+ ...props
2262
+ });
2263
+ }
2264
+ function DropzoneTitle({ className, ...props }) {
2265
+ return /* @__PURE__ */ jsx(EmptyTitle, {
2266
+ "data-slot": "dropzone-title",
2267
+ className: cn("text-foreground", className),
2268
+ ...props
2269
+ });
2270
+ }
2271
+ function DropzoneDescription(props) {
2272
+ return /* @__PURE__ */ jsx(EmptyDescription, {
2273
+ "data-slot": "dropzone-description",
2274
+ ...props
2275
+ });
2276
+ }
2277
+ function DropzoneActions({ className, ...props }) {
2278
+ return /* @__PURE__ */ jsx("div", {
2279
+ "data-slot": "dropzone-actions",
2280
+ className: cn("mt-2 flex items-center gap-2", className),
2281
+ onClick: (event) => event.stopPropagation(),
2282
+ onKeyDown: (event) => event.stopPropagation(),
2283
+ ...props
2284
+ });
2285
+ }
2286
+ /**
2287
+ * Opens the file picker on click. Default-renders a DS `<Button>`; pass
2288
+ * `render` (Base UI convention) to substitute another element.
2289
+ */
2290
+ const DropzoneTrigger = React$1.forwardRef(({ render, onClick, ...props }, ref) => {
2291
+ const { open, disabled } = useDropzoneContext();
2292
+ return useRender({
2293
+ defaultTagName: "button",
2294
+ props: mergeProps({
2295
+ ref,
2296
+ type: "button",
2297
+ "data-slot": "dropzone-trigger",
2298
+ disabled,
2299
+ onClick: (event) => {
2300
+ event.stopPropagation();
2301
+ open();
2302
+ onClick?.(event);
2303
+ }
2304
+ }, props),
2305
+ render: render ?? /* @__PURE__ */ jsx(Button, {})
2306
+ });
2307
+ });
2308
+ DropzoneTrigger.displayName = "DropzoneTrigger";
1771
2309
 
1772
2310
  //#endregion
1773
2311
  //#region src/components/popover.tsx
@@ -1908,60 +2446,36 @@ function EmojiPickerContent({ className, align = "start", searchPlaceholder, cat
1908
2446
  }
1909
2447
 
1910
2448
  //#endregion
1911
- //#region src/components/empty.tsx
1912
- function Empty({ className, ...props }) {
1913
- return /* @__PURE__ */ jsx("div", {
1914
- "data-slot": "empty",
1915
- className: cn("flex min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-lg border-dashed p-6 text-center text-balance md:p-12", className),
1916
- ...props
1917
- });
1918
- }
1919
- function EmptyHeader({ className, ...props }) {
1920
- return /* @__PURE__ */ jsx("div", {
1921
- "data-slot": "empty-header",
1922
- className: cn("flex max-w-sm flex-col items-center gap-2 text-center", className),
1923
- ...props
1924
- });
1925
- }
1926
- const emptyMediaVariants = cva("flex shrink-0 items-center justify-center mb-2 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
1927
- variants: { variant: {
1928
- default: "bg-transparent",
1929
- icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6"
1930
- } },
1931
- defaultVariants: { variant: "default" }
2449
+ //#region src/components/fab.tsx
2450
+ const fabVariants = cva("group/fab inline-flex shrink-0 items-center justify-center rounded-md shadow-lg font-semibold whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px active:not-aria-[haspopup]:shadow-md [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-6", {
2451
+ variants: {
2452
+ variant: {
2453
+ default: "bg-primary text-primary-foreground hover:bg-primary/90 aria-expanded:bg-primary/90",
2454
+ outline: "border border-input bg-background hover:bg-muted hover:text-accent-foreground focus-visible:border-ring aria-expanded:bg-muted active:bg-muted dark:border-input dark:bg-input/30 dark:hover:bg-input/50 dark:active:bg-input/50"
2455
+ },
2456
+ size: {
2457
+ lg: "h-10 gap-2 px-4 text-base",
2458
+ "icon-lg": "size-10"
2459
+ }
2460
+ },
2461
+ defaultVariants: {
2462
+ variant: "default",
2463
+ size: "icon-lg"
2464
+ }
1932
2465
  });
1933
- function EmptyMedia({ className, variant = "default", ...props }) {
1934
- return /* @__PURE__ */ jsx("div", {
1935
- "data-slot": "empty-icon",
1936
- "data-variant": variant,
1937
- className: cn(emptyMediaVariants({
2466
+ const FAB = React$1.forwardRef(({ className, variant, size, ...props }, ref) => {
2467
+ return /* @__PURE__ */ jsx(Button$1, {
2468
+ ref,
2469
+ "data-slot": "fab",
2470
+ className: cn(fabVariants({
1938
2471
  variant,
2472
+ size,
1939
2473
  className
1940
2474
  })),
1941
2475
  ...props
1942
2476
  });
1943
- }
1944
- function EmptyTitle({ className, ...props }) {
1945
- return /* @__PURE__ */ jsx("div", {
1946
- "data-slot": "empty-title",
1947
- className: cn("text-sm font-medium tracking-tight", className),
1948
- ...props
1949
- });
1950
- }
1951
- function EmptyDescription({ className, ...props }) {
1952
- return /* @__PURE__ */ jsx("div", {
1953
- "data-slot": "empty-description",
1954
- className: cn("text-muted-foreground [&>a:hover]:text-primary text-sm [&>a]:underline [&>a]:underline-offset-4", className),
1955
- ...props
1956
- });
1957
- }
1958
- function EmptyContent({ className, ...props }) {
1959
- return /* @__PURE__ */ jsx("div", {
1960
- "data-slot": "empty-content",
1961
- className: cn("flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance", className),
1962
- ...props
1963
- });
1964
- }
2477
+ });
2478
+ FAB.displayName = "FAB";
1965
2479
 
1966
2480
  //#endregion
1967
2481
  //#region src/components/label.tsx
@@ -2076,6 +2590,43 @@ function FieldError({ className, children, errors, ...props }) {
2076
2590
  });
2077
2591
  }
2078
2592
 
2593
+ //#endregion
2594
+ //#region src/components/gauge.tsx
2595
+ const GAUGES = 8;
2596
+ function getSegmentState(index, value) {
2597
+ if (index >= value) return "empty";
2598
+ if (value === GAUGES) return "filled";
2599
+ const distanceFromHead = value - 1 - index;
2600
+ if (distanceFromHead === 0) return "head";
2601
+ if (distanceFromHead === 1) return "trailing";
2602
+ return "filled";
2603
+ }
2604
+ function Gauge({ className, value, ...props }) {
2605
+ const clampedValue = Math.max(0, Math.min(value, GAUGES));
2606
+ return /* @__PURE__ */ jsx("div", {
2607
+ role: "meter",
2608
+ "aria-valuenow": clampedValue,
2609
+ "aria-valuemin": 0,
2610
+ "aria-valuemax": GAUGES,
2611
+ "data-slot": "gauge",
2612
+ className: cn("flex w-full items-center gap-3", className),
2613
+ ...props,
2614
+ children: Array.from({ length: GAUGES }, (_, index) => {
2615
+ const state = getSegmentState(index, clampedValue);
2616
+ return /* @__PURE__ */ jsx("div", {
2617
+ "data-slot": "gauge-item",
2618
+ "data-state": state,
2619
+ className: "relative h-3 flex-1 overflow-hidden rounded-full bg-muted",
2620
+ children: /* @__PURE__ */ jsx("div", {
2621
+ "data-slot": "gauge-indicator",
2622
+ className: cn("absolute inset-0 bg-primary transition-opacity", "data-[state=empty]:opacity-0", "data-[state=head]:opacity-35", "data-[state=trailing]:opacity-65", "data-[state=filled]:opacity-100"),
2623
+ "data-state": state
2624
+ })
2625
+ }, index);
2626
+ })
2627
+ });
2628
+ }
2629
+
2079
2630
  //#endregion
2080
2631
  //#region src/components/input-otp.tsx
2081
2632
  function InputOTP({ className, containerClassName, ...props }) {
@@ -2456,7 +3007,7 @@ function SelectContent({ className, children, side = "bottom", sideOffset = 4, a
2456
3007
  children: /* @__PURE__ */ jsxs(Select$1.Popup, {
2457
3008
  "data-slot": "select-content",
2458
3009
  "data-align-trigger": alignItemWithTrigger,
2459
- className: cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md p-1 bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none 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-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),
3010
+ className: cn("relative isolate z-50 max-h-(--available-height) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md p-1 bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none 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-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),
2460
3011
  ...props,
2461
3012
  children: [
2462
3013
  /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
@@ -3198,27 +3749,6 @@ function TabsContent({ className, ...props }) {
3198
3749
  });
3199
3750
  }
3200
3751
 
3201
- //#endregion
3202
- //#region src/components/tab-bar.tsx
3203
- function TabBar({ tabs, activeTab, onTabChange, renderTab, className }) {
3204
- return /* @__PURE__ */ jsx(Tabs, {
3205
- value: activeTab,
3206
- onValueChange: onTabChange,
3207
- className: `w-full ${className || ""}`,
3208
- children: /* @__PURE__ */ jsx(TabsList, {
3209
- className: "w-full h-auto flex border-t border-border bg-card p-0 rounded-none shadow-none",
3210
- children: tabs.map((tab) => /* @__PURE__ */ jsx(TabsTrigger, {
3211
- value: tab.value,
3212
- className: ["flex flex-col items-center flex-1 py-2 px-0 transition-colors border-b-2 border-b-transparent rounded-none bg-transparent data-active:shadow-none! dark:data-active:bg-transparent! dark:data-active:border-none!", activeTab === tab.value ? "text-foreground font-semibold border-b-primary" : "text-muted-foreground font-normal"].join(" "),
3213
- children: renderTab ? renderTab(tab, activeTab === tab.value) : /* @__PURE__ */ jsxs(Fragment, { children: [tab.renderIcon?.(), /* @__PURE__ */ jsx("span", {
3214
- className: "text-xs",
3215
- children: tab.label
3216
- })] })
3217
- }, tab.value))
3218
- })
3219
- });
3220
- }
3221
-
3222
3752
  //#endregion
3223
3753
  //#region src/components/toggle.tsx
3224
3754
  const toggleVariants = cva("group/toggle inline-flex items-center justify-center gap-1 rounded-md bg-transparent text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 data-disabled:pointer-events-none data-disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
@@ -3299,4 +3829,78 @@ function ToggleGroupItem({ className, children, variant = "default", size = "def
3299
3829
  }
3300
3830
 
3301
3831
  //#endregion
3302
- 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, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmojiPicker, EmojiPickerCategories, EmojiPickerContent, EmojiPickerTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, 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, 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, Slot, Spinner, Switch, TabBar, 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, selectTriggerVariants, spinnerVariants, toast, toggleVariants, useComboboxAnchor, useSidebar, useTheme };
3832
+ //#region src/hooks/use-audio-gauge.ts
3833
+ /**
3834
+ * Drives a `<Gauge>` from the system microphone. Opens the default audio
3835
+ * input via `getUserMedia` and runs a smoothed frequency-domain analyser.
3836
+ *
3837
+ * The returned `value` is calibrated for typical speech: a normal speaking
3838
+ * voice fills several segments, and the meter saturates at ~50% of the
3839
+ * digital signal range (matches the existing tractor-based audio meters
3840
+ * shipped in `aw-web` and `phone`).
3841
+ *
3842
+ * @example
3843
+ * const { value, error } = useAudioGauge()
3844
+ * return (
3845
+ * <>
3846
+ * <Gauge value={value} />
3847
+ * {error && <p>Microphone unavailable: {error.message}</p>}
3848
+ * </>
3849
+ * )
3850
+ */
3851
+ function useAudioGauge(options = {}) {
3852
+ const { intervalMs = 16, fftSize = 32, smoothing = .3, gain = 2 } = options;
3853
+ const [value, setValue] = React$1.useState(0);
3854
+ const [error, setError] = React$1.useState(null);
3855
+ const gainRef = React$1.useRef(gain);
3856
+ gainRef.current = gain;
3857
+ React$1.useEffect(() => {
3858
+ let cancelled = false;
3859
+ let stream;
3860
+ let ctx;
3861
+ let intervalId;
3862
+ setError(null);
3863
+ navigator.mediaDevices.getUserMedia({ audio: true }).then((s) => {
3864
+ if (cancelled) {
3865
+ s.getTracks().forEach((track) => track.stop());
3866
+ return;
3867
+ }
3868
+ stream = s;
3869
+ ctx = new AudioContext();
3870
+ const source = ctx.createMediaStreamSource(s);
3871
+ const analyser = ctx.createAnalyser();
3872
+ analyser.fftSize = fftSize;
3873
+ analyser.smoothingTimeConstant = smoothing;
3874
+ source.connect(analyser);
3875
+ const buffer = new Uint8Array(analyser.frequencyBinCount);
3876
+ ctx.resume().catch(() => {});
3877
+ intervalId = setInterval(() => {
3878
+ analyser.getByteFrequencyData(buffer);
3879
+ let sum = 0;
3880
+ for (const v of buffer) sum += v;
3881
+ const next = Math.round(sum / buffer.length / 255 * 8 * gainRef.current);
3882
+ setValue((prev) => prev === next ? prev : next);
3883
+ }, intervalMs);
3884
+ }).catch((err) => {
3885
+ if (!cancelled) setError(err);
3886
+ });
3887
+ return () => {
3888
+ cancelled = true;
3889
+ if (intervalId !== void 0) clearInterval(intervalId);
3890
+ stream?.getTracks().forEach((track) => track.stop());
3891
+ ctx?.close().catch(() => {});
3892
+ setValue(0);
3893
+ };
3894
+ }, [
3895
+ intervalMs,
3896
+ fftSize,
3897
+ smoothing
3898
+ ]);
3899
+ return {
3900
+ value,
3901
+ error
3902
+ };
3903
+ }
3904
+
3905
+ //#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 };