@aircall/ds 0.6.0 → 0.9.1
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/globals.css +1 -1
- package/dist/index.d.ts +486 -296
- package/dist/index.js +702 -98
- package/package.json +1 -1
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
|
|
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
|
|
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:
|
|
154
|
+
const buttonVariants = cva("group/button inline-flex shrink-0 cursor-pointer 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
|
|
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:
|
|
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-
|
|
1426
|
+
lg: "size-5",
|
|
1226
1427
|
xl: "size-6"
|
|
1227
1428
|
};
|
|
1228
1429
|
const CountryFlag = ({ countryIsoCode, size = "xl", className, ...otherProps }) => {
|
|
1229
|
-
|
|
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)
|
|
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
|
|
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/
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
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
|
-
|
|
1934
|
-
return /* @__PURE__ */ jsx(
|
|
1935
|
-
|
|
1936
|
-
"data-
|
|
1937
|
-
className: cn(
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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 };
|