@bmsuisse/ui 0.16.1 → 0.17.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.d.ts +115 -8
- package/dist/index.js +668 -342
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -167,8 +167,8 @@ var Badge = ({ className, variant, ...props }) => /* @__PURE__ */ jsx6("div", {
|
|
|
167
167
|
|
|
168
168
|
// src/primitives/dialog.tsx
|
|
169
169
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
170
|
-
import {
|
|
171
|
-
import { forwardRef as forwardRef5 } from "react";
|
|
170
|
+
import { XMarkIcon } from "@heroicons/react/24/outline";
|
|
171
|
+
import { forwardRef as forwardRef5, useCallback, useLayoutEffect, useRef } from "react";
|
|
172
172
|
import { jsx as jsx7, jsxs } from "react/jsx-runtime";
|
|
173
173
|
var Dialog = DialogPrimitive.Root;
|
|
174
174
|
var DialogTrigger = DialogPrimitive.Trigger;
|
|
@@ -188,7 +188,7 @@ var DialogContent = forwardRef5(({ className, children, showCloseButton = true,
|
|
|
188
188
|
{
|
|
189
189
|
ref,
|
|
190
190
|
className: cn(
|
|
191
|
-
"fixed left-1/2 top-1/2 z-50 flex max-h-[85vh] w-full max-w-md -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-lg border bg-background shadow-lg",
|
|
191
|
+
"fixed left-1/2 top-1/2 z-50 flex max-h-[85vh] [--dialog-divider:color-mix(in_oklab,var(--color-foreground)_12%,transparent)] dark:[--dialog-divider:var(--color-border)] w-full max-w-md -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-lg border bg-background shadow-lg",
|
|
192
192
|
className
|
|
193
193
|
),
|
|
194
194
|
...props,
|
|
@@ -200,7 +200,7 @@ var DialogContent = forwardRef5(({ className, children, showCloseButton = true,
|
|
|
200
200
|
"data-testid": closeButtonTestId,
|
|
201
201
|
className: "absolute right-4 top-4 z-20 rounded-sm opacity-70 hover:opacity-100",
|
|
202
202
|
children: [
|
|
203
|
-
/* @__PURE__ */ jsx7(
|
|
203
|
+
/* @__PURE__ */ jsx7(XMarkIcon, { className: "h-4 w-4" }),
|
|
204
204
|
/* @__PURE__ */ jsx7("span", { className: "sr-only", children: "Close" })
|
|
205
205
|
]
|
|
206
206
|
}
|
|
@@ -213,7 +213,17 @@ DialogContent.displayName = "DialogContent";
|
|
|
213
213
|
var DialogHeader = ({
|
|
214
214
|
className,
|
|
215
215
|
...props
|
|
216
|
-
}) => /* @__PURE__ */ jsx7(
|
|
216
|
+
}) => /* @__PURE__ */ jsx7(
|
|
217
|
+
"div",
|
|
218
|
+
{
|
|
219
|
+
"data-slot": "dialog-header",
|
|
220
|
+
className: cn(
|
|
221
|
+
"flex shrink-0 flex-col gap-1.5 bg-background px-6 pb-3 pt-6 transition-shadow [&:has(+[data-overflow-top])]:shadow-[inset_0_-1px_0_var(--dialog-divider)]",
|
|
222
|
+
className
|
|
223
|
+
),
|
|
224
|
+
...props
|
|
225
|
+
}
|
|
226
|
+
);
|
|
217
227
|
var DialogTitle = forwardRef5(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
|
|
218
228
|
DialogTitle.displayName = "DialogTitle";
|
|
219
229
|
var DialogDescription = forwardRef5(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
@@ -225,17 +235,77 @@ var DialogDescription = forwardRef5(({ className, ...props }, ref) => /* @__PURE
|
|
|
225
235
|
}
|
|
226
236
|
));
|
|
227
237
|
DialogDescription.displayName = "DialogDescription";
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
238
|
+
function useScrollEdges(ref) {
|
|
239
|
+
useLayoutEffect(() => {
|
|
240
|
+
const el = ref.current;
|
|
241
|
+
if (!el) return void 0;
|
|
242
|
+
const update = () => {
|
|
243
|
+
el.toggleAttribute("data-overflow-top", el.scrollTop > 0.5);
|
|
244
|
+
el.toggleAttribute(
|
|
245
|
+
"data-overflow-bottom",
|
|
246
|
+
el.scrollTop + el.clientHeight < el.scrollHeight - 0.5
|
|
247
|
+
);
|
|
248
|
+
};
|
|
249
|
+
update();
|
|
250
|
+
el.addEventListener("scroll", update, { passive: true });
|
|
251
|
+
const resizeObserver = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(update);
|
|
252
|
+
const observeChildren = () => {
|
|
253
|
+
if (!resizeObserver) return;
|
|
254
|
+
resizeObserver.disconnect();
|
|
255
|
+
resizeObserver.observe(el);
|
|
256
|
+
for (const child of Array.from(el.children)) resizeObserver.observe(child);
|
|
257
|
+
};
|
|
258
|
+
observeChildren();
|
|
259
|
+
const mutationObserver = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(() => {
|
|
260
|
+
observeChildren();
|
|
261
|
+
update();
|
|
262
|
+
});
|
|
263
|
+
mutationObserver?.observe(el, { childList: true });
|
|
264
|
+
return () => {
|
|
265
|
+
el.removeEventListener("scroll", update);
|
|
266
|
+
resizeObserver?.disconnect();
|
|
267
|
+
mutationObserver?.disconnect();
|
|
268
|
+
};
|
|
269
|
+
}, [ref]);
|
|
270
|
+
}
|
|
271
|
+
var DialogBody = forwardRef5(
|
|
272
|
+
({ className, ...props }, forwardedRef) => {
|
|
273
|
+
const ref = useRef(null);
|
|
274
|
+
useScrollEdges(ref);
|
|
275
|
+
const setRefs = useCallback(
|
|
276
|
+
(node) => {
|
|
277
|
+
ref.current = node;
|
|
278
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
279
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
280
|
+
},
|
|
281
|
+
[forwardedRef]
|
|
282
|
+
);
|
|
283
|
+
return /* @__PURE__ */ jsx7(
|
|
284
|
+
"div",
|
|
285
|
+
{
|
|
286
|
+
ref: setRefs,
|
|
287
|
+
"data-slot": "dialog-body",
|
|
288
|
+
className: cn(
|
|
289
|
+
"min-h-0 flex-1 overflow-y-auto px-6 pb-6 pt-1 empty:pb-3 empty:pt-0 [&:empty:has(+[data-slot=dialog-footer])]:hidden [&:has(+[data-slot=dialog-footer])]:pb-1",
|
|
290
|
+
className
|
|
291
|
+
),
|
|
292
|
+
...props
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
DialogBody.displayName = "DialogBody";
|
|
232
298
|
var DialogFooter = ({
|
|
233
299
|
className,
|
|
234
300
|
...props
|
|
235
301
|
}) => /* @__PURE__ */ jsx7(
|
|
236
302
|
"div",
|
|
237
303
|
{
|
|
238
|
-
|
|
304
|
+
"data-slot": "dialog-footer",
|
|
305
|
+
className: cn(
|
|
306
|
+
"flex shrink-0 justify-end gap-2 bg-background px-6 pb-6 pt-3 transition-shadow [[data-overflow-bottom]+&]:shadow-[inset_0_1px_0_var(--dialog-divider)] [[data-slot=dialog-body]:empty+&]:pt-1 [[data-slot=dialog-header]+&]:pt-1",
|
|
307
|
+
className
|
|
308
|
+
),
|
|
239
309
|
...props
|
|
240
310
|
}
|
|
241
311
|
);
|
|
@@ -264,7 +334,7 @@ PopoverContent.displayName = "PopoverContent";
|
|
|
264
334
|
|
|
265
335
|
// src/primitives/select.tsx
|
|
266
336
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
267
|
-
import {
|
|
337
|
+
import { CheckIcon, ChevronDownIcon } from "@heroicons/react/24/outline";
|
|
268
338
|
import { forwardRef as forwardRef7 } from "react";
|
|
269
339
|
import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
270
340
|
var Select = SelectPrimitive.Root;
|
|
@@ -283,7 +353,7 @@ var SelectTrigger = forwardRef7(
|
|
|
283
353
|
...props,
|
|
284
354
|
children: [
|
|
285
355
|
children,
|
|
286
|
-
/* @__PURE__ */ jsx9(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx9(
|
|
356
|
+
/* @__PURE__ */ jsx9(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx9(ChevronDownIcon, { className: "h-4 w-4 opacity-50" }) })
|
|
287
357
|
]
|
|
288
358
|
}
|
|
289
359
|
)
|
|
@@ -314,7 +384,7 @@ var SelectItem = forwardRef7(({ className, children, ...props }, ref) => /* @__P
|
|
|
314
384
|
),
|
|
315
385
|
...props,
|
|
316
386
|
children: [
|
|
317
|
-
/* @__PURE__ */ jsx9("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx9(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx9(
|
|
387
|
+
/* @__PURE__ */ jsx9("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx9(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx9(CheckIcon, { className: "h-4 w-4" }) }) }),
|
|
318
388
|
/* @__PURE__ */ jsx9(SelectPrimitive.ItemText, { children })
|
|
319
389
|
]
|
|
320
390
|
}
|
|
@@ -327,7 +397,7 @@ var Skeleton = ({ className, ...props }) => /* @__PURE__ */ jsx10("div", { class
|
|
|
327
397
|
|
|
328
398
|
// src/primitives/checkbox.tsx
|
|
329
399
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
330
|
-
import {
|
|
400
|
+
import { CheckIcon as CheckIcon2 } from "@heroicons/react/24/outline";
|
|
331
401
|
import { forwardRef as forwardRef8 } from "react";
|
|
332
402
|
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
333
403
|
var Checkbox = forwardRef8(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
@@ -339,7 +409,7 @@ var Checkbox = forwardRef8(({ className, ...props }, ref) => /* @__PURE__ */ jsx
|
|
|
339
409
|
className
|
|
340
410
|
),
|
|
341
411
|
...props,
|
|
342
|
-
children: /* @__PURE__ */ jsx11(CheckboxPrimitive.Indicator, { className: "flex items-center justify-center text-current", children: /* @__PURE__ */ jsx11(
|
|
412
|
+
children: /* @__PURE__ */ jsx11(CheckboxPrimitive.Indicator, { className: "flex items-center justify-center text-current", children: /* @__PURE__ */ jsx11(CheckIcon2, { className: "h-3.5 w-3.5" }) })
|
|
343
413
|
}
|
|
344
414
|
));
|
|
345
415
|
Checkbox.displayName = "Checkbox";
|
|
@@ -521,7 +591,7 @@ TableCaption.displayName = "TableCaption";
|
|
|
521
591
|
|
|
522
592
|
// src/primitives/dropdown-menu.tsx
|
|
523
593
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
524
|
-
import {
|
|
594
|
+
import { CheckIcon as CheckIcon3, ChevronRightIcon } from "@heroicons/react/24/outline";
|
|
525
595
|
import { forwardRef as forwardRef14 } from "react";
|
|
526
596
|
import { jsx as jsx17, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
527
597
|
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
@@ -567,7 +637,7 @@ var DropdownMenuCheckboxItem = forwardRef14(({ className, children, checked, ...
|
|
|
567
637
|
),
|
|
568
638
|
...props,
|
|
569
639
|
children: [
|
|
570
|
-
/* @__PURE__ */ jsx17("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx17(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx17(
|
|
640
|
+
/* @__PURE__ */ jsx17("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx17(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx17(CheckIcon3, { className: "h-4 w-4" }) }) }),
|
|
571
641
|
children
|
|
572
642
|
]
|
|
573
643
|
}
|
|
@@ -583,7 +653,7 @@ var DropdownMenuRadioItem = forwardRef14(({ className, children, ...props }, ref
|
|
|
583
653
|
),
|
|
584
654
|
...props,
|
|
585
655
|
children: [
|
|
586
|
-
/* @__PURE__ */ jsx17("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx17(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx17(
|
|
656
|
+
/* @__PURE__ */ jsx17("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx17(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx17("span", { className: "h-2 w-2 rounded-full bg-current" }) }) }),
|
|
587
657
|
children
|
|
588
658
|
]
|
|
589
659
|
}
|
|
@@ -622,7 +692,7 @@ var DropdownMenuSubTrigger = forwardRef14(({ className, children, ...props }, re
|
|
|
622
692
|
...props,
|
|
623
693
|
children: [
|
|
624
694
|
children,
|
|
625
|
-
/* @__PURE__ */ jsx17(
|
|
695
|
+
/* @__PURE__ */ jsx17(ChevronRightIcon, { className: "ml-auto h-4 w-4" })
|
|
626
696
|
]
|
|
627
697
|
}
|
|
628
698
|
));
|
|
@@ -643,8 +713,8 @@ DropdownMenuSubContent.displayName = "DropdownMenuSubContent";
|
|
|
643
713
|
// src/primitives/sheet.tsx
|
|
644
714
|
import { cva as cva3 } from "class-variance-authority";
|
|
645
715
|
import * as DialogPrimitive2 from "@radix-ui/react-dialog";
|
|
646
|
-
import {
|
|
647
|
-
import { forwardRef as forwardRef15, useEffect, useRef, useState } from "react";
|
|
716
|
+
import { XMarkIcon as XMarkIcon2 } from "@heroicons/react/24/outline";
|
|
717
|
+
import { forwardRef as forwardRef15, useEffect, useRef as useRef2, useState } from "react";
|
|
648
718
|
import { jsx as jsx18, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
649
719
|
var Sheet = DialogPrimitive2.Root;
|
|
650
720
|
var SheetTrigger = DialogPrimitive2.Trigger;
|
|
@@ -695,7 +765,7 @@ var SheetContent = forwardRef15(
|
|
|
695
765
|
}, []);
|
|
696
766
|
const [dragHeight, setDragHeight] = useState(null);
|
|
697
767
|
const [isDragging, setIsDragging] = useState(false);
|
|
698
|
-
const dragStart =
|
|
768
|
+
const dragStart = useRef2(null);
|
|
699
769
|
const canResize = resizable && side === "bottom";
|
|
700
770
|
const onHandlePointerDown = (e) => {
|
|
701
771
|
const popup = e.currentTarget.closest('[data-slot="sheet-content"]');
|
|
@@ -750,7 +820,7 @@ var SheetContent = forwardRef15(
|
|
|
750
820
|
),
|
|
751
821
|
children,
|
|
752
822
|
/* @__PURE__ */ jsxs5(DialogPrimitive2.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100", children: [
|
|
753
|
-
/* @__PURE__ */ jsx18(
|
|
823
|
+
/* @__PURE__ */ jsx18(XMarkIcon2, { className: "h-4 w-4" }),
|
|
754
824
|
/* @__PURE__ */ jsx18("span", { className: "sr-only", children: "Close" })
|
|
755
825
|
] })
|
|
756
826
|
]
|
|
@@ -966,7 +1036,7 @@ var FormModal = ({
|
|
|
966
1036
|
};
|
|
967
1037
|
|
|
968
1038
|
// src/patterns/modal/ResponsivePanel.tsx
|
|
969
|
-
import { useRef as
|
|
1039
|
+
import { useRef as useRef3, useState as useState6 } from "react";
|
|
970
1040
|
|
|
971
1041
|
// src/lib/useMediaQuery.ts
|
|
972
1042
|
import { useEffect as useEffect2, useState as useState5 } from "react";
|
|
@@ -1022,8 +1092,8 @@ var cornerGripPosition = {
|
|
|
1022
1092
|
function useDesktopPanelGeometry() {
|
|
1023
1093
|
const [style, setStyle] = useState6(void 0);
|
|
1024
1094
|
const [isDragging, setIsDragging] = useState6(false);
|
|
1025
|
-
const drag =
|
|
1026
|
-
const lastRect =
|
|
1095
|
+
const drag = useRef3(null);
|
|
1096
|
+
const lastRect = useRef3(null);
|
|
1027
1097
|
const beginDrag = (mode, corner) => (e) => {
|
|
1028
1098
|
const content = e.currentTarget.closest('[data-slot="dialog-content"]');
|
|
1029
1099
|
if (!(content instanceof HTMLElement)) return;
|
|
@@ -1286,8 +1356,103 @@ function FormField({
|
|
|
1286
1356
|
}
|
|
1287
1357
|
|
|
1288
1358
|
// src/patterns/combobox/Combobox.tsx
|
|
1289
|
-
import {
|
|
1290
|
-
import { useEffect as useEffect3, useMemo, useRef as
|
|
1359
|
+
import { CheckIcon as CheckIcon4, ChevronUpDownIcon, XMarkIcon as XMarkIcon3 } from "@heroicons/react/24/outline";
|
|
1360
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef4, useState as useState8 } from "react";
|
|
1361
|
+
|
|
1362
|
+
// src/lib/errorState.ts
|
|
1363
|
+
function toErrorInfo(error) {
|
|
1364
|
+
if (error == null || error === false || error === "") return void 0;
|
|
1365
|
+
if (typeof error === "string") return { message: error };
|
|
1366
|
+
if (error instanceof Error) {
|
|
1367
|
+
return { message: error.message || void 0, details: error.stack };
|
|
1368
|
+
}
|
|
1369
|
+
if (typeof error === "object") {
|
|
1370
|
+
const { title, message, details } = error;
|
|
1371
|
+
if (title !== void 0 || message !== void 0 || details !== void 0) return { title, message, details };
|
|
1372
|
+
}
|
|
1373
|
+
try {
|
|
1374
|
+
return { message: typeof error === "object" ? "Unknown error" : String(error) };
|
|
1375
|
+
} catch {
|
|
1376
|
+
return { message: "Unknown error" };
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// src/patterns/error-state/ErrorState.tsx
|
|
1381
|
+
import { ArrowPathIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
|
1382
|
+
import { jsx as jsx27, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1383
|
+
var defaultErrorStateLabels = {
|
|
1384
|
+
title: "Couldn't load data",
|
|
1385
|
+
retry: "Retry",
|
|
1386
|
+
showDetails: "Show details"
|
|
1387
|
+
};
|
|
1388
|
+
var ErrorState = ({
|
|
1389
|
+
error,
|
|
1390
|
+
onRetry,
|
|
1391
|
+
variant = "block",
|
|
1392
|
+
labels,
|
|
1393
|
+
className,
|
|
1394
|
+
...props
|
|
1395
|
+
}) => {
|
|
1396
|
+
const info = toErrorInfo(error);
|
|
1397
|
+
if (!info) return null;
|
|
1398
|
+
const l = { ...defaultErrorStateLabels, ...labels };
|
|
1399
|
+
const compact = variant === "compact";
|
|
1400
|
+
return /* @__PURE__ */ jsxs11(
|
|
1401
|
+
"div",
|
|
1402
|
+
{
|
|
1403
|
+
role: "alert",
|
|
1404
|
+
"data-testid": "error-state",
|
|
1405
|
+
className: cn(
|
|
1406
|
+
"flex gap-3 text-sm",
|
|
1407
|
+
compact ? "items-start rounded-md border border-destructive bg-destructive/10 p-3 text-red-800 dark:text-red-300" : "min-h-32 w-full flex-col items-center justify-center p-6 text-center",
|
|
1408
|
+
className
|
|
1409
|
+
),
|
|
1410
|
+
...props,
|
|
1411
|
+
children: [
|
|
1412
|
+
/* @__PURE__ */ jsx27(
|
|
1413
|
+
ExclamationTriangleIcon,
|
|
1414
|
+
{
|
|
1415
|
+
className: cn("shrink-0", compact ? "mt-0.5 h-4 w-4" : "h-8 w-8 text-destructive"),
|
|
1416
|
+
"aria-hidden": "true"
|
|
1417
|
+
}
|
|
1418
|
+
),
|
|
1419
|
+
/* @__PURE__ */ jsxs11("div", { className: cn("flex min-w-0 flex-col gap-1", compact ? "items-start" : "items-center"), children: [
|
|
1420
|
+
/* @__PURE__ */ jsx27("p", { className: "font-semibold", children: info.title ?? l.title }),
|
|
1421
|
+
info.message ? /* @__PURE__ */ jsx27("p", { className: cn(!compact && "text-muted-foreground"), children: info.message }) : null,
|
|
1422
|
+
info.details ? /* @__PURE__ */ jsxs11("details", { className: "mt-1 w-full max-w-full text-left text-xs", children: [
|
|
1423
|
+
/* @__PURE__ */ jsx27("summary", { className: "cursor-pointer select-none underline underline-offset-2", children: l.showDetails }),
|
|
1424
|
+
/* @__PURE__ */ jsx27("pre", { className: "mt-1 max-h-40 overflow-auto whitespace-pre-wrap break-words rounded bg-muted p-2 text-muted-foreground", children: info.details })
|
|
1425
|
+
] }) : null,
|
|
1426
|
+
onRetry ? /* @__PURE__ */ jsxs11(Button, { type: "button", variant: "outline", size: "sm", className: "mt-2", onClick: onRetry, children: [
|
|
1427
|
+
/* @__PURE__ */ jsx27(ArrowPathIcon, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
1428
|
+
l.retry
|
|
1429
|
+
] }) : null
|
|
1430
|
+
] })
|
|
1431
|
+
]
|
|
1432
|
+
}
|
|
1433
|
+
);
|
|
1434
|
+
};
|
|
1435
|
+
var InlineError = ({ error, onRetry, labels, className, ...props }) => {
|
|
1436
|
+
const info = toErrorInfo(error);
|
|
1437
|
+
if (!info) return null;
|
|
1438
|
+
const l = { ...defaultErrorStateLabels, ...labels };
|
|
1439
|
+
const text = info.message ?? info.title ?? l.title;
|
|
1440
|
+
return /* @__PURE__ */ jsxs11(
|
|
1441
|
+
"span",
|
|
1442
|
+
{
|
|
1443
|
+
role: "alert",
|
|
1444
|
+
"data-testid": "inline-error",
|
|
1445
|
+
title: info.details,
|
|
1446
|
+
className: cn("inline-flex min-w-0 items-center gap-1.5 text-xs text-red-700 dark:text-red-300", className),
|
|
1447
|
+
...props,
|
|
1448
|
+
children: [
|
|
1449
|
+
/* @__PURE__ */ jsx27(ExclamationTriangleIcon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }),
|
|
1450
|
+
/* @__PURE__ */ jsx27("span", { className: "truncate", children: text }),
|
|
1451
|
+
onRetry ? /* @__PURE__ */ jsx27("button", { type: "button", className: "shrink-0 underline underline-offset-2", onClick: onRetry, children: l.retry }) : null
|
|
1452
|
+
]
|
|
1453
|
+
}
|
|
1454
|
+
);
|
|
1455
|
+
};
|
|
1291
1456
|
|
|
1292
1457
|
// src/lib/optionGrouping.ts
|
|
1293
1458
|
function groupMembers(options, group) {
|
|
@@ -1316,7 +1481,7 @@ function buildRenderChunks(visibleOptions) {
|
|
|
1316
1481
|
}
|
|
1317
1482
|
|
|
1318
1483
|
// src/lib/optionGrid.tsx
|
|
1319
|
-
import { jsx as
|
|
1484
|
+
import { jsx as jsx28, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1320
1485
|
function renderGridOptions({
|
|
1321
1486
|
columns,
|
|
1322
1487
|
options,
|
|
@@ -1327,10 +1492,10 @@ function renderGridOptions({
|
|
|
1327
1492
|
onHover,
|
|
1328
1493
|
testId
|
|
1329
1494
|
}) {
|
|
1330
|
-
return /* @__PURE__ */
|
|
1331
|
-
/* @__PURE__ */
|
|
1332
|
-
multiple && /* @__PURE__ */
|
|
1333
|
-
columns.map((column) => /* @__PURE__ */
|
|
1495
|
+
return /* @__PURE__ */ jsxs12(Table, { className: "text-sm", children: [
|
|
1496
|
+
/* @__PURE__ */ jsx28(TableHeader, { children: /* @__PURE__ */ jsxs12(TableRow, { className: "hover:bg-transparent", children: [
|
|
1497
|
+
multiple && /* @__PURE__ */ jsx28(TableHead, { className: "sticky top-0 z-10 w-6 bg-muted" }),
|
|
1498
|
+
columns.map((column) => /* @__PURE__ */ jsx28(
|
|
1334
1499
|
TableHead,
|
|
1335
1500
|
{
|
|
1336
1501
|
className: cn("sticky top-0 z-10 whitespace-nowrap bg-muted text-xs", column.className),
|
|
@@ -1339,10 +1504,10 @@ function renderGridOptions({
|
|
|
1339
1504
|
column.key
|
|
1340
1505
|
))
|
|
1341
1506
|
] }) }),
|
|
1342
|
-
/* @__PURE__ */
|
|
1507
|
+
/* @__PURE__ */ jsx28(TableBody, { children: options.map((option, index) => {
|
|
1343
1508
|
const selected = isSelected(option);
|
|
1344
1509
|
const active = index === activeIndex;
|
|
1345
|
-
return /* @__PURE__ */
|
|
1510
|
+
return /* @__PURE__ */ jsxs12(
|
|
1346
1511
|
TableRow,
|
|
1347
1512
|
{
|
|
1348
1513
|
role: "option",
|
|
@@ -1366,7 +1531,7 @@ function renderGridOptions({
|
|
|
1366
1531
|
option.disabled && "pointer-events-none opacity-50"
|
|
1367
1532
|
),
|
|
1368
1533
|
children: [
|
|
1369
|
-
multiple && /* @__PURE__ */
|
|
1534
|
+
multiple && /* @__PURE__ */ jsx28(TableCell, { className: "w-6", children: /* @__PURE__ */ jsx28(
|
|
1370
1535
|
"input",
|
|
1371
1536
|
{
|
|
1372
1537
|
type: "checkbox",
|
|
@@ -1376,7 +1541,7 @@ function renderGridOptions({
|
|
|
1376
1541
|
className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
|
|
1377
1542
|
}
|
|
1378
1543
|
) }),
|
|
1379
|
-
columns.map((column) => /* @__PURE__ */
|
|
1544
|
+
columns.map((column) => /* @__PURE__ */ jsx28(TableCell, { className: cn("truncate", column.className), children: column.key === "label" ? option.label : option.data?.[column.key] }, column.key))
|
|
1380
1545
|
]
|
|
1381
1546
|
},
|
|
1382
1547
|
option.value
|
|
@@ -1386,7 +1551,7 @@ function renderGridOptions({
|
|
|
1386
1551
|
}
|
|
1387
1552
|
|
|
1388
1553
|
// src/patterns/combobox/Combobox.tsx
|
|
1389
|
-
import { jsx as
|
|
1554
|
+
import { jsx as jsx29, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1390
1555
|
function resolveGroupTriggerLabel(options, selectedValues, groupLabel) {
|
|
1391
1556
|
const selectedSet = new Set(selectedValues);
|
|
1392
1557
|
const groupsPresent = Array.from(
|
|
@@ -1415,6 +1580,8 @@ function Combobox(props) {
|
|
|
1415
1580
|
disabled,
|
|
1416
1581
|
loading = false,
|
|
1417
1582
|
loadingMessage = "Loading\u2026",
|
|
1583
|
+
error,
|
|
1584
|
+
onRetry,
|
|
1418
1585
|
clearable = true,
|
|
1419
1586
|
className,
|
|
1420
1587
|
id,
|
|
@@ -1425,10 +1592,11 @@ function Combobox(props) {
|
|
|
1425
1592
|
columns
|
|
1426
1593
|
} = props;
|
|
1427
1594
|
const testId = props["data-testid"];
|
|
1595
|
+
const hasError = toErrorInfo(error) !== void 0;
|
|
1428
1596
|
const [open, setOpen] = useState8(false);
|
|
1429
1597
|
const [search, setSearch] = useState8("");
|
|
1430
1598
|
const [activeIndex, setActiveIndex] = useState8(0);
|
|
1431
|
-
const inputRef =
|
|
1599
|
+
const inputRef = useRef4(null);
|
|
1432
1600
|
const selectedValues = useMemo(
|
|
1433
1601
|
() => props.multiple ? props.value : props.value ? [props.value] : [],
|
|
1434
1602
|
[props.multiple, props.value]
|
|
@@ -1466,6 +1634,7 @@ function Combobox(props) {
|
|
|
1466
1634
|
setOpen(false);
|
|
1467
1635
|
}
|
|
1468
1636
|
function handleKeyDown(event) {
|
|
1637
|
+
if (hasError) return;
|
|
1469
1638
|
if (event.key === "ArrowDown") {
|
|
1470
1639
|
event.preventDefault();
|
|
1471
1640
|
setActiveIndex((i) => Math.min(i + 1, visibleOptions.length - 1));
|
|
@@ -1490,7 +1659,7 @@ function Combobox(props) {
|
|
|
1490
1659
|
function renderOption({ option, index }) {
|
|
1491
1660
|
const isSelected = selectedValues.includes(option.value);
|
|
1492
1661
|
const isActive = index === activeIndex;
|
|
1493
|
-
return /* @__PURE__ */
|
|
1662
|
+
return /* @__PURE__ */ jsxs13(
|
|
1494
1663
|
"button",
|
|
1495
1664
|
{
|
|
1496
1665
|
type: "button",
|
|
@@ -1506,7 +1675,7 @@ function Combobox(props) {
|
|
|
1506
1675
|
isActive && "bg-accent text-accent-foreground"
|
|
1507
1676
|
),
|
|
1508
1677
|
children: [
|
|
1509
|
-
props.multiple ? /* @__PURE__ */
|
|
1678
|
+
props.multiple ? /* @__PURE__ */ jsx29(
|
|
1510
1679
|
"input",
|
|
1511
1680
|
{
|
|
1512
1681
|
type: "checkbox",
|
|
@@ -1515,15 +1684,15 @@ function Combobox(props) {
|
|
|
1515
1684
|
tabIndex: -1,
|
|
1516
1685
|
className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
|
|
1517
1686
|
}
|
|
1518
|
-
) : /* @__PURE__ */
|
|
1519
|
-
/* @__PURE__ */
|
|
1687
|
+
) : /* @__PURE__ */ jsx29(CheckIcon4, { className: cn("h-4 w-4 shrink-0", isSelected ? "opacity-100" : "opacity-0") }),
|
|
1688
|
+
/* @__PURE__ */ jsx29("span", { className: "truncate", children: option.label })
|
|
1520
1689
|
]
|
|
1521
1690
|
},
|
|
1522
1691
|
option.value
|
|
1523
1692
|
);
|
|
1524
1693
|
}
|
|
1525
|
-
return /* @__PURE__ */
|
|
1526
|
-
/* @__PURE__ */
|
|
1694
|
+
return /* @__PURE__ */ jsxs13(Popover, { open, onOpenChange: setOpen, children: [
|
|
1695
|
+
/* @__PURE__ */ jsx29(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs13(
|
|
1527
1696
|
Button,
|
|
1528
1697
|
{
|
|
1529
1698
|
id,
|
|
@@ -1532,16 +1701,18 @@ function Combobox(props) {
|
|
|
1532
1701
|
variant: "outline",
|
|
1533
1702
|
role: "combobox",
|
|
1534
1703
|
"aria-expanded": open,
|
|
1704
|
+
"aria-invalid": hasError || void 0,
|
|
1535
1705
|
disabled: disabled || loading,
|
|
1536
1706
|
className: cn(
|
|
1537
1707
|
"w-full justify-between font-normal",
|
|
1708
|
+
hasError && "border-destructive",
|
|
1538
1709
|
(selectedOptions.length === 0 || loading) && "text-muted-foreground",
|
|
1539
1710
|
className
|
|
1540
1711
|
),
|
|
1541
1712
|
children: [
|
|
1542
|
-
/* @__PURE__ */
|
|
1543
|
-
/* @__PURE__ */
|
|
1544
|
-
clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */
|
|
1713
|
+
/* @__PURE__ */ jsx29("span", { className: "truncate", children: triggerLabel }),
|
|
1714
|
+
/* @__PURE__ */ jsxs13("span", { className: "flex items-center gap-1", children: [
|
|
1715
|
+
clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */ jsx29(
|
|
1545
1716
|
"span",
|
|
1546
1717
|
{
|
|
1547
1718
|
role: "button",
|
|
@@ -1556,15 +1727,15 @@ function Combobox(props) {
|
|
|
1556
1727
|
props.onChange(null);
|
|
1557
1728
|
}
|
|
1558
1729
|
},
|
|
1559
|
-
children: /* @__PURE__ */
|
|
1730
|
+
children: /* @__PURE__ */ jsx29(XMarkIcon3, { className: "h-3.5 w-3.5" })
|
|
1560
1731
|
}
|
|
1561
1732
|
) : null,
|
|
1562
|
-
/* @__PURE__ */
|
|
1733
|
+
/* @__PURE__ */ jsx29(ChevronUpDownIcon, { className: "h-4 w-4 shrink-0 opacity-50", "aria-hidden": "true" })
|
|
1563
1734
|
] })
|
|
1564
1735
|
]
|
|
1565
1736
|
}
|
|
1566
1737
|
) }),
|
|
1567
|
-
/* @__PURE__ */
|
|
1738
|
+
/* @__PURE__ */ jsx29(
|
|
1568
1739
|
PopoverContent,
|
|
1569
1740
|
{
|
|
1570
1741
|
align: "start",
|
|
@@ -1574,8 +1745,8 @@ function Combobox(props) {
|
|
|
1574
1745
|
columns && "w-max min-w-[var(--radix-popover-trigger-width)] max-w-[min(40rem,90vw)]"
|
|
1575
1746
|
),
|
|
1576
1747
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
1577
|
-
children: /* @__PURE__ */
|
|
1578
|
-
/* @__PURE__ */
|
|
1748
|
+
children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-2", children: [
|
|
1749
|
+
/* @__PURE__ */ jsx29(
|
|
1579
1750
|
Input,
|
|
1580
1751
|
{
|
|
1581
1752
|
ref: inputRef,
|
|
@@ -1589,7 +1760,7 @@ function Combobox(props) {
|
|
|
1589
1760
|
"aria-label": searchPlaceholder
|
|
1590
1761
|
}
|
|
1591
1762
|
),
|
|
1592
|
-
/* @__PURE__ */
|
|
1763
|
+
/* @__PURE__ */ jsx29("div", { role: hasError ? void 0 : "listbox", className: cn("max-h-60 overflow-y-auto", !columns && "flex flex-col gap-0.5"), children: hasError ? /* @__PURE__ */ jsx29("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsx29(InlineError, { error, onRetry }) }) : visibleOptions.length === 0 ? /* @__PURE__ */ jsx29("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : columns ? renderGridOptions({
|
|
1593
1764
|
columns,
|
|
1594
1765
|
options: visibleOptions,
|
|
1595
1766
|
activeIndex,
|
|
@@ -1609,14 +1780,14 @@ function Combobox(props) {
|
|
|
1609
1780
|
// lives on this wrapper's `pt-1`, not on the header itself -- a margin on
|
|
1610
1781
|
// the sticky element would travel with it once stuck, leaving a gap at the
|
|
1611
1782
|
// scrollport's top that the row behind it shows through.
|
|
1612
|
-
/* @__PURE__ */
|
|
1613
|
-
/* @__PURE__ */
|
|
1783
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-0.5 pt-1 first:pt-0", children: [
|
|
1784
|
+
/* @__PURE__ */ jsxs13(
|
|
1614
1785
|
"div",
|
|
1615
1786
|
{
|
|
1616
1787
|
"data-group-header": true,
|
|
1617
1788
|
className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
1618
1789
|
children: [
|
|
1619
|
-
props.multiple ? /* @__PURE__ */
|
|
1790
|
+
props.multiple ? /* @__PURE__ */ jsx29(
|
|
1620
1791
|
"input",
|
|
1621
1792
|
{
|
|
1622
1793
|
type: "checkbox",
|
|
@@ -1630,7 +1801,7 @@ function Combobox(props) {
|
|
|
1630
1801
|
"aria-label": `Select all of ${groupLabel(chunk.group)}`
|
|
1631
1802
|
}
|
|
1632
1803
|
) : null,
|
|
1633
|
-
/* @__PURE__ */
|
|
1804
|
+
/* @__PURE__ */ jsx29("span", { className: "truncate", children: groupLabel(chunk.group) })
|
|
1634
1805
|
]
|
|
1635
1806
|
}
|
|
1636
1807
|
),
|
|
@@ -1645,9 +1816,9 @@ function Combobox(props) {
|
|
|
1645
1816
|
}
|
|
1646
1817
|
|
|
1647
1818
|
// src/patterns/tag-combobox/TagCombobox.tsx
|
|
1648
|
-
import {
|
|
1649
|
-
import { useEffect as useEffect4, useMemo as useMemo2, useRef as
|
|
1650
|
-
import { jsx as
|
|
1819
|
+
import { XMarkIcon as XMarkIcon4 } from "@heroicons/react/24/outline";
|
|
1820
|
+
import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef5, useState as useState9 } from "react";
|
|
1821
|
+
import { jsx as jsx30, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1651
1822
|
function TagCombobox({
|
|
1652
1823
|
options,
|
|
1653
1824
|
value,
|
|
@@ -1657,6 +1828,8 @@ function TagCombobox({
|
|
|
1657
1828
|
disabled,
|
|
1658
1829
|
loading = false,
|
|
1659
1830
|
loadingMessage = "Loading\u2026",
|
|
1831
|
+
error,
|
|
1832
|
+
onRetry,
|
|
1660
1833
|
className,
|
|
1661
1834
|
id,
|
|
1662
1835
|
onSearchChange,
|
|
@@ -1669,8 +1842,8 @@ function TagCombobox({
|
|
|
1669
1842
|
const [open, setOpen] = useState9(false);
|
|
1670
1843
|
const [search, setSearch] = useState9("");
|
|
1671
1844
|
const [activeIndex, setActiveIndex] = useState9(0);
|
|
1672
|
-
const inputRef =
|
|
1673
|
-
const fieldRef =
|
|
1845
|
+
const inputRef = useRef5(null);
|
|
1846
|
+
const fieldRef = useRef5(null);
|
|
1674
1847
|
const selectedOptions = useMemo2(
|
|
1675
1848
|
() => value.map((v) => options.find((o) => o.value === v) ?? { value: v, label: v }),
|
|
1676
1849
|
[options, value]
|
|
@@ -1716,6 +1889,7 @@ function TagCombobox({
|
|
|
1716
1889
|
onChange(next);
|
|
1717
1890
|
inputRef.current?.focus();
|
|
1718
1891
|
}
|
|
1892
|
+
const hasError = toErrorInfo(error) !== void 0;
|
|
1719
1893
|
function handleKeyDown(event) {
|
|
1720
1894
|
if (event.key === "ArrowDown") {
|
|
1721
1895
|
event.preventDefault();
|
|
@@ -1727,7 +1901,7 @@ function TagCombobox({
|
|
|
1727
1901
|
} else if (event.key === "Enter") {
|
|
1728
1902
|
event.preventDefault();
|
|
1729
1903
|
const option = visibleOptions[activeIndex];
|
|
1730
|
-
if (option) toggleOption(option);
|
|
1904
|
+
if (option && !hasError) toggleOption(option);
|
|
1731
1905
|
} else if (event.key === "Escape") {
|
|
1732
1906
|
setOpen(false);
|
|
1733
1907
|
} else if (event.key === "Backspace" && search === "") {
|
|
@@ -1738,7 +1912,7 @@ function TagCombobox({
|
|
|
1738
1912
|
function renderOption({ option, index }) {
|
|
1739
1913
|
const isSelected = value.includes(option.value);
|
|
1740
1914
|
const isActive = index === activeIndex;
|
|
1741
|
-
return /* @__PURE__ */
|
|
1915
|
+
return /* @__PURE__ */ jsxs14(
|
|
1742
1916
|
"button",
|
|
1743
1917
|
{
|
|
1744
1918
|
type: "button",
|
|
@@ -1754,7 +1928,7 @@ function TagCombobox({
|
|
|
1754
1928
|
isActive && "bg-accent text-accent-foreground"
|
|
1755
1929
|
),
|
|
1756
1930
|
children: [
|
|
1757
|
-
/* @__PURE__ */
|
|
1931
|
+
/* @__PURE__ */ jsx30(
|
|
1758
1932
|
"input",
|
|
1759
1933
|
{
|
|
1760
1934
|
type: "checkbox",
|
|
@@ -1764,14 +1938,14 @@ function TagCombobox({
|
|
|
1764
1938
|
className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
|
|
1765
1939
|
}
|
|
1766
1940
|
),
|
|
1767
|
-
/* @__PURE__ */
|
|
1941
|
+
/* @__PURE__ */ jsx30("span", { className: "truncate", children: option.label })
|
|
1768
1942
|
]
|
|
1769
1943
|
},
|
|
1770
1944
|
option.value
|
|
1771
1945
|
);
|
|
1772
1946
|
}
|
|
1773
|
-
return /* @__PURE__ */
|
|
1774
|
-
/* @__PURE__ */
|
|
1947
|
+
return /* @__PURE__ */ jsxs14(Popover, { open, onOpenChange: setOpen, children: [
|
|
1948
|
+
/* @__PURE__ */ jsx30(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs14(
|
|
1775
1949
|
"div",
|
|
1776
1950
|
{
|
|
1777
1951
|
ref: fieldRef,
|
|
@@ -1789,13 +1963,13 @@ function TagCombobox({
|
|
|
1789
1963
|
if (!disabled) inputRef.current?.focus();
|
|
1790
1964
|
},
|
|
1791
1965
|
children: [
|
|
1792
|
-
selectedOptions.map((option) => /* @__PURE__ */
|
|
1966
|
+
selectedOptions.map((option) => /* @__PURE__ */ jsxs14(
|
|
1793
1967
|
"span",
|
|
1794
1968
|
{
|
|
1795
1969
|
className: "inline-flex items-center gap-1 rounded-full bg-muted py-0.5 pr-1 pl-2.5 text-xs font-medium text-muted-foreground",
|
|
1796
1970
|
children: [
|
|
1797
|
-
/* @__PURE__ */
|
|
1798
|
-
!disabled && /* @__PURE__ */
|
|
1971
|
+
/* @__PURE__ */ jsx30("span", { className: "max-w-48 truncate", children: option.label }),
|
|
1972
|
+
!disabled && /* @__PURE__ */ jsx30(
|
|
1799
1973
|
"button",
|
|
1800
1974
|
{
|
|
1801
1975
|
type: "button",
|
|
@@ -1805,14 +1979,14 @@ function TagCombobox({
|
|
|
1805
1979
|
event.stopPropagation();
|
|
1806
1980
|
removeValue(option.value);
|
|
1807
1981
|
},
|
|
1808
|
-
children: /* @__PURE__ */
|
|
1982
|
+
children: /* @__PURE__ */ jsx30(XMarkIcon4, { className: "h-3 w-3" })
|
|
1809
1983
|
}
|
|
1810
1984
|
)
|
|
1811
1985
|
]
|
|
1812
1986
|
},
|
|
1813
1987
|
option.value
|
|
1814
1988
|
)),
|
|
1815
|
-
/* @__PURE__ */
|
|
1989
|
+
/* @__PURE__ */ jsx30(
|
|
1816
1990
|
"input",
|
|
1817
1991
|
{
|
|
1818
1992
|
ref: inputRef,
|
|
@@ -1830,7 +2004,7 @@ function TagCombobox({
|
|
|
1830
2004
|
]
|
|
1831
2005
|
}
|
|
1832
2006
|
) }),
|
|
1833
|
-
/* @__PURE__ */
|
|
2007
|
+
/* @__PURE__ */ jsx30(
|
|
1834
2008
|
PopoverContent,
|
|
1835
2009
|
{
|
|
1836
2010
|
align: "start",
|
|
@@ -1844,7 +2018,7 @@ function TagCombobox({
|
|
|
1844
2018
|
onInteractOutside: (event) => {
|
|
1845
2019
|
if (fieldRef.current?.contains(event.target)) event.preventDefault();
|
|
1846
2020
|
},
|
|
1847
|
-
children: /* @__PURE__ */
|
|
2021
|
+
children: /* @__PURE__ */ jsx30("div", { role: hasError ? void 0 : "listbox", className: cn("max-h-60 overflow-y-auto", !columns && "flex flex-col gap-0.5"), children: loading ? /* @__PURE__ */ jsx30("p", { className: "py-2 text-center text-sm text-muted-foreground", children: loadingMessage }) : hasError ? /* @__PURE__ */ jsx30("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsx30(InlineError, { error, onRetry }) }) : visibleOptions.length === 0 ? /* @__PURE__ */ jsx30("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : columns ? renderGridOptions({
|
|
1848
2022
|
columns,
|
|
1849
2023
|
options: visibleOptions,
|
|
1850
2024
|
activeIndex,
|
|
@@ -1863,14 +2037,14 @@ function TagCombobox({
|
|
|
1863
2037
|
// past its first row. Spacing above a group lives on this wrapper's
|
|
1864
2038
|
// `pt-1`/`first:pt-0`, not on the header itself -- see `Combobox`'s own
|
|
1865
2039
|
// identical structure for why both of these matter.
|
|
1866
|
-
/* @__PURE__ */
|
|
1867
|
-
/* @__PURE__ */
|
|
2040
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-0.5 pt-1 first:pt-0", children: [
|
|
2041
|
+
/* @__PURE__ */ jsxs14(
|
|
1868
2042
|
"div",
|
|
1869
2043
|
{
|
|
1870
2044
|
"data-group-header": true,
|
|
1871
2045
|
className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
1872
2046
|
children: [
|
|
1873
|
-
/* @__PURE__ */
|
|
2047
|
+
/* @__PURE__ */ jsx30(
|
|
1874
2048
|
"input",
|
|
1875
2049
|
{
|
|
1876
2050
|
type: "checkbox",
|
|
@@ -1884,7 +2058,7 @@ function TagCombobox({
|
|
|
1884
2058
|
"aria-label": `Select all of ${groupLabel(chunk.group)}`
|
|
1885
2059
|
}
|
|
1886
2060
|
),
|
|
1887
|
-
/* @__PURE__ */
|
|
2061
|
+
/* @__PURE__ */ jsx30("span", { className: "truncate", children: groupLabel(chunk.group) })
|
|
1888
2062
|
]
|
|
1889
2063
|
}
|
|
1890
2064
|
),
|
|
@@ -1898,8 +2072,13 @@ function TagCombobox({
|
|
|
1898
2072
|
}
|
|
1899
2073
|
|
|
1900
2074
|
// src/patterns/alert-box/AlertBox.tsx
|
|
1901
|
-
import {
|
|
1902
|
-
|
|
2075
|
+
import {
|
|
2076
|
+
CheckCircleIcon,
|
|
2077
|
+
ExclamationCircleIcon,
|
|
2078
|
+
ExclamationTriangleIcon as ExclamationTriangleIcon2,
|
|
2079
|
+
InformationCircleIcon
|
|
2080
|
+
} from "@heroicons/react/24/outline";
|
|
2081
|
+
import { jsx as jsx31, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1903
2082
|
var variantStyles = {
|
|
1904
2083
|
error: "border-destructive bg-destructive/10 text-red-800 dark:text-red-300",
|
|
1905
2084
|
warning: "border-amber-500 bg-amber-500/10 text-amber-900 dark:text-amber-200",
|
|
@@ -1907,24 +2086,24 @@ var variantStyles = {
|
|
|
1907
2086
|
success: "border-emerald-500 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"
|
|
1908
2087
|
};
|
|
1909
2088
|
var defaultIcons = {
|
|
1910
|
-
error: /* @__PURE__ */
|
|
1911
|
-
warning: /* @__PURE__ */
|
|
1912
|
-
info: /* @__PURE__ */
|
|
1913
|
-
success: /* @__PURE__ */
|
|
2089
|
+
error: /* @__PURE__ */ jsx31(ExclamationCircleIcon, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
2090
|
+
warning: /* @__PURE__ */ jsx31(ExclamationTriangleIcon2, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
2091
|
+
info: /* @__PURE__ */ jsx31(InformationCircleIcon, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
2092
|
+
success: /* @__PURE__ */ jsx31(CheckCircleIcon, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
|
|
1914
2093
|
};
|
|
1915
2094
|
var AlertBox = ({ variant, title, children, className, icon }) => {
|
|
1916
2095
|
const resolvedIcon = icon === void 0 ? defaultIcons[variant] : icon;
|
|
1917
|
-
return /* @__PURE__ */
|
|
1918
|
-
resolvedIcon != null && /* @__PURE__ */
|
|
1919
|
-
/* @__PURE__ */
|
|
1920
|
-
title && /* @__PURE__ */
|
|
1921
|
-
/* @__PURE__ */
|
|
2096
|
+
return /* @__PURE__ */ jsx31("div", { className: cn("rounded-md border p-3 text-sm", variantStyles[variant], className), children: /* @__PURE__ */ jsxs15("div", { className: "flex flex-row items-start gap-2", children: [
|
|
2097
|
+
resolvedIcon != null && /* @__PURE__ */ jsx31("span", { className: "mt-0.5 shrink-0", children: resolvedIcon }),
|
|
2098
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-1", children: [
|
|
2099
|
+
title && /* @__PURE__ */ jsx31("p", { className: "font-bold", children: title }),
|
|
2100
|
+
/* @__PURE__ */ jsx31("div", { children })
|
|
1922
2101
|
] })
|
|
1923
2102
|
] }) });
|
|
1924
2103
|
};
|
|
1925
2104
|
|
|
1926
2105
|
// src/patterns/status-badge/StatusBadge.tsx
|
|
1927
|
-
import { jsx as
|
|
2106
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
1928
2107
|
var TONE_CLASSES = {
|
|
1929
2108
|
success: "border-transparent bg-emerald-500/15 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300",
|
|
1930
2109
|
warning: "border-transparent bg-amber-500/15 text-amber-800 dark:bg-amber-500/20 dark:text-amber-300",
|
|
@@ -1971,11 +2150,11 @@ function resolveTone(status, tone, toneMap) {
|
|
|
1971
2150
|
var StatusBadge = ({ status, label, tone, toneMap, className, ...props }) => {
|
|
1972
2151
|
const resolvedTone = resolveTone(status, tone, toneMap);
|
|
1973
2152
|
const displayLabel = label ?? deriveLabel(status);
|
|
1974
|
-
return /* @__PURE__ */
|
|
2153
|
+
return /* @__PURE__ */ jsx32(Badge, { variant: "outline", className: cn(TONE_CLASSES[resolvedTone], className), ...props, children: displayLabel });
|
|
1975
2154
|
};
|
|
1976
2155
|
|
|
1977
2156
|
// src/patterns/button-group/ButtonGroup.tsx
|
|
1978
|
-
import { jsx as
|
|
2157
|
+
import { jsx as jsx33, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1979
2158
|
function ButtonGroup({
|
|
1980
2159
|
options,
|
|
1981
2160
|
value,
|
|
@@ -1985,10 +2164,10 @@ function ButtonGroup({
|
|
|
1985
2164
|
className,
|
|
1986
2165
|
...props
|
|
1987
2166
|
}) {
|
|
1988
|
-
return /* @__PURE__ */
|
|
2167
|
+
return /* @__PURE__ */ jsx33("div", { role: "group", "aria-label": props["aria-label"], className: cn("inline-flex", className), "data-testid": props["data-testid"], children: options.map((option, index) => {
|
|
1989
2168
|
const selected = option.value === value;
|
|
1990
2169
|
const Icon2 = option.icon;
|
|
1991
|
-
return /* @__PURE__ */
|
|
2170
|
+
return /* @__PURE__ */ jsxs16(
|
|
1992
2171
|
Button,
|
|
1993
2172
|
{
|
|
1994
2173
|
type: "button",
|
|
@@ -2006,7 +2185,7 @@ function ButtonGroup({
|
|
|
2006
2185
|
index > 0 && "-ml-px"
|
|
2007
2186
|
),
|
|
2008
2187
|
children: [
|
|
2009
|
-
Icon2 && /* @__PURE__ */
|
|
2188
|
+
Icon2 && /* @__PURE__ */ jsx33(Icon2, { className: "h-4 w-4" }),
|
|
2010
2189
|
option.label
|
|
2011
2190
|
]
|
|
2012
2191
|
},
|
|
@@ -2017,8 +2196,8 @@ function ButtonGroup({
|
|
|
2017
2196
|
|
|
2018
2197
|
// src/patterns/loading-spinner/LoadingSpinner.tsx
|
|
2019
2198
|
import { cva as cva4 } from "class-variance-authority";
|
|
2020
|
-
import {
|
|
2021
|
-
import { jsx as
|
|
2199
|
+
import { ArrowPathIcon as ArrowPathIcon2 } from "@heroicons/react/24/outline";
|
|
2200
|
+
import { jsx as jsx34, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2022
2201
|
var loadingSpinnerIconVariants = cva4("animate-spin text-muted-foreground", {
|
|
2023
2202
|
variants: {
|
|
2024
2203
|
size: {
|
|
@@ -2036,75 +2215,167 @@ var LoadingSpinner = ({
|
|
|
2036
2215
|
label,
|
|
2037
2216
|
className,
|
|
2038
2217
|
...props
|
|
2039
|
-
}) => /* @__PURE__ */
|
|
2040
|
-
/* @__PURE__ */
|
|
2041
|
-
label ? /* @__PURE__ */
|
|
2218
|
+
}) => /* @__PURE__ */ jsxs17("span", { role: "status", className: cn("inline-flex items-center gap-2", className), ...props, children: [
|
|
2219
|
+
/* @__PURE__ */ jsx34(ArrowPathIcon2, { className: cn(loadingSpinnerIconVariants({ size })), "aria-hidden": "true" }),
|
|
2220
|
+
label ? /* @__PURE__ */ jsx34("span", { children: label }) : null
|
|
2042
2221
|
] });
|
|
2043
2222
|
var LoadingOverlay = ({
|
|
2044
2223
|
size = "lg",
|
|
2045
2224
|
label,
|
|
2046
2225
|
className,
|
|
2047
2226
|
...props
|
|
2048
|
-
}) => /* @__PURE__ */
|
|
2227
|
+
}) => /* @__PURE__ */ jsx34(
|
|
2049
2228
|
"div",
|
|
2050
2229
|
{
|
|
2051
2230
|
className: cn("flex h-full min-h-32 w-full items-center justify-center", className),
|
|
2052
2231
|
...props,
|
|
2053
|
-
children: /* @__PURE__ */
|
|
2232
|
+
children: /* @__PURE__ */ jsx34(LoadingSpinner, { size, label })
|
|
2054
2233
|
}
|
|
2055
2234
|
);
|
|
2056
2235
|
|
|
2236
|
+
// src/patterns/loading-spinner/LoadingState.tsx
|
|
2237
|
+
import { useEffect as useEffect5, useState as useState10 } from "react";
|
|
2238
|
+
import { jsx as jsx35, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2239
|
+
function useDelayedFlag(active, delayMs = 150) {
|
|
2240
|
+
const [shown, setShown] = useState10(false);
|
|
2241
|
+
useEffect5(() => {
|
|
2242
|
+
if (!active) {
|
|
2243
|
+
setShown(false);
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
if (delayMs <= 0) {
|
|
2247
|
+
setShown(true);
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
const timer = setTimeout(() => setShown(true), delayMs);
|
|
2251
|
+
return () => clearTimeout(timer);
|
|
2252
|
+
}, [active, delayMs]);
|
|
2253
|
+
return active && (delayMs <= 0 || shown);
|
|
2254
|
+
}
|
|
2255
|
+
var LoadingState = ({
|
|
2256
|
+
variant = "skeleton",
|
|
2257
|
+
lines = 3,
|
|
2258
|
+
label = "Loading\u2026",
|
|
2259
|
+
delayMs = 150,
|
|
2260
|
+
className,
|
|
2261
|
+
...props
|
|
2262
|
+
}) => {
|
|
2263
|
+
const visible = useDelayedFlag(true, delayMs);
|
|
2264
|
+
if (variant === "spinner") {
|
|
2265
|
+
return /* @__PURE__ */ jsx35("div", { "data-testid": "loading-state", "aria-busy": "true", className: cn(!visible && "invisible", className), ...props, children: /* @__PURE__ */ jsx35(LoadingOverlay, { label }) });
|
|
2266
|
+
}
|
|
2267
|
+
return /* @__PURE__ */ jsxs18(
|
|
2268
|
+
"div",
|
|
2269
|
+
{
|
|
2270
|
+
role: "status",
|
|
2271
|
+
"aria-busy": "true",
|
|
2272
|
+
"data-testid": "loading-state",
|
|
2273
|
+
className: cn("flex w-full flex-col gap-2 p-1", !visible && "invisible", className),
|
|
2274
|
+
...props,
|
|
2275
|
+
children: [
|
|
2276
|
+
/* @__PURE__ */ jsx35("span", { className: "sr-only", children: label }),
|
|
2277
|
+
Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx35(Skeleton, { "aria-hidden": "true", className: cn("h-4", i === lines - 1 && lines > 1 ? "w-2/3" : "w-full") }, i))
|
|
2278
|
+
]
|
|
2279
|
+
}
|
|
2280
|
+
);
|
|
2281
|
+
};
|
|
2282
|
+
var RefreshBar = ({ active, className }) => active ? /* @__PURE__ */ jsx35(
|
|
2283
|
+
"div",
|
|
2284
|
+
{
|
|
2285
|
+
role: "progressbar",
|
|
2286
|
+
"aria-label": "Refreshing",
|
|
2287
|
+
"data-testid": "refresh-bar",
|
|
2288
|
+
className: cn("absolute inset-x-0 top-0 z-30 h-0.5 overflow-hidden bg-muted", className),
|
|
2289
|
+
children: /* @__PURE__ */ jsx35("div", { className: "h-full w-1/3 animate-pulse bg-primary motion-reduce:animate-none" })
|
|
2290
|
+
}
|
|
2291
|
+
) : null;
|
|
2292
|
+
|
|
2293
|
+
// src/patterns/async-boundary/AsyncBoundary.tsx
|
|
2294
|
+
import { Fragment as Fragment3, jsx as jsx36, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2295
|
+
var AsyncBoundary = ({
|
|
2296
|
+
loading = false,
|
|
2297
|
+
error,
|
|
2298
|
+
onRetry,
|
|
2299
|
+
empty = false,
|
|
2300
|
+
emptyContent,
|
|
2301
|
+
loadingContent,
|
|
2302
|
+
hasData,
|
|
2303
|
+
children
|
|
2304
|
+
}) => {
|
|
2305
|
+
const failed = toErrorInfo(error) !== void 0;
|
|
2306
|
+
const showData = hasData ?? false;
|
|
2307
|
+
if (loading && !showData) return /* @__PURE__ */ jsx36(Fragment3, { children: loadingContent ?? /* @__PURE__ */ jsx36(LoadingState, {}) });
|
|
2308
|
+
if (failed && !showData) return /* @__PURE__ */ jsx36(ErrorState, { error, onRetry });
|
|
2309
|
+
if (empty && !showData) return /* @__PURE__ */ jsx36(Fragment3, { children: emptyContent ?? null });
|
|
2310
|
+
return /* @__PURE__ */ jsxs19("div", { className: "relative", children: [
|
|
2311
|
+
/* @__PURE__ */ jsx36(RefreshBar, { active: loading }),
|
|
2312
|
+
failed ? /* @__PURE__ */ jsx36(InlineError, { error, onRetry, className: "mb-2" }) : null,
|
|
2313
|
+
children
|
|
2314
|
+
] });
|
|
2315
|
+
};
|
|
2316
|
+
|
|
2057
2317
|
// src/patterns/search-bar/SearchBar.tsx
|
|
2058
|
-
import {
|
|
2059
|
-
import { jsx as
|
|
2318
|
+
import { ExclamationTriangleIcon as ExclamationTriangleIcon3, MagnifyingGlassIcon, XMarkIcon as XMarkIcon5 } from "@heroicons/react/24/outline";
|
|
2319
|
+
import { jsx as jsx37, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2060
2320
|
function SearchBar({
|
|
2061
2321
|
value,
|
|
2062
2322
|
onChange,
|
|
2323
|
+
loading,
|
|
2063
2324
|
isLoading,
|
|
2325
|
+
error,
|
|
2326
|
+
onRetry,
|
|
2064
2327
|
onClear,
|
|
2065
2328
|
clearLabel = "Clear search",
|
|
2066
2329
|
inputRef,
|
|
2067
2330
|
className,
|
|
2068
2331
|
...props
|
|
2069
2332
|
}) {
|
|
2333
|
+
const busy = loading ?? isLoading;
|
|
2334
|
+
const hasError = toErrorInfo(error) !== void 0;
|
|
2070
2335
|
const showClear = onClear !== false && value.length > 0;
|
|
2071
|
-
return /* @__PURE__ */
|
|
2072
|
-
/* @__PURE__ */
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2336
|
+
return /* @__PURE__ */ jsxs20("div", { className, children: [
|
|
2337
|
+
/* @__PURE__ */ jsxs20("div", { className: "relative", children: [
|
|
2338
|
+
/* @__PURE__ */ jsx37("span", { className: "pointer-events-none absolute top-1/2 left-4 -translate-y-1/2 text-muted-foreground", children: busy ? /* @__PURE__ */ jsx37(LoadingSpinner, { size: "sm" }) : hasError ? /* @__PURE__ */ jsx37(ExclamationTriangleIcon3, { className: "h-4 w-4 text-destructive", "aria-hidden": "true" }) : /* @__PURE__ */ jsx37(MagnifyingGlassIcon, { className: "h-4 w-4", "aria-hidden": "true" }) }),
|
|
2339
|
+
/* @__PURE__ */ jsx37(
|
|
2340
|
+
"input",
|
|
2341
|
+
{
|
|
2342
|
+
ref: inputRef,
|
|
2343
|
+
type: "search",
|
|
2344
|
+
"aria-invalid": hasError || void 0,
|
|
2345
|
+
value,
|
|
2346
|
+
onChange: (event) => onChange(event.target.value),
|
|
2347
|
+
className: cn(
|
|
2348
|
+
"h-11 w-full rounded-full border bg-background text-[15px] text-foreground placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
|
|
2349
|
+
// Chromium/Safari render their own native clear (×) control on
|
|
2350
|
+
// type="search" inputs -- left alone, it stacks on top of our own
|
|
2351
|
+
// custom clear button below instead of replacing it.
|
|
2352
|
+
"[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none",
|
|
2353
|
+
hasError ? "border-destructive" : "border-input",
|
|
2354
|
+
showClear ? "pr-11" : "pr-4",
|
|
2355
|
+
"pl-11"
|
|
2356
|
+
),
|
|
2357
|
+
...props
|
|
2358
|
+
}
|
|
2359
|
+
),
|
|
2360
|
+
showClear && /* @__PURE__ */ jsx37(
|
|
2361
|
+
"button",
|
|
2362
|
+
{
|
|
2363
|
+
type: "button",
|
|
2364
|
+
onClick: () => onClear ? onClear() : onChange(""),
|
|
2365
|
+
"aria-label": clearLabel,
|
|
2366
|
+
className: "absolute top-1/2 right-2 flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-full text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
2367
|
+
children: /* @__PURE__ */ jsx37(XMarkIcon5, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
2368
|
+
}
|
|
2369
|
+
)
|
|
2370
|
+
] }),
|
|
2371
|
+
hasError && /* @__PURE__ */ jsx37(InlineError, { error, onRetry, className: "mt-1 ml-4" })
|
|
2102
2372
|
] });
|
|
2103
2373
|
}
|
|
2104
2374
|
|
|
2105
2375
|
// src/patterns/kpi-card/KpiCard.tsx
|
|
2106
2376
|
import { memo } from "react";
|
|
2107
|
-
import {
|
|
2377
|
+
import { ExclamationTriangleIcon as ExclamationTriangleIcon4 } from "@heroicons/react/24/outline";
|
|
2378
|
+
import { Fragment as Fragment4, jsx as jsx38, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2108
2379
|
var DONUT_COLORS = [
|
|
2109
2380
|
"var(--chart-1, #3b82f6)",
|
|
2110
2381
|
"var(--chart-2, #22c55e)",
|
|
@@ -2125,13 +2396,13 @@ function DonutChart({
|
|
|
2125
2396
|
const radius = (size - thickness) / 2;
|
|
2126
2397
|
const circumference = 2 * Math.PI * radius;
|
|
2127
2398
|
let offset = 0;
|
|
2128
|
-
return /* @__PURE__ */
|
|
2129
|
-
/* @__PURE__ */
|
|
2130
|
-
/* @__PURE__ */
|
|
2399
|
+
return /* @__PURE__ */ jsxs21("div", { className: "relative inline-flex shrink-0 items-center justify-center", style: { width: size, height: size }, children: [
|
|
2400
|
+
/* @__PURE__ */ jsxs21("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, className: "-rotate-90", children: [
|
|
2401
|
+
/* @__PURE__ */ jsx38("circle", { cx: size / 2, cy: size / 2, r: radius, fill: "none", stroke: "currentColor", className: "text-muted/40", strokeWidth: thickness }),
|
|
2131
2402
|
data.map((segment, i) => {
|
|
2132
2403
|
const fraction = segment.value / total;
|
|
2133
2404
|
const dash = fraction * circumference;
|
|
2134
|
-
const el = /* @__PURE__ */
|
|
2405
|
+
const el = /* @__PURE__ */ jsx38(
|
|
2135
2406
|
"circle",
|
|
2136
2407
|
{
|
|
2137
2408
|
cx: size / 2,
|
|
@@ -2150,9 +2421,9 @@ function DonutChart({
|
|
|
2150
2421
|
return el;
|
|
2151
2422
|
})
|
|
2152
2423
|
] }),
|
|
2153
|
-
(centerValue || centerLabel) && /* @__PURE__ */
|
|
2154
|
-
centerValue && /* @__PURE__ */
|
|
2155
|
-
centerLabel && /* @__PURE__ */
|
|
2424
|
+
(centerValue || centerLabel) && /* @__PURE__ */ jsxs21("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: [
|
|
2425
|
+
centerValue && /* @__PURE__ */ jsx38("span", { className: "text-base font-black tracking-tight text-foreground tabular-nums", children: centerValue }),
|
|
2426
|
+
centerLabel && /* @__PURE__ */ jsx38("span", { className: "text-[9px] font-medium text-muted-foreground uppercase", children: centerLabel })
|
|
2156
2427
|
] })
|
|
2157
2428
|
] });
|
|
2158
2429
|
}
|
|
@@ -2164,7 +2435,7 @@ function Sparkline({ data, color }) {
|
|
|
2164
2435
|
const W = 72;
|
|
2165
2436
|
const H = 28;
|
|
2166
2437
|
const pts = data.map((v, i) => `${i / (data.length - 1) * W},${H - (v - min) / range * (H - 2) - 1}`).join(" ");
|
|
2167
|
-
return /* @__PURE__ */
|
|
2438
|
+
return /* @__PURE__ */ jsx38("svg", { width: W, height: H, viewBox: `0 0 ${W} ${H}`, className: "overflow-visible opacity-70", children: /* @__PURE__ */ jsx38("polyline", { points: pts, fill: "none", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
2168
2439
|
}
|
|
2169
2440
|
function AreaSparkline({ data, color }) {
|
|
2170
2441
|
if (data.length < 2) return null;
|
|
@@ -2178,14 +2449,14 @@ function AreaSparkline({ data, color }) {
|
|
|
2178
2449
|
const ys = data.map((v) => H - (v - min) / range * (H - 2 * P) - P);
|
|
2179
2450
|
const linePts = xs.map((x, i) => `${x},${ys[i]}`).join(" ");
|
|
2180
2451
|
const areaPath = `M${xs[0]},${H} ` + xs.map((x, i) => `L${x},${ys[i]}`).join(" ") + ` L${xs[xs.length - 1]},${H} Z`;
|
|
2181
|
-
return /* @__PURE__ */
|
|
2182
|
-
/* @__PURE__ */
|
|
2183
|
-
/* @__PURE__ */
|
|
2184
|
-
/* @__PURE__ */
|
|
2452
|
+
return /* @__PURE__ */ jsxs21("svg", { width: W, height: H, viewBox: `0 0 ${W} ${H}`, className: "overflow-visible", preserveAspectRatio: "none", children: [
|
|
2453
|
+
/* @__PURE__ */ jsx38("defs", { children: /* @__PURE__ */ jsxs21("linearGradient", { id: "kpi-card-spark-fill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
2454
|
+
/* @__PURE__ */ jsx38("stop", { offset: "0%", stopColor: color, stopOpacity: "0.22" }),
|
|
2455
|
+
/* @__PURE__ */ jsx38("stop", { offset: "100%", stopColor: color, stopOpacity: "0" })
|
|
2185
2456
|
] }) }),
|
|
2186
|
-
/* @__PURE__ */
|
|
2187
|
-
/* @__PURE__ */
|
|
2188
|
-
/* @__PURE__ */
|
|
2457
|
+
/* @__PURE__ */ jsx38("path", { d: areaPath, fill: "url(#kpi-card-spark-fill)" }),
|
|
2458
|
+
/* @__PURE__ */ jsx38("polyline", { points: linePts, fill: "none", stroke: color, strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
2459
|
+
/* @__PURE__ */ jsx38("circle", { cx: xs[xs.length - 1], cy: ys[ys.length - 1], r: "2.5", fill: color })
|
|
2189
2460
|
] });
|
|
2190
2461
|
}
|
|
2191
2462
|
var rowBetween = "flex items-center justify-between";
|
|
@@ -2197,6 +2468,8 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2197
2468
|
badgeLabel,
|
|
2198
2469
|
variant = "default",
|
|
2199
2470
|
loading = false,
|
|
2471
|
+
error,
|
|
2472
|
+
onRetry,
|
|
2200
2473
|
sub,
|
|
2201
2474
|
subTone = "default",
|
|
2202
2475
|
href,
|
|
@@ -2209,9 +2482,33 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2209
2482
|
background,
|
|
2210
2483
|
testId
|
|
2211
2484
|
}) {
|
|
2485
|
+
const errorInfo = loading ? void 0 : toErrorInfo(error);
|
|
2486
|
+
if (errorInfo) {
|
|
2487
|
+
const hero = variant === "hero";
|
|
2488
|
+
return /* @__PURE__ */ jsxs21(
|
|
2489
|
+
"div",
|
|
2490
|
+
{
|
|
2491
|
+
"data-testid": testId,
|
|
2492
|
+
role: "status",
|
|
2493
|
+
className: cn(
|
|
2494
|
+
"flex min-w-0 flex-col gap-1 rounded-2xl border border-destructive/40 bg-card p-3",
|
|
2495
|
+
hero && "col-span-2 flex-1 lg:col-span-1 lg:max-w-[340px] lg:min-w-[260px]"
|
|
2496
|
+
),
|
|
2497
|
+
children: [
|
|
2498
|
+
/* @__PURE__ */ jsx38("p", { className: "text-[11px] font-bold tracking-[0.14em] text-muted-foreground uppercase", children: label }),
|
|
2499
|
+
/* @__PURE__ */ jsxs21("p", { className: "flex items-center gap-1.5 text-xl font-bold text-muted-foreground", children: [
|
|
2500
|
+
"\u2014",
|
|
2501
|
+
/* @__PURE__ */ jsx38(ExclamationTriangleIcon4, { className: "h-4 w-4 text-destructive", "aria-hidden": "true" })
|
|
2502
|
+
] }),
|
|
2503
|
+
/* @__PURE__ */ jsx38("p", { className: "truncate text-xs text-red-700 dark:text-red-300", children: errorInfo.message ?? errorInfo.title ?? "Failed to load" }),
|
|
2504
|
+
onRetry ? /* @__PURE__ */ jsx38("button", { type: "button", onClick: onRetry, className: "self-start text-xs text-red-700 underline underline-offset-2 dark:text-red-300", children: "Retry" }) : null
|
|
2505
|
+
]
|
|
2506
|
+
}
|
|
2507
|
+
);
|
|
2508
|
+
}
|
|
2212
2509
|
const bgStyle = background ? { background } : void 0;
|
|
2213
2510
|
if (variant === "hero") {
|
|
2214
|
-
return /* @__PURE__ */
|
|
2511
|
+
return /* @__PURE__ */ jsxs21(
|
|
2215
2512
|
"div",
|
|
2216
2513
|
{
|
|
2217
2514
|
"data-testid": testId,
|
|
@@ -2221,13 +2518,13 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2221
2518
|
boxShadow: `0 2px 10px color-mix(in oklch, ${background ?? "var(--color-primary)"} 18%, transparent)`
|
|
2222
2519
|
},
|
|
2223
2520
|
children: [
|
|
2224
|
-
/* @__PURE__ */
|
|
2225
|
-
/* @__PURE__ */
|
|
2226
|
-
Icon2 && /* @__PURE__ */
|
|
2521
|
+
/* @__PURE__ */ jsxs21("div", { className: "relative flex items-start justify-between", children: [
|
|
2522
|
+
/* @__PURE__ */ jsx38("p", { className: "text-[11px] font-bold tracking-[0.14em] text-white/70 uppercase", children: label }),
|
|
2523
|
+
Icon2 && /* @__PURE__ */ jsx38("div", { className: "flex h-6 w-6 items-center justify-center rounded-lg bg-white/15 ring-1 ring-white/20", children: /* @__PURE__ */ jsx38(Icon2, { className: "h-3 w-3 text-white/90", "aria-hidden": "true" }) })
|
|
2227
2524
|
] }),
|
|
2228
|
-
/* @__PURE__ */
|
|
2229
|
-
(badge || badgeLabel) && !loading && /* @__PURE__ */
|
|
2230
|
-
badge && /* @__PURE__ */
|
|
2525
|
+
/* @__PURE__ */ jsx38("div", { className: "relative", children: loading ? /* @__PURE__ */ jsx38("div", { className: "h-6 w-28 animate-pulse rounded-lg bg-white/15" }) : /* @__PURE__ */ jsx38("p", { className: "text-2xl leading-none font-black tracking-tight text-white tabular-nums md:text-[22px]", children: value ?? "\u2014" }) }),
|
|
2526
|
+
(badge || badgeLabel) && !loading && /* @__PURE__ */ jsxs21("div", { className: "relative flex items-center gap-2", children: [
|
|
2527
|
+
badge && /* @__PURE__ */ jsx38(
|
|
2231
2528
|
"span",
|
|
2232
2529
|
{
|
|
2233
2530
|
className: cn(
|
|
@@ -2237,28 +2534,28 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2237
2534
|
children: badge.text
|
|
2238
2535
|
}
|
|
2239
2536
|
),
|
|
2240
|
-
badgeLabel && /* @__PURE__ */
|
|
2537
|
+
badgeLabel && /* @__PURE__ */ jsx38("span", { className: "text-[11px] text-white/70", children: badgeLabel })
|
|
2241
2538
|
] }),
|
|
2242
|
-
sub && !loading && /* @__PURE__ */
|
|
2243
|
-
progress != null && !loading && /* @__PURE__ */
|
|
2244
|
-
/* @__PURE__ */
|
|
2245
|
-
progressLabel && /* @__PURE__ */
|
|
2539
|
+
sub && !loading && /* @__PURE__ */ jsx38("p", { className: "relative -mt-1 text-[11px] font-medium text-white/75 tabular-nums", children: sub }),
|
|
2540
|
+
progress != null && !loading && /* @__PURE__ */ jsxs21("div", { className: "relative flex items-center gap-1.5", children: [
|
|
2541
|
+
/* @__PURE__ */ jsx38("div", { className: "h-1 flex-1 overflow-hidden rounded-full bg-white/20", children: /* @__PURE__ */ jsx38("div", { className: "h-1 rounded-full bg-white/85", style: { width: `${Math.min(100, Math.max(0, progress))}%` } }) }),
|
|
2542
|
+
progressLabel && /* @__PURE__ */ jsx38("span", { className: "shrink-0 text-[11px] font-semibold text-white/80 tabular-nums", children: progressLabel })
|
|
2246
2543
|
] }),
|
|
2247
|
-
sparkline && sparkline.length > 1 && !loading && /* @__PURE__ */
|
|
2544
|
+
sparkline && sparkline.length > 1 && !loading && /* @__PURE__ */ jsx38("div", { className: "relative -mx-4 mt-auto -mb-4 md:-mx-3 md:-mb-3", children: /* @__PURE__ */ jsx38(AreaSparkline, { data: sparkline, color: "#ffffff" }) })
|
|
2248
2545
|
]
|
|
2249
2546
|
}
|
|
2250
2547
|
);
|
|
2251
2548
|
}
|
|
2252
2549
|
if (variant === "mini") {
|
|
2253
|
-
const iconSlot = Icon2 ? href ? /* @__PURE__ */
|
|
2550
|
+
const iconSlot = Icon2 ? href ? /* @__PURE__ */ jsx38(
|
|
2254
2551
|
"a",
|
|
2255
2552
|
{
|
|
2256
2553
|
href,
|
|
2257
2554
|
className: "tap-target flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary transition-[background-color,scale] hover:bg-primary/20 active:scale-90 motion-reduce:transition-none md:h-6 md:w-6",
|
|
2258
|
-
children: /* @__PURE__ */
|
|
2555
|
+
children: /* @__PURE__ */ jsx38(Icon2, { className: "h-4 w-4 md:h-3.5 md:w-3.5", "aria-hidden": "true" })
|
|
2259
2556
|
}
|
|
2260
|
-
) : /* @__PURE__ */
|
|
2261
|
-
return /* @__PURE__ */
|
|
2557
|
+
) : /* @__PURE__ */ jsx38("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary md:h-6 md:w-6", children: /* @__PURE__ */ jsx38(Icon2, { className: "h-4 w-4 md:h-3.5 md:w-3.5", "aria-hidden": "true" }) }) : null;
|
|
2558
|
+
return /* @__PURE__ */ jsxs21(
|
|
2262
2559
|
"div",
|
|
2263
2560
|
{
|
|
2264
2561
|
"data-testid": testId,
|
|
@@ -2268,13 +2565,13 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2268
2565
|
href && "hover:border-primary/20 hover:shadow-md"
|
|
2269
2566
|
),
|
|
2270
2567
|
children: [
|
|
2271
|
-
/* @__PURE__ */
|
|
2272
|
-
/* @__PURE__ */
|
|
2568
|
+
/* @__PURE__ */ jsxs21("div", { className: rowBetween, children: [
|
|
2569
|
+
/* @__PURE__ */ jsx38("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
|
|
2273
2570
|
iconSlot
|
|
2274
2571
|
] }),
|
|
2275
|
-
/* @__PURE__ */
|
|
2276
|
-
/* @__PURE__ */
|
|
2277
|
-
badge && /* @__PURE__ */
|
|
2572
|
+
/* @__PURE__ */ jsx38("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: loading ? /* @__PURE__ */ jsx38(Skeleton, { className: "h-6 w-16" }) : /* @__PURE__ */ jsxs21(Fragment4, { children: [
|
|
2573
|
+
/* @__PURE__ */ jsx38("p", { className: "min-w-0 animate-in truncate text-[20px] leading-none font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none md:text-[16px]", children: value ?? "\u2014" }),
|
|
2574
|
+
badge && /* @__PURE__ */ jsx38(
|
|
2278
2575
|
"span",
|
|
2279
2576
|
{
|
|
2280
2577
|
className: cn(
|
|
@@ -2285,8 +2582,8 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2285
2582
|
}
|
|
2286
2583
|
)
|
|
2287
2584
|
] }) }),
|
|
2288
|
-
(sub || sparkline) && !loading && /* @__PURE__ */
|
|
2289
|
-
sub && /* @__PURE__ */
|
|
2585
|
+
(sub || sparkline) && !loading && /* @__PURE__ */ jsxs21("div", { className: "flex items-end justify-between gap-2", children: [
|
|
2586
|
+
sub && /* @__PURE__ */ jsx38(
|
|
2290
2587
|
"p",
|
|
2291
2588
|
{
|
|
2292
2589
|
className: cn(
|
|
@@ -2296,7 +2593,7 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2296
2593
|
children: sub
|
|
2297
2594
|
}
|
|
2298
2595
|
),
|
|
2299
|
-
sparkline && /* @__PURE__ */
|
|
2596
|
+
sparkline && /* @__PURE__ */ jsx38(Sparkline, { data: sparkline, color: sparklineColor })
|
|
2300
2597
|
] })
|
|
2301
2598
|
]
|
|
2302
2599
|
}
|
|
@@ -2304,22 +2601,22 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2304
2601
|
}
|
|
2305
2602
|
if (variant === "donut") {
|
|
2306
2603
|
const total = segments?.reduce((sum, s) => sum + s.value, 0) ?? 0;
|
|
2307
|
-
return /* @__PURE__ */
|
|
2308
|
-
/* @__PURE__ */
|
|
2309
|
-
/* @__PURE__ */
|
|
2310
|
-
Icon2 && /* @__PURE__ */
|
|
2604
|
+
return /* @__PURE__ */ jsxs21("div", { "data-testid": testId, style: bgStyle, className: "rounded-xl border border-border bg-card p-5", children: [
|
|
2605
|
+
/* @__PURE__ */ jsxs21("div", { className: rowBetween, children: [
|
|
2606
|
+
/* @__PURE__ */ jsx38("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
|
|
2607
|
+
Icon2 && /* @__PURE__ */ jsx38("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx38(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
|
|
2311
2608
|
] }),
|
|
2312
|
-
loading ? /* @__PURE__ */
|
|
2313
|
-
/* @__PURE__ */
|
|
2314
|
-
/* @__PURE__ */
|
|
2315
|
-
/* @__PURE__ */
|
|
2316
|
-
/* @__PURE__ */
|
|
2317
|
-
/* @__PURE__ */
|
|
2609
|
+
loading ? /* @__PURE__ */ jsxs21("div", { className: "mt-3 flex items-center gap-4", children: [
|
|
2610
|
+
/* @__PURE__ */ jsx38(Skeleton, { className: "h-24 w-24 shrink-0 rounded-full" }),
|
|
2611
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex flex-1 flex-col gap-2", children: [
|
|
2612
|
+
/* @__PURE__ */ jsx38(Skeleton, { className: "h-3 w-full" }),
|
|
2613
|
+
/* @__PURE__ */ jsx38(Skeleton, { className: "h-3 w-3/4" }),
|
|
2614
|
+
/* @__PURE__ */ jsx38(Skeleton, { className: "h-3 w-1/2" })
|
|
2318
2615
|
] })
|
|
2319
|
-
] }) : segments && segments.length > 0 ? /* @__PURE__ */
|
|
2320
|
-
/* @__PURE__ */
|
|
2321
|
-
/* @__PURE__ */
|
|
2322
|
-
/* @__PURE__ */
|
|
2616
|
+
] }) : segments && segments.length > 0 ? /* @__PURE__ */ jsxs21("div", { className: "mt-3 flex items-center gap-4", children: [
|
|
2617
|
+
/* @__PURE__ */ jsx38(DonutChart, { data: segments, centerValue: centerValue ?? String(total) }),
|
|
2618
|
+
/* @__PURE__ */ jsx38("ul", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: segments.map((segment, i) => /* @__PURE__ */ jsxs21("li", { className: "flex items-center gap-2 text-[12px]", children: [
|
|
2619
|
+
/* @__PURE__ */ jsx38(
|
|
2323
2620
|
"span",
|
|
2324
2621
|
{
|
|
2325
2622
|
className: "h-2 w-2 shrink-0 rounded-full",
|
|
@@ -2327,11 +2624,11 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2327
2624
|
"aria-hidden": "true"
|
|
2328
2625
|
}
|
|
2329
2626
|
),
|
|
2330
|
-
/* @__PURE__ */
|
|
2331
|
-
/* @__PURE__ */
|
|
2627
|
+
/* @__PURE__ */ jsx38("span", { className: "min-w-0 flex-1 truncate text-muted-foreground", children: segment.label }),
|
|
2628
|
+
/* @__PURE__ */ jsx38("span", { className: "shrink-0 font-semibold text-foreground tabular-nums", children: total > 0 ? `${Math.round(segment.value / total * 100)}%` : segment.value })
|
|
2332
2629
|
] }, segment.label)) })
|
|
2333
|
-
] }) : /* @__PURE__ */
|
|
2334
|
-
sub && !loading && /* @__PURE__ */
|
|
2630
|
+
] }) : /* @__PURE__ */ jsx38("span", { className: "mt-3 block text-2xl font-black text-muted-foreground/40", children: "\u2014" }),
|
|
2631
|
+
sub && !loading && /* @__PURE__ */ jsx38("p", { className: "mt-2.5 text-[11px] text-muted-foreground tabular-nums", children: sub })
|
|
2335
2632
|
] });
|
|
2336
2633
|
}
|
|
2337
2634
|
const strValue = value != null ? String(value) : null;
|
|
@@ -2341,28 +2638,28 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2341
2638
|
const prefix = isSuffix ? null : parts?.[0];
|
|
2342
2639
|
const number = isSuffix ? parts[0] : parts?.[1] ?? parts?.[0];
|
|
2343
2640
|
const suffix = isSuffix ? parts[1] : null;
|
|
2344
|
-
return /* @__PURE__ */
|
|
2345
|
-
/* @__PURE__ */
|
|
2346
|
-
/* @__PURE__ */
|
|
2347
|
-
Icon2 && /* @__PURE__ */
|
|
2641
|
+
return /* @__PURE__ */ jsxs21("div", { "data-testid": testId, style: bgStyle, className: "rounded-xl border border-border bg-card p-5", children: [
|
|
2642
|
+
/* @__PURE__ */ jsxs21("div", { className: rowBetween, children: [
|
|
2643
|
+
/* @__PURE__ */ jsx38("p", { className: "text-[11px] font-bold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
|
|
2644
|
+
Icon2 && /* @__PURE__ */ jsx38("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary/8 text-primary", children: /* @__PURE__ */ jsx38(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })
|
|
2348
2645
|
] }),
|
|
2349
|
-
/* @__PURE__ */
|
|
2350
|
-
loading ? /* @__PURE__ */
|
|
2351
|
-
prefix && /* @__PURE__ */
|
|
2646
|
+
/* @__PURE__ */ jsxs21("div", { className: "mt-3", children: [
|
|
2647
|
+
loading ? /* @__PURE__ */ jsx38(Skeleton, { className: "h-7 w-20" }) : value == null ? /* @__PURE__ */ jsx38("span", { className: "text-2xl font-black text-muted-foreground/40", children: "\u2014" }) : plain ? /* @__PURE__ */ jsx38("span", { className: "animate-in text-2xl font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none", children: strValue }) : /* @__PURE__ */ jsxs21("span", { className: "animate-in font-black tracking-tight text-foreground duration-200 fade-in-0 tabular-nums motion-reduce:animate-none", children: [
|
|
2648
|
+
prefix && /* @__PURE__ */ jsxs21("span", { className: "text-sm text-muted-foreground", children: [
|
|
2352
2649
|
prefix,
|
|
2353
2650
|
" "
|
|
2354
2651
|
] }),
|
|
2355
|
-
/* @__PURE__ */
|
|
2356
|
-
suffix && /* @__PURE__ */
|
|
2652
|
+
/* @__PURE__ */ jsx38("span", { className: "text-2xl", children: number }),
|
|
2653
|
+
suffix && /* @__PURE__ */ jsxs21("span", { className: "text-sm text-muted-foreground", children: [
|
|
2357
2654
|
" ",
|
|
2358
2655
|
suffix
|
|
2359
2656
|
] })
|
|
2360
2657
|
] }),
|
|
2361
|
-
sub && !loading && /* @__PURE__ */
|
|
2658
|
+
sub && !loading && /* @__PURE__ */ jsx38("p", { className: "mt-0.5 text-[11px] text-muted-foreground tabular-nums", children: sub })
|
|
2362
2659
|
] }),
|
|
2363
|
-
(badge || badgeLabel || sparkline) && !loading && /* @__PURE__ */
|
|
2364
|
-
/* @__PURE__ */
|
|
2365
|
-
badge && /* @__PURE__ */
|
|
2660
|
+
(badge || badgeLabel || sparkline) && !loading && /* @__PURE__ */ jsxs21("div", { className: "mt-2.5 flex items-end justify-between gap-2", children: [
|
|
2661
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
2662
|
+
badge && /* @__PURE__ */ jsx38(
|
|
2366
2663
|
"span",
|
|
2367
2664
|
{
|
|
2368
2665
|
className: cn(
|
|
@@ -2372,20 +2669,23 @@ var KpiCard = memo(function KpiCard2({
|
|
|
2372
2669
|
children: badge.text
|
|
2373
2670
|
}
|
|
2374
2671
|
),
|
|
2375
|
-
badgeLabel && /* @__PURE__ */
|
|
2672
|
+
badgeLabel && /* @__PURE__ */ jsx38("span", { className: "text-[11px] text-muted-foreground", children: badgeLabel })
|
|
2376
2673
|
] }),
|
|
2377
|
-
sparkline && /* @__PURE__ */
|
|
2674
|
+
sparkline && /* @__PURE__ */ jsx38(Sparkline, { data: sparkline, color: sparklineColor })
|
|
2378
2675
|
] })
|
|
2379
2676
|
] });
|
|
2380
2677
|
});
|
|
2381
2678
|
|
|
2382
2679
|
// src/patterns/search-panel/SearchPanel.tsx
|
|
2383
|
-
import {
|
|
2384
|
-
import { jsx as
|
|
2680
|
+
import { ExclamationTriangleIcon as ExclamationTriangleIcon5, MagnifyingGlassIcon as MagnifyingGlassIcon2 } from "@heroicons/react/24/outline";
|
|
2681
|
+
import { jsx as jsx39, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2385
2682
|
function SearchPanel({
|
|
2386
2683
|
value,
|
|
2387
2684
|
onChange,
|
|
2685
|
+
loading,
|
|
2388
2686
|
isLoading,
|
|
2687
|
+
error,
|
|
2688
|
+
onRetry,
|
|
2389
2689
|
trailingSlot,
|
|
2390
2690
|
shortcutHint,
|
|
2391
2691
|
modes,
|
|
@@ -2396,7 +2696,9 @@ function SearchPanel({
|
|
|
2396
2696
|
className,
|
|
2397
2697
|
...props
|
|
2398
2698
|
}) {
|
|
2399
|
-
|
|
2699
|
+
const busy = loading ?? isLoading;
|
|
2700
|
+
const hasError = toErrorInfo(error) !== void 0;
|
|
2701
|
+
return /* @__PURE__ */ jsxs22(
|
|
2400
2702
|
"div",
|
|
2401
2703
|
{
|
|
2402
2704
|
className: cn(
|
|
@@ -2405,9 +2707,9 @@ function SearchPanel({
|
|
|
2405
2707
|
className
|
|
2406
2708
|
),
|
|
2407
2709
|
children: [
|
|
2408
|
-
/* @__PURE__ */
|
|
2409
|
-
|
|
2410
|
-
/* @__PURE__ */
|
|
2710
|
+
/* @__PURE__ */ jsxs22("div", { className: "flex h-14 items-center gap-3 px-4", children: [
|
|
2711
|
+
busy ? /* @__PURE__ */ jsx39(LoadingSpinner, { size: "sm" }) : hasError ? /* @__PURE__ */ jsx39(ExclamationTriangleIcon5, { className: "h-5 w-5 shrink-0 text-destructive", "aria-hidden": "true" }) : /* @__PURE__ */ jsx39(MagnifyingGlassIcon2, { className: "h-5 w-5 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
|
|
2712
|
+
/* @__PURE__ */ jsx39(
|
|
2411
2713
|
"input",
|
|
2412
2714
|
{
|
|
2413
2715
|
ref: inputRef,
|
|
@@ -2419,7 +2721,7 @@ function SearchPanel({
|
|
|
2419
2721
|
}
|
|
2420
2722
|
),
|
|
2421
2723
|
trailingSlot,
|
|
2422
|
-
shortcutHint && /* @__PURE__ */
|
|
2724
|
+
shortcutHint && /* @__PURE__ */ jsx39(
|
|
2423
2725
|
"kbd",
|
|
2424
2726
|
{
|
|
2425
2727
|
"aria-hidden": "true",
|
|
@@ -2428,7 +2730,8 @@ function SearchPanel({
|
|
|
2428
2730
|
}
|
|
2429
2731
|
)
|
|
2430
2732
|
] }),
|
|
2431
|
-
|
|
2733
|
+
hasError && /* @__PURE__ */ jsx39("div", { className: "px-4 pb-3", children: /* @__PURE__ */ jsx39(InlineError, { error, onRetry }) }),
|
|
2734
|
+
modes && modes.length > 0 && /* @__PURE__ */ jsx39("div", { className: "flex items-center gap-2 px-4 pb-3", children: modes.map(({ key, label, icon: Icon2 }) => /* @__PURE__ */ jsxs22(
|
|
2432
2735
|
"button",
|
|
2433
2736
|
{
|
|
2434
2737
|
type: "button",
|
|
@@ -2438,7 +2741,7 @@ function SearchPanel({
|
|
|
2438
2741
|
activeMode === key ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"
|
|
2439
2742
|
),
|
|
2440
2743
|
children: [
|
|
2441
|
-
Icon2 && /* @__PURE__ */
|
|
2744
|
+
Icon2 && /* @__PURE__ */ jsx39(Icon2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2442
2745
|
label
|
|
2443
2746
|
]
|
|
2444
2747
|
},
|
|
@@ -2450,10 +2753,10 @@ function SearchPanel({
|
|
|
2450
2753
|
}
|
|
2451
2754
|
|
|
2452
2755
|
// src/patterns/search-panel/SearchTrigger.tsx
|
|
2453
|
-
import {
|
|
2454
|
-
import { jsx as
|
|
2756
|
+
import { MagnifyingGlassIcon as MagnifyingGlassIcon3 } from "@heroicons/react/24/outline";
|
|
2757
|
+
import { jsx as jsx40 } from "react/jsx-runtime";
|
|
2455
2758
|
function SearchTrigger({ label = "Search", ...props }) {
|
|
2456
|
-
return /* @__PURE__ */
|
|
2759
|
+
return /* @__PURE__ */ jsx40(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": label, ...props, children: /* @__PURE__ */ jsx40(MagnifyingGlassIcon3, { className: "h-[18px] w-[18px]", "aria-hidden": "true" }) });
|
|
2457
2760
|
}
|
|
2458
2761
|
|
|
2459
2762
|
// src/patterns/sidebar/context.ts
|
|
@@ -2465,7 +2768,7 @@ function useSidebarCollapsed() {
|
|
|
2465
2768
|
}
|
|
2466
2769
|
|
|
2467
2770
|
// src/patterns/sidebar/NavItem.tsx
|
|
2468
|
-
import { jsx as
|
|
2771
|
+
import { jsx as jsx41, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2469
2772
|
function NavItem({
|
|
2470
2773
|
as,
|
|
2471
2774
|
icon: Icon2,
|
|
@@ -2477,7 +2780,7 @@ function NavItem({
|
|
|
2477
2780
|
}) {
|
|
2478
2781
|
const collapsed = useSidebarCollapsed();
|
|
2479
2782
|
const Comp = as ?? "a";
|
|
2480
|
-
const row = /* @__PURE__ */
|
|
2783
|
+
const row = /* @__PURE__ */ jsxs23(
|
|
2481
2784
|
Comp,
|
|
2482
2785
|
{
|
|
2483
2786
|
className: cn(
|
|
@@ -2489,7 +2792,7 @@ function NavItem({
|
|
|
2489
2792
|
),
|
|
2490
2793
|
...rest,
|
|
2491
2794
|
children: [
|
|
2492
|
-
Icon2 && /* @__PURE__ */
|
|
2795
|
+
Icon2 && /* @__PURE__ */ jsx41(
|
|
2493
2796
|
Icon2,
|
|
2494
2797
|
{
|
|
2495
2798
|
className: cn("h-[18px] w-[18px] shrink-0", active ? "text-nav-primary" : "text-muted-foreground"),
|
|
@@ -2497,7 +2800,7 @@ function NavItem({
|
|
|
2497
2800
|
"aria-hidden": true
|
|
2498
2801
|
}
|
|
2499
2802
|
),
|
|
2500
|
-
/* @__PURE__ */
|
|
2803
|
+
/* @__PURE__ */ jsx41(
|
|
2501
2804
|
"span",
|
|
2502
2805
|
{
|
|
2503
2806
|
className: cn(
|
|
@@ -2512,9 +2815,9 @@ function NavItem({
|
|
|
2512
2815
|
}
|
|
2513
2816
|
);
|
|
2514
2817
|
if (!collapsed) return row;
|
|
2515
|
-
return /* @__PURE__ */
|
|
2516
|
-
/* @__PURE__ */
|
|
2517
|
-
/* @__PURE__ */
|
|
2818
|
+
return /* @__PURE__ */ jsxs23(Tooltip, { children: [
|
|
2819
|
+
/* @__PURE__ */ jsx41(TooltipTrigger, { asChild: true, children: row }),
|
|
2820
|
+
/* @__PURE__ */ jsxs23(TooltipContent, { side: "right", className: "flex items-center gap-1.5", children: [
|
|
2518
2821
|
label,
|
|
2519
2822
|
badge
|
|
2520
2823
|
] })
|
|
@@ -2522,9 +2825,9 @@ function NavItem({
|
|
|
2522
2825
|
}
|
|
2523
2826
|
|
|
2524
2827
|
// src/patterns/sidebar/NavGroup.tsx
|
|
2525
|
-
import {
|
|
2526
|
-
import { useState as
|
|
2527
|
-
import { jsx as
|
|
2828
|
+
import { ChevronDownIcon as ChevronDownIcon2 } from "@heroicons/react/24/outline";
|
|
2829
|
+
import { useState as useState11 } from "react";
|
|
2830
|
+
import { jsx as jsx42, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2528
2831
|
function NavGroup({
|
|
2529
2832
|
label,
|
|
2530
2833
|
defaultCollapsed = false,
|
|
@@ -2533,15 +2836,15 @@ function NavGroup({
|
|
|
2533
2836
|
children,
|
|
2534
2837
|
className
|
|
2535
2838
|
}) {
|
|
2536
|
-
const [internalCollapsed, setInternalCollapsed] =
|
|
2839
|
+
const [internalCollapsed, setInternalCollapsed] = useState11(defaultCollapsed);
|
|
2537
2840
|
const groupCollapsed = collapsed ?? internalCollapsed;
|
|
2538
2841
|
const railCollapsed = useSidebarCollapsed();
|
|
2539
2842
|
const setGroupCollapsed = (next) => {
|
|
2540
2843
|
onCollapsedChange?.(next);
|
|
2541
2844
|
if (collapsed === void 0) setInternalCollapsed(next);
|
|
2542
2845
|
};
|
|
2543
|
-
return /* @__PURE__ */
|
|
2544
|
-
label && (railCollapsed ? /* @__PURE__ */
|
|
2846
|
+
return /* @__PURE__ */ jsxs24("div", { className: cn("flex flex-col gap-0.5", className), children: [
|
|
2847
|
+
label && (railCollapsed ? /* @__PURE__ */ jsx42("div", { className: "mx-2 my-1 h-px bg-border/50", "aria-hidden": true }) : /* @__PURE__ */ jsxs24(
|
|
2545
2848
|
"button",
|
|
2546
2849
|
{
|
|
2547
2850
|
type: "button",
|
|
@@ -2549,9 +2852,9 @@ function NavGroup({
|
|
|
2549
2852
|
"aria-expanded": !groupCollapsed,
|
|
2550
2853
|
className: "group flex w-full items-center justify-between px-2.5 py-0.5 text-[11px] font-medium text-muted-foreground/80 hover:text-foreground",
|
|
2551
2854
|
children: [
|
|
2552
|
-
/* @__PURE__ */
|
|
2553
|
-
/* @__PURE__ */
|
|
2554
|
-
|
|
2855
|
+
/* @__PURE__ */ jsx42("span", { children: label }),
|
|
2856
|
+
/* @__PURE__ */ jsx42(
|
|
2857
|
+
ChevronDownIcon2,
|
|
2555
2858
|
{
|
|
2556
2859
|
className: cn(
|
|
2557
2860
|
"h-4 w-4 opacity-40 transition-transform group-hover:opacity-100",
|
|
@@ -2563,19 +2866,19 @@ function NavGroup({
|
|
|
2563
2866
|
]
|
|
2564
2867
|
}
|
|
2565
2868
|
)),
|
|
2566
|
-
!groupCollapsed && /* @__PURE__ */
|
|
2869
|
+
!groupCollapsed && /* @__PURE__ */ jsx42("div", { className: "flex flex-col gap-0.5", children })
|
|
2567
2870
|
] });
|
|
2568
2871
|
}
|
|
2569
2872
|
|
|
2570
2873
|
// src/patterns/sidebar/Sidebar.tsx
|
|
2571
|
-
import {
|
|
2874
|
+
import { ChevronDoubleLeftIcon, ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
|
|
2572
2875
|
import {
|
|
2573
|
-
useCallback,
|
|
2574
|
-
useEffect as
|
|
2575
|
-
useRef as
|
|
2576
|
-
useState as
|
|
2876
|
+
useCallback as useCallback2,
|
|
2877
|
+
useEffect as useEffect6,
|
|
2878
|
+
useRef as useRef6,
|
|
2879
|
+
useState as useState12
|
|
2577
2880
|
} from "react";
|
|
2578
|
-
import { Fragment as
|
|
2881
|
+
import { Fragment as Fragment5, jsx as jsx43, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2579
2882
|
function resolve(value, collapsed) {
|
|
2580
2883
|
return typeof value === "function" ? value(collapsed) : value;
|
|
2581
2884
|
}
|
|
@@ -2594,10 +2897,10 @@ function Sidebar({
|
|
|
2594
2897
|
children,
|
|
2595
2898
|
className
|
|
2596
2899
|
}) {
|
|
2597
|
-
const [internalWidth, setInternalWidth] =
|
|
2900
|
+
const [internalWidth, setInternalWidth] = useState12(defaultWidth);
|
|
2598
2901
|
const currentWidth = width ?? internalWidth;
|
|
2599
|
-
const [isDragging, setIsDragging] =
|
|
2600
|
-
const setWidth =
|
|
2902
|
+
const [isDragging, setIsDragging] = useState12(false);
|
|
2903
|
+
const setWidth = useCallback2(
|
|
2601
2904
|
(next) => {
|
|
2602
2905
|
const clamped = Math.min(maxWidth, Math.max(minWidth, next));
|
|
2603
2906
|
onWidthChange?.(clamped);
|
|
@@ -2605,7 +2908,7 @@ function Sidebar({
|
|
|
2605
2908
|
},
|
|
2606
2909
|
[maxWidth, minWidth, onWidthChange, width]
|
|
2607
2910
|
);
|
|
2608
|
-
const dragStartRef =
|
|
2911
|
+
const dragStartRef = useRef6(null);
|
|
2609
2912
|
const handlePointerDown = (event) => {
|
|
2610
2913
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2611
2914
|
dragStartRef.current = { x: event.clientX, width: currentWidth };
|
|
@@ -2622,7 +2925,7 @@ function Sidebar({
|
|
|
2622
2925
|
dragStartRef.current = null;
|
|
2623
2926
|
setIsDragging(false);
|
|
2624
2927
|
};
|
|
2625
|
-
return /* @__PURE__ */
|
|
2928
|
+
return /* @__PURE__ */ jsx43(SidebarContextProvider, { value: { collapsed }, children: /* @__PURE__ */ jsx43(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs25(
|
|
2626
2929
|
"aside",
|
|
2627
2930
|
{
|
|
2628
2931
|
style: { width: collapsed ? railWidth : currentWidth },
|
|
@@ -2632,13 +2935,13 @@ function Sidebar({
|
|
|
2632
2935
|
className
|
|
2633
2936
|
),
|
|
2634
2937
|
children: [
|
|
2635
|
-
(header || onCollapsedChange) && /* @__PURE__ */
|
|
2938
|
+
(header || onCollapsedChange) && /* @__PURE__ */ jsx43("div", { className: "flex h-16 items-center gap-2 border-b border-border px-3", children: collapsed && onCollapsedChange ? (
|
|
2636
2939
|
// Collapsed rail: the header slot (e.g. a compact logo mark) and the
|
|
2637
2940
|
// expand toggle would otherwise compete for the narrow rail's width
|
|
2638
2941
|
// and clip each other, so they're stacked instead — the header shows
|
|
2639
2942
|
// by default and cross-fades to the toggle icon on hover, matching
|
|
2640
2943
|
// the same affordance OneSales' own sidebar rail uses.
|
|
2641
|
-
/* @__PURE__ */
|
|
2944
|
+
/* @__PURE__ */ jsxs25(
|
|
2642
2945
|
"button",
|
|
2643
2946
|
{
|
|
2644
2947
|
type: "button",
|
|
@@ -2646,9 +2949,9 @@ function Sidebar({
|
|
|
2646
2949
|
"aria-label": "Expand sidebar",
|
|
2647
2950
|
className: "group relative flex h-9 w-9 shrink-0 items-center justify-center",
|
|
2648
2951
|
children: [
|
|
2649
|
-
header && /* @__PURE__ */
|
|
2650
|
-
/* @__PURE__ */
|
|
2651
|
-
|
|
2952
|
+
header && /* @__PURE__ */ jsx43("span", { className: "transition-opacity group-hover:opacity-0", children: resolve(header, collapsed) }),
|
|
2953
|
+
/* @__PURE__ */ jsx43(
|
|
2954
|
+
ChevronDoubleRightIcon,
|
|
2652
2955
|
{
|
|
2653
2956
|
className: "absolute h-4 w-4 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100",
|
|
2654
2957
|
"aria-hidden": true
|
|
@@ -2657,22 +2960,22 @@ function Sidebar({
|
|
|
2657
2960
|
]
|
|
2658
2961
|
}
|
|
2659
2962
|
)
|
|
2660
|
-
) : /* @__PURE__ */
|
|
2661
|
-
header && /* @__PURE__ */
|
|
2662
|
-
onCollapsedChange && /* @__PURE__ */
|
|
2963
|
+
) : /* @__PURE__ */ jsxs25(Fragment5, { children: [
|
|
2964
|
+
header && /* @__PURE__ */ jsx43("div", { className: "min-w-0 flex-1", children: resolve(header, collapsed) }),
|
|
2965
|
+
onCollapsedChange && /* @__PURE__ */ jsx43(
|
|
2663
2966
|
"button",
|
|
2664
2967
|
{
|
|
2665
2968
|
type: "button",
|
|
2666
2969
|
onClick: () => onCollapsedChange(!collapsed),
|
|
2667
2970
|
"aria-label": "Collapse sidebar",
|
|
2668
2971
|
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
2669
|
-
children: /* @__PURE__ */
|
|
2972
|
+
children: /* @__PURE__ */ jsx43(ChevronDoubleLeftIcon, { className: "h-4 w-4", "aria-hidden": true })
|
|
2670
2973
|
}
|
|
2671
2974
|
)
|
|
2672
2975
|
] }) }),
|
|
2673
|
-
/* @__PURE__ */
|
|
2674
|
-
footer && /* @__PURE__ */
|
|
2675
|
-
resizable && !collapsed && /* @__PURE__ */
|
|
2976
|
+
/* @__PURE__ */ jsx43(SidebarNav, { children }),
|
|
2977
|
+
footer && /* @__PURE__ */ jsx43("div", { className: "border-t border-border/50 px-2 py-2", children: resolve(footer, collapsed) }),
|
|
2978
|
+
resizable && !collapsed && /* @__PURE__ */ jsx43(
|
|
2676
2979
|
"div",
|
|
2677
2980
|
{
|
|
2678
2981
|
onPointerDown: handlePointerDown,
|
|
@@ -2687,9 +2990,9 @@ function Sidebar({
|
|
|
2687
2990
|
) }) });
|
|
2688
2991
|
}
|
|
2689
2992
|
function SidebarNav({ children, className, ...rest }) {
|
|
2690
|
-
const ref =
|
|
2691
|
-
const [showFade, setShowFade] =
|
|
2692
|
-
|
|
2993
|
+
const ref = useRef6(null);
|
|
2994
|
+
const [showFade, setShowFade] = useState12(false);
|
|
2995
|
+
useEffect6(() => {
|
|
2693
2996
|
const el = ref.current;
|
|
2694
2997
|
if (!el) return;
|
|
2695
2998
|
const update = () => setShowFade(el.scrollHeight - el.scrollTop - el.clientHeight > 1);
|
|
@@ -2702,8 +3005,8 @@ function SidebarNav({ children, className, ...rest }) {
|
|
|
2702
3005
|
observer.disconnect();
|
|
2703
3006
|
};
|
|
2704
3007
|
}, []);
|
|
2705
|
-
return /* @__PURE__ */
|
|
2706
|
-
/* @__PURE__ */
|
|
3008
|
+
return /* @__PURE__ */ jsxs25("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
|
|
3009
|
+
/* @__PURE__ */ jsx43(
|
|
2707
3010
|
"div",
|
|
2708
3011
|
{
|
|
2709
3012
|
ref,
|
|
@@ -2712,7 +3015,7 @@ function SidebarNav({ children, className, ...rest }) {
|
|
|
2712
3015
|
children
|
|
2713
3016
|
}
|
|
2714
3017
|
),
|
|
2715
|
-
showFade && /* @__PURE__ */
|
|
3018
|
+
showFade && /* @__PURE__ */ jsx43(
|
|
2716
3019
|
"div",
|
|
2717
3020
|
{
|
|
2718
3021
|
className: "pointer-events-none absolute right-0 bottom-0 left-0 h-6 bg-gradient-to-t from-sidebar to-transparent",
|
|
@@ -2723,11 +3026,11 @@ function SidebarNav({ children, className, ...rest }) {
|
|
|
2723
3026
|
}
|
|
2724
3027
|
|
|
2725
3028
|
// src/patterns/ai/AiButton.tsx
|
|
2726
|
-
import {
|
|
3029
|
+
import { SparklesIcon } from "@heroicons/react/24/outline";
|
|
2727
3030
|
import { forwardRef as forwardRef17 } from "react";
|
|
2728
|
-
import { jsx as
|
|
3031
|
+
import { jsx as jsx44, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2729
3032
|
var AiButton = forwardRef17(
|
|
2730
|
-
({ variant = "ai-subtle", loading = false, icon: Icon2 =
|
|
3033
|
+
({ variant = "ai-subtle", loading = false, icon: Icon2 = SparklesIcon, disabled, children, ...props }, ref) => /* @__PURE__ */ jsxs26(
|
|
2731
3034
|
Button,
|
|
2732
3035
|
{
|
|
2733
3036
|
ref,
|
|
@@ -2736,7 +3039,7 @@ var AiButton = forwardRef17(
|
|
|
2736
3039
|
"aria-busy": loading || void 0,
|
|
2737
3040
|
...props,
|
|
2738
3041
|
children: [
|
|
2739
|
-
loading ? /* @__PURE__ */
|
|
3042
|
+
loading ? /* @__PURE__ */ jsx44(LoadingSpinner, { size: "sm", className: "[&_svg]:text-current" }) : /* @__PURE__ */ jsx44(Icon2, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
2740
3043
|
children
|
|
2741
3044
|
]
|
|
2742
3045
|
}
|
|
@@ -2745,9 +3048,9 @@ var AiButton = forwardRef17(
|
|
|
2745
3048
|
AiButton.displayName = "AiButton";
|
|
2746
3049
|
|
|
2747
3050
|
// src/patterns/ai/AiExplainButton.tsx
|
|
2748
|
-
import {
|
|
2749
|
-
import { useState as
|
|
2750
|
-
import { jsx as
|
|
3051
|
+
import { SparklesIcon as SparklesIcon2 } from "@heroicons/react/24/outline";
|
|
3052
|
+
import { useState as useState13 } from "react";
|
|
3053
|
+
import { jsx as jsx45, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2751
3054
|
function AiExplainButton({
|
|
2752
3055
|
onExplain,
|
|
2753
3056
|
label = "Explain",
|
|
@@ -2757,10 +3060,10 @@ function AiExplainButton({
|
|
|
2757
3060
|
size = "sm",
|
|
2758
3061
|
...props
|
|
2759
3062
|
}) {
|
|
2760
|
-
const [open, setOpen] =
|
|
2761
|
-
const [loading, setLoading] =
|
|
2762
|
-
const [content, setContent] =
|
|
2763
|
-
const [error, setError] =
|
|
3063
|
+
const [open, setOpen] = useState13(false);
|
|
3064
|
+
const [loading, setLoading] = useState13(false);
|
|
3065
|
+
const [content, setContent] = useState13(null);
|
|
3066
|
+
const [error, setError] = useState13(null);
|
|
2764
3067
|
const load = async () => {
|
|
2765
3068
|
setLoading(true);
|
|
2766
3069
|
setError(null);
|
|
@@ -2776,30 +3079,32 @@ function AiExplainButton({
|
|
|
2776
3079
|
setOpen(next);
|
|
2777
3080
|
if (next && content === null && error === null && !loading) void load();
|
|
2778
3081
|
};
|
|
2779
|
-
return /* @__PURE__ */
|
|
2780
|
-
/* @__PURE__ */
|
|
2781
|
-
/* @__PURE__ */
|
|
2782
|
-
/* @__PURE__ */
|
|
2783
|
-
/* @__PURE__ */
|
|
3082
|
+
return /* @__PURE__ */ jsxs27(Popover, { open, onOpenChange: handleOpenChange, children: [
|
|
3083
|
+
/* @__PURE__ */ jsx45(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx45(AiButton, { size, ...props, children: label }) }),
|
|
3084
|
+
/* @__PURE__ */ jsxs27(PopoverContent, { align, className: contentClassName, children: [
|
|
3085
|
+
/* @__PURE__ */ jsxs27("p", { className: "mb-2 flex items-center gap-1.5 text-sm font-semibold", children: [
|
|
3086
|
+
/* @__PURE__ */ jsx45(SparklesIcon2, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
|
|
2784
3087
|
title ?? label
|
|
2785
3088
|
] }),
|
|
2786
|
-
loading ? /* @__PURE__ */
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
3089
|
+
loading ? /* @__PURE__ */ jsx45(LoadingSpinner, { size: "sm", label: "Thinking\u2026", className: "text-sm text-muted-foreground" }) : error ? /* @__PURE__ */ jsx45(
|
|
3090
|
+
ErrorState,
|
|
3091
|
+
{
|
|
3092
|
+
variant: "compact",
|
|
3093
|
+
error,
|
|
3094
|
+
onRetry: () => void load(),
|
|
3095
|
+
labels: { title: "Something went wrong", retry: "Try again" }
|
|
3096
|
+
}
|
|
3097
|
+
) : /* @__PURE__ */ jsx45("div", { className: "max-h-80 overflow-y-auto text-sm leading-relaxed", children: content })
|
|
2793
3098
|
] })
|
|
2794
3099
|
] });
|
|
2795
3100
|
}
|
|
2796
3101
|
|
|
2797
3102
|
// src/patterns/ai/VoiceInputButton.tsx
|
|
2798
|
-
import {
|
|
3103
|
+
import { MicrophoneIcon, SlashIcon } from "@heroicons/react/24/outline";
|
|
2799
3104
|
import { forwardRef as forwardRef18 } from "react";
|
|
2800
3105
|
|
|
2801
3106
|
// src/patterns/ai/useSpeechRecognition.ts
|
|
2802
|
-
import { useCallback as
|
|
3107
|
+
import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef7, useState as useState14 } from "react";
|
|
2803
3108
|
function getCtor() {
|
|
2804
3109
|
if (typeof window === "undefined") return void 0;
|
|
2805
3110
|
const w = window;
|
|
@@ -2832,21 +3137,21 @@ function useSpeechRecognition({
|
|
|
2832
3137
|
onError,
|
|
2833
3138
|
engine
|
|
2834
3139
|
} = {}) {
|
|
2835
|
-
const [listening, setListening] =
|
|
2836
|
-
const [interim, setInterim] =
|
|
2837
|
-
const [error, setError] =
|
|
2838
|
-
const recognitionRef =
|
|
2839
|
-
const onResultRef =
|
|
2840
|
-
const onErrorRef =
|
|
2841
|
-
const engineRef =
|
|
3140
|
+
const [listening, setListening] = useState14(false);
|
|
3141
|
+
const [interim, setInterim] = useState14("");
|
|
3142
|
+
const [error, setError] = useState14(null);
|
|
3143
|
+
const recognitionRef = useRef7(null);
|
|
3144
|
+
const onResultRef = useRef7(onResult);
|
|
3145
|
+
const onErrorRef = useRef7(onError);
|
|
3146
|
+
const engineRef = useRef7(engine);
|
|
2842
3147
|
onResultRef.current = onResult;
|
|
2843
3148
|
onErrorRef.current = onError;
|
|
2844
3149
|
engineRef.current = engine;
|
|
2845
3150
|
const supported = engine !== void 0 || getCtor() !== void 0;
|
|
2846
|
-
const stop =
|
|
3151
|
+
const stop = useCallback3(() => {
|
|
2847
3152
|
recognitionRef.current?.stop();
|
|
2848
3153
|
}, []);
|
|
2849
|
-
const start =
|
|
3154
|
+
const start = useCallback3(() => {
|
|
2850
3155
|
if (recognitionRef.current) return;
|
|
2851
3156
|
const recognition = engineRef.current ? engineRef.current() : browserEngine();
|
|
2852
3157
|
if (!recognition) return;
|
|
@@ -2879,16 +3184,16 @@ function useSpeechRecognition({
|
|
|
2879
3184
|
setListening(true);
|
|
2880
3185
|
recognition.start();
|
|
2881
3186
|
}, [lang]);
|
|
2882
|
-
const toggle =
|
|
3187
|
+
const toggle = useCallback3(() => {
|
|
2883
3188
|
if (recognitionRef.current) stop();
|
|
2884
3189
|
else start();
|
|
2885
3190
|
}, [start, stop]);
|
|
2886
|
-
|
|
3191
|
+
useEffect7(() => () => recognitionRef.current?.abort(), []);
|
|
2887
3192
|
return { supported, listening, interim, error, start, stop, toggle };
|
|
2888
3193
|
}
|
|
2889
3194
|
|
|
2890
3195
|
// src/patterns/ai/VoiceInputButton.tsx
|
|
2891
|
-
import { jsx as
|
|
3196
|
+
import { jsx as jsx46, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2892
3197
|
var VoiceInputButton = forwardRef18(
|
|
2893
3198
|
({
|
|
2894
3199
|
onTranscript,
|
|
@@ -2914,8 +3219,8 @@ var VoiceInputButton = forwardRef18(
|
|
|
2914
3219
|
return (
|
|
2915
3220
|
// `title` lives on the wrapper, not the button: buttonVariants sets
|
|
2916
3221
|
// `disabled:pointer-events-none`, so a disabled button never shows its own.
|
|
2917
|
-
/* @__PURE__ */
|
|
2918
|
-
/* @__PURE__ */
|
|
3222
|
+
/* @__PURE__ */ jsxs28("span", { className: "inline-flex min-w-0 items-center gap-2", title: supported ? void 0 : unsupportedTitle, children: [
|
|
3223
|
+
/* @__PURE__ */ jsxs28(
|
|
2919
3224
|
Button,
|
|
2920
3225
|
{
|
|
2921
3226
|
ref,
|
|
@@ -2932,12 +3237,25 @@ var VoiceInputButton = forwardRef18(
|
|
|
2932
3237
|
),
|
|
2933
3238
|
...props,
|
|
2934
3239
|
children: [
|
|
2935
|
-
supported ? /* @__PURE__ */
|
|
3240
|
+
supported ? /* @__PURE__ */ jsx46(
|
|
3241
|
+
MicrophoneIcon,
|
|
3242
|
+
{
|
|
3243
|
+
className: cn("h-4 w-4", listening && "animate-pulse motion-reduce:animate-none"),
|
|
3244
|
+
"aria-hidden": "true"
|
|
3245
|
+
}
|
|
3246
|
+
) : (
|
|
3247
|
+
// heroicons has no muted-mic glyph -- MicrophoneIcon plus SlashIcon
|
|
3248
|
+
// (same 24x24 viewBox/stroke) layered on top approximates one.
|
|
3249
|
+
/* @__PURE__ */ jsxs28("span", { className: "relative inline-flex h-4 w-4", "aria-hidden": "true", children: [
|
|
3250
|
+
/* @__PURE__ */ jsx46(MicrophoneIcon, { className: "h-4 w-4" }),
|
|
3251
|
+
/* @__PURE__ */ jsx46(SlashIcon, { className: "absolute inset-0 h-4 w-4" })
|
|
3252
|
+
] })
|
|
3253
|
+
),
|
|
2936
3254
|
label
|
|
2937
3255
|
]
|
|
2938
3256
|
}
|
|
2939
3257
|
),
|
|
2940
|
-
showInterim && interim ? /* @__PURE__ */
|
|
3258
|
+
showInterim && interim ? /* @__PURE__ */ jsx46("span", { "aria-live": "polite", className: "truncate text-sm text-muted-foreground italic", children: interim }) : null
|
|
2941
3259
|
] })
|
|
2942
3260
|
);
|
|
2943
3261
|
}
|
|
@@ -2945,9 +3263,9 @@ var VoiceInputButton = forwardRef18(
|
|
|
2945
3263
|
VoiceInputButton.displayName = "VoiceInputButton";
|
|
2946
3264
|
|
|
2947
3265
|
// src/patterns/ai/VoiceTranscript.tsx
|
|
2948
|
-
import {
|
|
2949
|
-
import { useState as
|
|
2950
|
-
import { jsx as
|
|
3266
|
+
import { ArrowUturnLeftIcon, SparklesIcon as SparklesIcon3 } from "@heroicons/react/24/outline";
|
|
3267
|
+
import { useState as useState15 } from "react";
|
|
3268
|
+
import { jsx as jsx47, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2951
3269
|
function VoiceTranscript({
|
|
2952
3270
|
value,
|
|
2953
3271
|
onChange,
|
|
@@ -2961,9 +3279,9 @@ function VoiceTranscript({
|
|
|
2961
3279
|
className,
|
|
2962
3280
|
...props
|
|
2963
3281
|
}) {
|
|
2964
|
-
const [loading, setLoading] =
|
|
2965
|
-
const [error, setError] =
|
|
2966
|
-
const [previous, setPrevious] =
|
|
3282
|
+
const [loading, setLoading] = useState15(false);
|
|
3283
|
+
const [error, setError] = useState15(null);
|
|
3284
|
+
const [previous, setPrevious] = useState15(null);
|
|
2967
3285
|
const append = (chunk) => {
|
|
2968
3286
|
onChange(value ? `${value.trimEnd()} ${chunk}` : chunk);
|
|
2969
3287
|
};
|
|
@@ -2987,8 +3305,8 @@ function VoiceTranscript({
|
|
|
2987
3305
|
onChange(previous);
|
|
2988
3306
|
setPrevious(null);
|
|
2989
3307
|
};
|
|
2990
|
-
return /* @__PURE__ */
|
|
2991
|
-
/* @__PURE__ */
|
|
3308
|
+
return /* @__PURE__ */ jsxs29("div", { className: cn("flex flex-col gap-2", className), ...props, children: [
|
|
3309
|
+
/* @__PURE__ */ jsx47(
|
|
2992
3310
|
Textarea,
|
|
2993
3311
|
{
|
|
2994
3312
|
value,
|
|
@@ -2999,8 +3317,8 @@ function VoiceTranscript({
|
|
|
2999
3317
|
"aria-busy": loading || void 0
|
|
3000
3318
|
}
|
|
3001
3319
|
),
|
|
3002
|
-
/* @__PURE__ */
|
|
3003
|
-
/* @__PURE__ */
|
|
3320
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
3321
|
+
/* @__PURE__ */ jsx47(
|
|
3004
3322
|
VoiceInputButton,
|
|
3005
3323
|
{
|
|
3006
3324
|
onTranscript: append,
|
|
@@ -3013,35 +3331,35 @@ function VoiceTranscript({
|
|
|
3013
3331
|
disabled: disabled || loading
|
|
3014
3332
|
}
|
|
3015
3333
|
),
|
|
3016
|
-
onTransform ? /* @__PURE__ */
|
|
3334
|
+
onTransform ? /* @__PURE__ */ jsx47(
|
|
3017
3335
|
AiButton,
|
|
3018
3336
|
{
|
|
3019
3337
|
variant: "ai",
|
|
3020
3338
|
size: "sm",
|
|
3021
|
-
icon:
|
|
3339
|
+
icon: SparklesIcon3,
|
|
3022
3340
|
loading,
|
|
3023
3341
|
disabled: disabled || value.trim().length === 0,
|
|
3024
3342
|
onClick: () => void transform(),
|
|
3025
3343
|
children: transformLabel
|
|
3026
3344
|
}
|
|
3027
3345
|
) : null,
|
|
3028
|
-
previous !== null && !loading ? /* @__PURE__ */
|
|
3029
|
-
/* @__PURE__ */
|
|
3346
|
+
previous !== null && !loading ? /* @__PURE__ */ jsxs29(Button, { type: "button", variant: "ghost", size: "sm", onClick: undo, children: [
|
|
3347
|
+
/* @__PURE__ */ jsx47(ArrowUturnLeftIcon, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
3030
3348
|
"Undo"
|
|
3031
3349
|
] }) : null
|
|
3032
3350
|
] }),
|
|
3033
3351
|
error ? (
|
|
3034
3352
|
// AlertBox has no `role` prop, so the live region wraps it; same shape
|
|
3035
3353
|
// a consuming app's own form errors take.
|
|
3036
|
-
/* @__PURE__ */
|
|
3354
|
+
/* @__PURE__ */ jsx47("div", { role: "alert", children: /* @__PURE__ */ jsx47(AlertBox, { variant: "error", children: error }) })
|
|
3037
3355
|
) : null
|
|
3038
3356
|
] });
|
|
3039
3357
|
}
|
|
3040
3358
|
|
|
3041
3359
|
// src/patterns/stepper/Stepper.tsx
|
|
3042
|
-
import { useEffect as
|
|
3043
|
-
import {
|
|
3044
|
-
import { jsx as
|
|
3360
|
+
import { useEffect as useEffect8, useRef as useRef8 } from "react";
|
|
3361
|
+
import { CheckIcon as CheckIcon5, PlusIcon } from "@heroicons/react/24/outline";
|
|
3362
|
+
import { jsx as jsx48, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
3045
3363
|
function Stepper({
|
|
3046
3364
|
step,
|
|
3047
3365
|
maxStep,
|
|
@@ -3053,8 +3371,8 @@ function Stepper({
|
|
|
3053
3371
|
scrollTestId = "stepper-scroll",
|
|
3054
3372
|
newTestId
|
|
3055
3373
|
}) {
|
|
3056
|
-
const stepRefs =
|
|
3057
|
-
|
|
3374
|
+
const stepRefs = useRef8(/* @__PURE__ */ new Map());
|
|
3375
|
+
useEffect8(() => {
|
|
3058
3376
|
const activeStep = stepRefs.current.get(step);
|
|
3059
3377
|
activeStep?.scrollIntoView({ block: "nearest", inline: "center" });
|
|
3060
3378
|
}, [step]);
|
|
@@ -3062,19 +3380,19 @@ function Stepper({
|
|
|
3062
3380
|
// onNew is pinned outside the horizontally-scrolling region below (a sibling of
|
|
3063
3381
|
// it, not a child) so its own edge never scrolls off-screen along with the
|
|
3064
3382
|
// later steps once the scroller's min-width doesn't fit the viewport.
|
|
3065
|
-
/* @__PURE__ */
|
|
3066
|
-
/* @__PURE__ */
|
|
3383
|
+
/* @__PURE__ */ jsxs30("div", { className: "flex w-full shrink-0 items-center gap-2", children: [
|
|
3384
|
+
/* @__PURE__ */ jsx48("nav", { "aria-label": navLabel, className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx48(
|
|
3067
3385
|
"div",
|
|
3068
3386
|
{
|
|
3069
3387
|
"data-testid": scrollTestId,
|
|
3070
3388
|
className: "flex w-full items-center gap-0 overflow-x-auto px-2 py-1",
|
|
3071
|
-
children: /* @__PURE__ */
|
|
3389
|
+
children: /* @__PURE__ */ jsx48("ol", { className: "flex min-w-[700px] flex-1 items-start", children: steps.map(({ n, label, phase }, i) => {
|
|
3072
3390
|
const done = step > n;
|
|
3073
3391
|
const active = step === n;
|
|
3074
3392
|
const navigable = n <= maxStep && n !== step;
|
|
3075
3393
|
const last = i === steps.length - 1;
|
|
3076
|
-
return /* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
3394
|
+
return /* @__PURE__ */ jsxs30("li", { className: cn("flex items-start", !last && "flex-1"), children: [
|
|
3395
|
+
/* @__PURE__ */ jsxs30(
|
|
3078
3396
|
"button",
|
|
3079
3397
|
{
|
|
3080
3398
|
ref: (element) => {
|
|
@@ -3090,17 +3408,17 @@ function Stepper({
|
|
|
3090
3408
|
navigable ? "cursor-pointer hover:opacity-80" : "cursor-default"
|
|
3091
3409
|
),
|
|
3092
3410
|
children: [
|
|
3093
|
-
/* @__PURE__ */
|
|
3411
|
+
/* @__PURE__ */ jsx48(
|
|
3094
3412
|
"div",
|
|
3095
3413
|
{
|
|
3096
3414
|
className: cn(
|
|
3097
3415
|
"flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-[11px] font-bold transition-colors",
|
|
3098
3416
|
done ? "bg-primary/15 text-primary" : active ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"
|
|
3099
3417
|
),
|
|
3100
|
-
children: done ? /* @__PURE__ */
|
|
3418
|
+
children: done ? /* @__PURE__ */ jsx48(CheckIcon5, { className: "h-3 w-3" }) : n
|
|
3101
3419
|
}
|
|
3102
3420
|
),
|
|
3103
|
-
/* @__PURE__ */
|
|
3421
|
+
/* @__PURE__ */ jsx48(
|
|
3104
3422
|
"span",
|
|
3105
3423
|
{
|
|
3106
3424
|
className: cn(
|
|
@@ -3110,11 +3428,11 @@ function Stepper({
|
|
|
3110
3428
|
children: label
|
|
3111
3429
|
}
|
|
3112
3430
|
),
|
|
3113
|
-
/* @__PURE__ */
|
|
3431
|
+
/* @__PURE__ */ jsx48("span", { className: "text-[9px] tracking-[0.5px] text-muted-foreground/50 uppercase", children: phase })
|
|
3114
3432
|
]
|
|
3115
3433
|
}
|
|
3116
3434
|
),
|
|
3117
|
-
!last && /* @__PURE__ */
|
|
3435
|
+
!last && /* @__PURE__ */ jsx48(
|
|
3118
3436
|
"div",
|
|
3119
3437
|
{
|
|
3120
3438
|
className: cn(
|
|
@@ -3127,7 +3445,7 @@ function Stepper({
|
|
|
3127
3445
|
}) })
|
|
3128
3446
|
}
|
|
3129
3447
|
) }),
|
|
3130
|
-
onNew && /* @__PURE__ */
|
|
3448
|
+
onNew && /* @__PURE__ */ jsxs30(
|
|
3131
3449
|
"button",
|
|
3132
3450
|
{
|
|
3133
3451
|
"data-testid": newTestId,
|
|
@@ -3135,7 +3453,7 @@ function Stepper({
|
|
|
3135
3453
|
title: newLabel,
|
|
3136
3454
|
className: "flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-[12px] font-semibold text-foreground transition-colors hover:bg-muted/50",
|
|
3137
3455
|
children: [
|
|
3138
|
-
/* @__PURE__ */
|
|
3456
|
+
/* @__PURE__ */ jsx48(PlusIcon, { className: "h-3.5 w-3.5" }),
|
|
3139
3457
|
newLabel
|
|
3140
3458
|
]
|
|
3141
3459
|
}
|
|
@@ -3147,6 +3465,7 @@ export {
|
|
|
3147
3465
|
AiButton,
|
|
3148
3466
|
AiExplainButton,
|
|
3149
3467
|
AlertBox,
|
|
3468
|
+
AsyncBoundary,
|
|
3150
3469
|
Badge,
|
|
3151
3470
|
Button,
|
|
3152
3471
|
ButtonGroup,
|
|
@@ -3184,13 +3503,16 @@ export {
|
|
|
3184
3503
|
DropdownMenuSubContent,
|
|
3185
3504
|
DropdownMenuSubTrigger,
|
|
3186
3505
|
DropdownMenuTrigger,
|
|
3506
|
+
ErrorState,
|
|
3187
3507
|
FormField,
|
|
3188
3508
|
FormModal,
|
|
3509
|
+
InlineError,
|
|
3189
3510
|
Input,
|
|
3190
3511
|
KpiCard,
|
|
3191
3512
|
Label,
|
|
3192
3513
|
LoadingOverlay,
|
|
3193
3514
|
LoadingSpinner,
|
|
3515
|
+
LoadingState,
|
|
3194
3516
|
Modal,
|
|
3195
3517
|
NavGroup,
|
|
3196
3518
|
NavItem,
|
|
@@ -3199,6 +3521,7 @@ export {
|
|
|
3199
3521
|
PopoverContent,
|
|
3200
3522
|
PopoverTrigger,
|
|
3201
3523
|
QuestionDialog,
|
|
3524
|
+
RefreshBar,
|
|
3202
3525
|
ResponsivePanel,
|
|
3203
3526
|
ScrollArea,
|
|
3204
3527
|
ScrollBar,
|
|
@@ -3252,11 +3575,14 @@ export {
|
|
|
3252
3575
|
badgeVariants,
|
|
3253
3576
|
buttonVariants,
|
|
3254
3577
|
cn,
|
|
3578
|
+
defaultErrorStateLabels,
|
|
3255
3579
|
describeSpeechError,
|
|
3256
3580
|
getConfirmButtonPlacement,
|
|
3257
3581
|
isWindowsPlatform,
|
|
3258
3582
|
loadingSpinnerIconVariants,
|
|
3259
3583
|
sheetVariants,
|
|
3584
|
+
toErrorInfo,
|
|
3585
|
+
useDelayedFlag,
|
|
3260
3586
|
useMediaQuery,
|
|
3261
3587
|
useSidebarCollapsed,
|
|
3262
3588
|
useSpeechRecognition
|