@arqel-dev/ui 0.15.0 → 0.16.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/README.md +1 -1
- package/dist/{AreaChart-NMQ2RQGA.js → AreaChart-JAZ6LUYG.js} +8 -4
- package/dist/AreaChart-JAZ6LUYG.js.map +1 -0
- package/dist/ArqelCreatePage-UZAHV4DK.js +8 -0
- package/dist/{ArqelCreatePage-MV4JN2MO.js.map → ArqelCreatePage-UZAHV4DK.js.map} +1 -1
- package/dist/ArqelDashboardPage-Z5R4XSH4.js +6 -0
- package/dist/{ArqelDashboardPage-QK3BYVK3.js.map → ArqelDashboardPage-Z5R4XSH4.js.map} +1 -1
- package/dist/ArqelEditPage-RRYFYOTC.js +8 -0
- package/dist/{ArqelShowPage-5WWUSYC7.js.map → ArqelEditPage-RRYFYOTC.js.map} +1 -1
- package/dist/ArqelIndexPage-J2EUV2PI.js +12 -0
- package/dist/{ArqelIndexPage-R6JL5KOH.js.map → ArqelIndexPage-J2EUV2PI.js.map} +1 -1
- package/dist/ArqelShowPage-TVCJAV2F.js +6 -0
- package/dist/{ArqelEditPage-7J3IS2BR.js.map → ArqelShowPage-TVCJAV2F.js.map} +1 -1
- package/dist/{BarChart-KLHKTLO2.js → BarChart-E52NJSJE.js} +8 -4
- package/dist/BarChart-E52NJSJE.js.map +1 -0
- package/dist/{DonutChart-L56R4QF2.js → DonutChart-IGSF4LEB.js} +3 -3
- package/dist/{DonutChart-L56R4QF2.js.map → DonutChart-IGSF4LEB.js.map} +1 -1
- package/dist/{FormRenderer-BJBRIkfH.d.ts → FormRenderer-By8fZp4t.d.ts} +2 -0
- package/dist/{LineChart-GHMPHFZZ.js → LineChart-RRPGAZON.js} +8 -4
- package/dist/LineChart-RRPGAZON.js.map +1 -0
- package/dist/PieChart-SGN7V5LQ.js +4 -0
- package/dist/{PieChart-T2VQOFPP.js.map → PieChart-SGN7V5LQ.js.map} +1 -1
- package/dist/{RadarChart-CSWR5AO7.js → RadarChart-3AOM736J.js} +8 -4
- package/dist/RadarChart-3AOM736J.js.map +1 -0
- package/dist/{Topbar-DffcNdcJ.d.ts → UserMenu-B5XbC_Gy.d.ts} +24 -4
- package/dist/action.js +3 -3
- package/dist/{chunk-BJER6TOI.js → chunk-3VSDQCFO.js} +3 -3
- package/dist/{chunk-BJER6TOI.js.map → chunk-3VSDQCFO.js.map} +1 -1
- package/dist/{chunk-ZAM5TUPP.js → chunk-CMNZLA32.js} +60 -25
- package/dist/chunk-CMNZLA32.js.map +1 -0
- package/dist/{chunk-CVAMCRLL.js → chunk-EG2DASKR.js} +8 -4
- package/dist/chunk-EG2DASKR.js.map +1 -0
- package/dist/{chunk-DSULVRB6.js → chunk-GJ2FKAOM.js} +5 -3
- package/dist/chunk-GJ2FKAOM.js.map +1 -0
- package/dist/{chunk-FV7RESKL.js → chunk-HNUUZEOL.js} +28 -16
- package/dist/chunk-HNUUZEOL.js.map +1 -0
- package/dist/{chunk-37FLEB4N.js → chunk-IO6RMVUF.js} +8 -4
- package/dist/chunk-IO6RMVUF.js.map +1 -0
- package/dist/{chunk-N6WLCHQE.js → chunk-Q7WGPKUN.js} +6 -4
- package/dist/chunk-Q7WGPKUN.js.map +1 -0
- package/dist/{chunk-AZICSFBA.js → chunk-S5UNPCMQ.js} +112 -38
- package/dist/chunk-S5UNPCMQ.js.map +1 -0
- package/dist/{chunk-MTNFFSVA.js → chunk-SK6H7ARE.js} +5 -3
- package/dist/chunk-SK6H7ARE.js.map +1 -0
- package/dist/{chunk-FP5R6WED.js → chunk-SLLTFOOS.js} +22 -14
- package/dist/chunk-SLLTFOOS.js.map +1 -0
- package/dist/{chunk-3HN4ARGG.js → chunk-VKZY6J6G.js} +34 -3
- package/dist/chunk-VKZY6J6G.js.map +1 -0
- package/dist/{chunk-PSPJA5U6.js → chunk-WMOBLT3Z.js} +10 -5
- package/dist/chunk-WMOBLT3Z.js.map +1 -0
- package/dist/{chunk-C5H44NSP.js → chunk-WZRPGPLH.js} +10 -5
- package/dist/chunk-WZRPGPLH.js.map +1 -0
- package/dist/{chunk-N5U2BB6T.js → chunk-X2X6YDOX.js} +102 -28
- package/dist/chunk-X2X6YDOX.js.map +1 -0
- package/dist/{chunk-4I5S6MH3.js → chunk-X4ZFKOPX.js} +4 -4
- package/dist/{chunk-4I5S6MH3.js.map → chunk-X4ZFKOPX.js.map} +1 -1
- package/dist/{chunk-UXN67E5L.js → chunk-YQ6KXR3U.js} +8 -10
- package/dist/chunk-YQ6KXR3U.js.map +1 -0
- package/dist/{chunk-HSDZM4AM.js → chunk-YYQ45W25.js} +37 -24
- package/dist/chunk-YYQ45W25.js.map +1 -0
- package/dist/flash.js +1 -1
- package/dist/form.d.ts +3 -1
- package/dist/form.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +10 -10
- package/dist/pages.js +17 -17
- package/dist/palette.js +1 -1
- package/dist/resource.js +2 -2
- package/dist/shell.d.ts +1 -1
- package/dist/shell.js +9 -5
- package/dist/shell.js.map +1 -1
- package/dist/table.js +1 -1
- package/dist/utility.js +1 -1
- package/dist/widgets.js +2 -2
- package/package.json +6 -6
- package/dist/AreaChart-NMQ2RQGA.js.map +0 -1
- package/dist/ArqelCreatePage-MV4JN2MO.js +0 -8
- package/dist/ArqelDashboardPage-QK3BYVK3.js +0 -6
- package/dist/ArqelEditPage-7J3IS2BR.js +0 -8
- package/dist/ArqelIndexPage-R6JL5KOH.js +0 -12
- package/dist/ArqelShowPage-5WWUSYC7.js +0 -6
- package/dist/BarChart-KLHKTLO2.js.map +0 -1
- package/dist/LineChart-GHMPHFZZ.js.map +0 -1
- package/dist/PieChart-T2VQOFPP.js +0 -4
- package/dist/RadarChart-CSWR5AO7.js.map +0 -1
- package/dist/chunk-37FLEB4N.js.map +0 -1
- package/dist/chunk-3HN4ARGG.js.map +0 -1
- package/dist/chunk-AZICSFBA.js.map +0 -1
- package/dist/chunk-C5H44NSP.js.map +0 -1
- package/dist/chunk-CVAMCRLL.js.map +0 -1
- package/dist/chunk-DSULVRB6.js.map +0 -1
- package/dist/chunk-FP5R6WED.js.map +0 -1
- package/dist/chunk-FV7RESKL.js.map +0 -1
- package/dist/chunk-HSDZM4AM.js.map +0 -1
- package/dist/chunk-MTNFFSVA.js.map +0 -1
- package/dist/chunk-N5U2BB6T.js.map +0 -1
- package/dist/chunk-N6WLCHQE.js.map +0 -1
- package/dist/chunk-PSPJA5U6.js.map +0 -1
- package/dist/chunk-UXN67E5L.js.map +0 -1
- package/dist/chunk-ZAM5TUPP.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from './chunk-B7ULUJVO.js';
|
|
2
|
+
import { useArqelTranslations } from '@arqel-dev/react/utils';
|
|
2
3
|
import { usePage } from '@inertiajs/react';
|
|
3
4
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
5
|
|
|
@@ -18,8 +19,9 @@ function BreadcrumbsList({
|
|
|
18
19
|
separator = /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "/" }),
|
|
19
20
|
className
|
|
20
21
|
}) {
|
|
22
|
+
const t = useArqelTranslations();
|
|
21
23
|
if (items.length === 0) return null;
|
|
22
|
-
return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: cn("text-sm", className), children: /* @__PURE__ */ jsx("ol", { className: "flex flex-wrap items-center gap-2 text-muted-foreground", children: items.map((item, index) => {
|
|
24
|
+
return /* @__PURE__ */ jsx("nav", { "aria-label": t("arqel.aria.breadcrumb", "Breadcrumb"), className: cn("text-sm", className), children: /* @__PURE__ */ jsx("ol", { className: "flex flex-wrap items-center gap-2 text-muted-foreground", children: items.map((item, index) => {
|
|
23
25
|
const isLast = index === items.length - 1;
|
|
24
26
|
return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2", children: [
|
|
25
27
|
isLast || !item.url ? /* @__PURE__ */ jsx("span", { "aria-current": isLast ? "page" : void 0, className: "text-foreground", children: item.label }) : /* @__PURE__ */ jsx("a", { href: item.url, className: "hover:underline", children: item.label }),
|
|
@@ -88,5 +90,5 @@ function LoadingSkeleton({
|
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
export { Breadcrumbs, EmptyState, ErrorState, LoadingSkeleton };
|
|
91
|
-
//# sourceMappingURL=chunk-
|
|
92
|
-
//# sourceMappingURL=chunk-
|
|
93
|
+
//# sourceMappingURL=chunk-SK6H7ARE.js.map
|
|
94
|
+
//# sourceMappingURL=chunk-SK6H7ARE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utility/Breadcrumbs.tsx","../src/utility/EmptyState.tsx","../src/utility/ErrorState.tsx","../src/utility/LoadingSkeleton.tsx"],"names":["jsxs","jsx"],"mappings":"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","file":"chunk-SK6H7ARE.js","sourcesContent":["/**\n * `<Breadcrumbs>` — renders the panel's breadcrumb trail.\n *\n * Items can be passed explicitly, or auto-resolved from Inertia shared\n * props (`panel.breadcrumbs`, populated server-side by\n * `Arqel\\Nav\\BreadcrumbsBuilder`). Each item is a link unless it's the\n * last entry, which is rendered as plain text and marked\n * `aria-current=\"page\"`.\n *\n * `usePage()` is only invoked when `items` is omitted — explicit-items\n * usage works outside Inertia (e.g. dashboard widgets, tests).\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport { usePage } from '@inertiajs/react';\nimport type { ReactNode } from 'react';\nimport { cn } from '../utils/cn.js';\n\nexport interface BreadcrumbItem {\n label: string;\n url?: string | null;\n}\n\nexport interface BreadcrumbsProps {\n items?: BreadcrumbItem[];\n separator?: ReactNode;\n className?: string;\n}\n\ninterface PanelWithCrumbs {\n panel?: { breadcrumbs?: BreadcrumbItem[] } | null;\n}\n\nexport function Breadcrumbs(props: BreadcrumbsProps) {\n if (props.items === undefined) {\n return <SharedBreadcrumbs {...props} />;\n }\n return <BreadcrumbsList {...props} items={props.items} />;\n}\n\nfunction SharedBreadcrumbs(props: BreadcrumbsProps) {\n const page = usePage();\n const items = (page.props as unknown as PanelWithCrumbs).panel?.breadcrumbs ?? [];\n return <BreadcrumbsList {...props} items={items} />;\n}\n\nfunction BreadcrumbsList({\n items,\n separator = <span aria-hidden=\"true\">/</span>,\n className,\n}: BreadcrumbsProps & { items: BreadcrumbItem[] }) {\n const t = useArqelTranslations();\n if (items.length === 0) return null;\n\n return (\n <nav aria-label={t('arqel.aria.breadcrumb', 'Breadcrumb')} className={cn('text-sm', className)}>\n <ol className=\"flex flex-wrap items-center gap-2 text-muted-foreground\">\n {items.map((item, index) => {\n const isLast = index === items.length - 1;\n return (\n <li key={`${item.label}-${item.url ?? 'leaf'}`} className=\"flex items-center gap-2\">\n {isLast || !item.url ? (\n <span aria-current={isLast ? 'page' : undefined} className=\"text-foreground\">\n {item.label}\n </span>\n ) : (\n <a href={item.url} className=\"hover:underline\">\n {item.label}\n </a>\n )}\n {!isLast && separator}\n </li>\n );\n })}\n </ol>\n </nav>\n );\n}\n","/**\n * `<EmptyState>` — standardised empty-state block.\n *\n * Used by DataTable's empty slot and as a stand-alone fallback when\n * a resource collection has zero records.\n */\n\nimport type { ReactNode } from 'react';\nimport { cn } from '../utils/cn.js';\n\nexport interface EmptyStateProps {\n title: ReactNode;\n description?: ReactNode;\n icon?: ReactNode;\n action?: ReactNode;\n className?: string;\n}\n\nexport function EmptyState({ title, description, icon, action, className }: EmptyStateProps) {\n return (\n <div\n className={cn(\n 'flex flex-col items-center gap-3 px-4 py-12 text-center text-muted-foreground',\n className,\n )}\n >\n {icon && <div className=\"text-3xl\">{icon}</div>}\n <h2 className=\"text-base font-semibold text-foreground\">{title}</h2>\n {description && <p className=\"max-w-md text-sm\">{description}</p>}\n {action && <div className=\"mt-2\">{action}</div>}\n </div>\n );\n}\n","/**\n * `<ErrorState>` — generic error block (404 / 403 / 500).\n *\n * Phase 1 ships a plain layout; richer illustrations / Heroicons land\n * with `@arqel-dev/fields` icon registry.\n */\n\nimport type { ReactNode } from 'react';\nimport { cn } from '../utils/cn.js';\n\nexport interface ErrorStateProps {\n status?: number;\n title: ReactNode;\n description?: ReactNode;\n action?: ReactNode;\n className?: string;\n}\n\nexport function ErrorState({ status, title, description, action, className }: ErrorStateProps) {\n return (\n <div\n role=\"alert\"\n className={cn('flex flex-col items-center gap-3 px-4 py-16 text-center', className)}\n >\n {status && <span className=\"text-sm font-mono text-muted-foreground\">{status}</span>}\n <h1 className=\"text-2xl font-semibold tracking-tight\">{title}</h1>\n {description && <p className=\"max-w-md text-sm text-muted-foreground\">{description}</p>}\n {action && <div className=\"mt-2\">{action}</div>}\n </div>\n );\n}\n","/**\n * `<LoadingSkeleton>` — animated placeholder block.\n *\n * Variants:\n * - `line`: single text line (default)\n * - `block`: rectangular block (cards, hero areas)\n * - `circle`: circular (avatars)\n *\n * Heights / widths default to readable values; override via Tailwind\n * classes or explicit `height` / `width` props for bespoke layouts.\n */\n\nimport { cn } from '../utils/cn.js';\n\nexport type SkeletonVariant = 'line' | 'block' | 'circle';\n\nexport interface LoadingSkeletonProps {\n variant?: SkeletonVariant;\n width?: number | string;\n height?: number | string;\n count?: number;\n className?: string;\n}\n\nconst VARIANT_CLASSES: Record<SkeletonVariant, string> = {\n line: 'h-4 w-full',\n block: 'h-24 w-full',\n circle: 'h-10 w-10 rounded-full',\n};\n\nexport function LoadingSkeleton({\n variant = 'line',\n width,\n height,\n count = 1,\n className,\n}: LoadingSkeletonProps) {\n const items = Array.from({ length: count }, (_, i) => i);\n return (\n <div className=\"flex flex-col gap-2\" aria-hidden=\"true\">\n {items.map((i) => (\n <div\n key={i}\n style={{ width, height }}\n className={cn(\n 'animate-pulse bg-muted',\n variant !== 'circle' && 'rounded-sm',\n VARIANT_CLASSES[variant],\n className,\n )}\n />\n ))}\n </div>\n );\n}\n"]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Sheet, SheetContent, SheetHeader, SheetTitle } from './chunk-
|
|
1
|
+
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Sheet, SheetContent, SheetHeader, SheetTitle } from './chunk-VKZY6J6G.js';
|
|
2
2
|
import { FormActions } from './chunk-MZMYV43C.js';
|
|
3
|
-
import { FormRenderer } from './chunk-
|
|
3
|
+
import { FormRenderer } from './chunk-CMNZLA32.js';
|
|
4
4
|
import { Button } from './chunk-H23VX3RR.js';
|
|
5
5
|
import { cn } from './chunk-B7ULUJVO.js';
|
|
6
|
+
import { useArqelTranslations } from '@arqel-dev/react/utils';
|
|
6
7
|
import { useState, useMemo, useEffect } from 'react';
|
|
7
8
|
import { XIcon } from 'lucide-react';
|
|
8
9
|
import { Dialog as Dialog$1 } from 'radix-ui';
|
|
9
10
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
|
-
import { useArqelTranslations } from '@arqel-dev/react/utils';
|
|
11
11
|
|
|
12
12
|
function Dialog({
|
|
13
13
|
...props
|
|
@@ -150,6 +150,7 @@ function ActionFormModal({
|
|
|
150
150
|
errors = {},
|
|
151
151
|
initialValues = {}
|
|
152
152
|
}) {
|
|
153
|
+
const t = useArqelTranslations();
|
|
153
154
|
const [values, setValues] = useState(initialValues);
|
|
154
155
|
const size = action.modalSize ?? "md";
|
|
155
156
|
const schema = useMemo(
|
|
@@ -193,7 +194,7 @@ function ActionFormModal({
|
|
|
193
194
|
{
|
|
194
195
|
processing,
|
|
195
196
|
submitLabel: action.label,
|
|
196
|
-
extra: /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: "Cancel" }) })
|
|
197
|
+
extra: /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: t("form.cancel", "Cancel") }) })
|
|
197
198
|
}
|
|
198
199
|
)
|
|
199
200
|
]
|
|
@@ -229,11 +230,17 @@ function ConfirmDialog({
|
|
|
229
230
|
config?.description && /* @__PURE__ */ jsx(DialogDescription, { children: config.description })
|
|
230
231
|
] }),
|
|
231
232
|
requiresText !== void 0 && /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
|
|
232
|
-
/* @__PURE__ */
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
/* @__PURE__ */ jsx("span", { children: (() => {
|
|
234
|
+
const [before, after] = t(
|
|
235
|
+
"arqel.messages.type_to_confirm",
|
|
236
|
+
"Type :value to confirm"
|
|
237
|
+
).split(":value");
|
|
238
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
239
|
+
before,
|
|
240
|
+
/* @__PURE__ */ jsx("code", { className: "font-mono text-foreground", children: requiresText }),
|
|
241
|
+
after
|
|
242
|
+
] });
|
|
243
|
+
})() }),
|
|
237
244
|
/* @__PURE__ */ jsx(
|
|
238
245
|
"input",
|
|
239
246
|
{
|
|
@@ -355,6 +362,7 @@ function ActionMenu({
|
|
|
355
362
|
trigger,
|
|
356
363
|
className
|
|
357
364
|
}) {
|
|
365
|
+
const t = useArqelTranslations();
|
|
358
366
|
const [confirmAction, setConfirmAction] = useState(null);
|
|
359
367
|
const [formAction, setFormAction] = useState(null);
|
|
360
368
|
const [sheetOpen, setSheetOpen] = useState(false);
|
|
@@ -393,7 +401,7 @@ function ActionMenu({
|
|
|
393
401
|
onInvoke(action);
|
|
394
402
|
}
|
|
395
403
|
};
|
|
396
|
-
const triggerNode = trigger ?? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-touch", "aria-label": "Actions", children: "\u22EF" });
|
|
404
|
+
const triggerNode = trigger ?? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-touch", "aria-label": t("arqel.actions.menu", "Actions"), children: "\u22EF" });
|
|
397
405
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
398
406
|
/* @__PURE__ */ jsx("div", { "data-arqel-action-dropdown": "", className: "hidden md:contents", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
399
407
|
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: triggerNode }),
|
|
@@ -421,7 +429,7 @@ function ActionMenu({
|
|
|
421
429
|
"button",
|
|
422
430
|
{
|
|
423
431
|
type: "button",
|
|
424
|
-
"aria-label": "Actions",
|
|
432
|
+
"aria-label": t("arqel.actions.menu", "Actions"),
|
|
425
433
|
className: "inline-flex size-11 items-center justify-center rounded-md text-lg hover:bg-accent",
|
|
426
434
|
onClick: () => setSheetOpen(true),
|
|
427
435
|
children: "\u22EF"
|
|
@@ -433,7 +441,7 @@ function ActionMenu({
|
|
|
433
441
|
side: "bottom",
|
|
434
442
|
className: "max-h-[80vh] gap-0 overflow-y-auto pb-[env(safe-area-inset-bottom)]",
|
|
435
443
|
children: [
|
|
436
|
-
/* @__PURE__ */ jsx(SheetHeader, { className: "px-4 pt-4 pb-2", children: /* @__PURE__ */ jsx(SheetTitle, { children: "Actions" }) }),
|
|
444
|
+
/* @__PURE__ */ jsx(SheetHeader, { className: "px-4 pt-4 pb-2", children: /* @__PURE__ */ jsx(SheetTitle, { children: t("arqel.actions.menu", "Actions") }) }),
|
|
437
445
|
/* @__PURE__ */ jsx("div", { className: "flex flex-col px-2 pb-2", children: actions.map((action) => /* @__PURE__ */ jsx(
|
|
438
446
|
"button",
|
|
439
447
|
{
|
|
@@ -489,5 +497,5 @@ function ActionMenu({
|
|
|
489
497
|
}
|
|
490
498
|
|
|
491
499
|
export { ActionButton, ActionFormModal, ActionMenu, ConfirmDialog };
|
|
492
|
-
//# sourceMappingURL=chunk-
|
|
493
|
-
//# sourceMappingURL=chunk-
|
|
500
|
+
//# sourceMappingURL=chunk-SLLTFOOS.js.map
|
|
501
|
+
//# sourceMappingURL=chunk-SLLTFOOS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shadcn/ui/dialog.tsx","../src/action/ActionFormModal.tsx","../src/action/ConfirmDialog.tsx","../src/action/ActionButton.tsx","../src/action/ActionMenu.tsx"],"names":["DialogPrimitive","jsx","jsxs","useArqelTranslations","useState","Fragment"],"mappings":"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","file":"chunk-SLLTFOOS.js","sourcesContent":["import * as React from \"react\"\nimport { XIcon } from \"lucide-react\"\nimport { Dialog as DialogPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/utils/cn\"\nimport { Button } from \"@/shadcn/ui/button\"\n\nfunction Dialog({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n}\n\nfunction DialogTrigger({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />\n}\n\nfunction DialogPortal({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />\n}\n\nfunction DialogClose({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean\n}) {\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogFooter({\n className,\n showCloseButton = false,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean\n}) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close asChild>\n <Button variant=\"outline\">Close</Button>\n </DialogPrimitive.Close>\n )}\n </div>\n )\n}\n\nfunction DialogTitle({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-none font-semibold\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n}\n","/**\n * `<ActionFormModal>` — modal hosting a `<FormRenderer>` for an Action's\n * companion form (see `arqel-dev/actions` `HasForm`).\n *\n * Action form fields are a flat list — Action FormSchema is built\n * inline from the `ActionFormField[]` since layout components don't\n * apply at this scale. Submit fires `onSubmit(values)`; the parent\n * (typically `useAction`) handles invocation + loading.\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { ActionSchema, ModalSize } from '@arqel-dev/types/actions';\nimport type { FieldSchema } from '@arqel-dev/types/fields';\nimport type { FormSchema } from '@arqel-dev/types/forms';\nimport { useMemo, useState } from 'react';\nimport { FormActions } from '../form/FormActions.js';\nimport { FormRenderer } from '../form/FormRenderer.js';\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '../shadcn/ui/dialog.js';\nimport { cn } from '../utils/cn.js';\nimport { Button } from './Button.js';\n\nexport interface ActionFormModalProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n action: ActionSchema;\n fields: FieldSchema[];\n onSubmit: (values: Record<string, unknown>) => void;\n processing?: boolean;\n errors?: Record<string, string[]> | undefined;\n initialValues?: Record<string, unknown>;\n}\n\nconst SIZE_CLASSES: Record<ModalSize, string> = {\n sm: 'sm:max-w-[24rem]',\n md: 'sm:max-w-[32rem]',\n lg: 'sm:max-w-[44rem]',\n xl: 'sm:max-w-[56rem]',\n full: 'sm:max-w-[90vw] h-[90vh]',\n};\n\nexport function ActionFormModal({\n open,\n onOpenChange,\n action,\n fields,\n onSubmit,\n processing = false,\n errors = {},\n initialValues = {},\n}: ActionFormModalProps) {\n const t = useArqelTranslations();\n const [values, setValues] = useState<Record<string, unknown>>(initialValues);\n const size = action.modalSize ?? 'md';\n\n const schema: FormSchema = useMemo(\n () => ({\n schema: (action.form ?? []).map((entry) => ({\n kind: 'field' as const,\n name: entry.name,\n type: entry.type,\n })),\n columns: 1,\n model: null,\n inline: false,\n disabled: false,\n }),\n [action.form],\n );\n\n return (\n <Dialog open={open} onOpenChange={(next) => onOpenChange(next)}>\n <DialogContent className={cn('overflow-auto', SIZE_CLASSES[size])}>\n <DialogHeader>\n <DialogTitle>{action.label}</DialogTitle>\n </DialogHeader>\n <form\n className=\"flex flex-col gap-4\"\n onSubmit={(e) => {\n e.preventDefault();\n onSubmit(values);\n }}\n >\n <FormRenderer\n schema={schema}\n fields={fields}\n values={values}\n errors={errors}\n onChange={(name, value) => setValues((prev) => ({ ...prev, [name]: value }))}\n disabled={processing}\n />\n <FormActions\n processing={processing}\n submitLabel={action.label}\n extra={\n <DialogClose asChild>\n <Button type=\"button\" variant=\"ghost\" disabled={processing}>\n {t('form.cancel', 'Cancel')}\n </Button>\n </DialogClose>\n }\n />\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n","/**\n * `<ConfirmDialog>` — confirmation modal driven by the shadcn (Radix) Dialog.\n *\n * Phase 1 supports the canonical `ConfirmationConfig`:\n * - heading / description\n * - color (destructive | warning | info) — drives submit button variant\n * - submit / cancel labels\n * - \"type to confirm\" via `requiresText` (submit stays disabled until\n * the typed value matches exactly)\n *\n * Keyboard: Escape cancels (Radix built-in dismiss); Enter inside the\n * type-to-confirm input fires confirm when the value matches.\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { ConfirmationConfig } from '@arqel-dev/types/actions';\nimport { useEffect, useState } from 'react';\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '../shadcn/ui/dialog.js';\nimport { Button, type ButtonProps } from './Button.js';\n\nexport interface ConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n config?: ConfirmationConfig | undefined;\n onConfirm: () => void;\n processing?: boolean;\n}\n\nconst COLOR_VARIANT: Record<NonNullable<ConfirmationConfig['color']>, ButtonProps['variant']> = {\n destructive: 'destructive',\n warning: 'default',\n info: 'default',\n};\n\nexport function ConfirmDialog({\n open,\n onOpenChange,\n config,\n onConfirm,\n processing = false,\n}: ConfirmDialogProps) {\n const t = useArqelTranslations();\n const [typed, setTyped] = useState('');\n\n useEffect(() => {\n if (!open) setTyped('');\n }, [open]);\n\n const requiresText = config?.requiresText;\n const submitDisabled = processing || (requiresText !== undefined && typed !== requiresText);\n const variant = COLOR_VARIANT[config?.color ?? 'destructive'];\n // Precedence: explicit config > shared translation > English fallback.\n const submitLabel =\n config?.submitLabel ??\n (config?.color === 'info'\n ? t('arqel.actions.confirm', 'Confirm')\n : t('arqel.actions.delete', 'Delete'));\n const cancelLabel = config?.cancelLabel ?? t('arqel.actions.cancel', 'Cancel');\n\n return (\n <Dialog open={open} onOpenChange={(next) => onOpenChange(next)}>\n <DialogContent className=\"sm:max-w-[28rem]\" showCloseButton={false}>\n <DialogHeader>\n <DialogTitle>\n {config?.heading ?? t('arqel.messages.delete_confirm', 'Are you sure?')}\n </DialogTitle>\n {config?.description && <DialogDescription>{config.description}</DialogDescription>}\n </DialogHeader>\n {requiresText !== undefined && (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n <span>\n {(() => {\n // Render the required value as inline <code> while keeping the\n // surrounding prompt translatable: split the localized string on\n // its :value placeholder and splice the <code> element in.\n const [before, after] = t(\n 'arqel.messages.type_to_confirm',\n 'Type :value to confirm',\n ).split(':value');\n return (\n <>\n {before}\n <code className=\"font-mono text-foreground\">{requiresText}</code>\n {after}\n </>\n );\n })()}\n </span>\n <input\n type=\"text\"\n ref={(el) => el?.focus()}\n value={typed}\n onChange={(e) => setTyped(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !submitDisabled) {\n e.preventDefault();\n onConfirm();\n }\n }}\n className=\"h-9 rounded-md border border-input bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n />\n </label>\n )}\n <DialogFooter>\n <DialogClose asChild>\n <Button type=\"button\" variant=\"ghost\" disabled={processing}>\n {cancelLabel}\n </Button>\n </DialogClose>\n <Button type=\"button\" variant={variant} disabled={submitDisabled} onClick={onConfirm}>\n {processing ? t('form.saving', 'Working…') : submitLabel}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","/**\n * `<ActionButton>` — renders an `ActionSchema` as a button.\n *\n * Behaviour matrix:\n * - `requiresConfirmation` only → opens ConfirmDialog\n * - `form` only → opens ActionFormModal\n * - both → ConfirmDialog first, then form\n * - neither → fires `onInvoke` immediately\n *\n * `onInvoke` receives optional form data so parents (typically using\n * `useAction()` from @arqel-dev/hooks) can dispatch the underlying request.\n */\n\nimport type { ActionColor, ActionSchema, ActionVariant } from '@arqel-dev/types/actions';\nimport type { FieldSchema } from '@arqel-dev/types/fields';\nimport { useState } from 'react';\nimport { ActionFormModal } from './ActionFormModal.js';\nimport { Button, type ButtonProps } from './Button.js';\nimport { ConfirmDialog } from './ConfirmDialog.js';\n\nexport interface ActionButtonProps {\n action: ActionSchema;\n /**\n * Field schemas for the form modal (resolved server-side from\n * `Action::form` via `FieldSchemaSerializer`). Optional override — when\n * omitted the component reads `action.formFields`, the canonical payload\n * shipped by `Action::toArray()` (#213).\n */\n formFields?: FieldSchema[] | undefined;\n onInvoke: (formValues?: Record<string, unknown>) => void;\n processing?: boolean;\n errors?: Record<string, string[]>;\n size?: ButtonProps['size'];\n className?: string;\n}\n\nconst COLOR_TO_VARIANT: Record<ActionColor, ButtonProps['variant']> = {\n primary: 'default',\n secondary: 'outline',\n destructive: 'destructive',\n success: 'default',\n warning: 'default',\n info: 'default',\n};\n\nconst VARIANT_OVERRIDE: Partial<Record<ActionVariant, ButtonProps['variant']>> = {\n default: 'default',\n outline: 'outline',\n ghost: 'ghost',\n destructive: 'destructive',\n};\n\nexport function ActionButton({\n action,\n formFields,\n onInvoke,\n processing = false,\n errors,\n size = 'default',\n className,\n}: ActionButtonProps) {\n const [confirmOpen, setConfirmOpen] = useState(false);\n const [formOpen, setFormOpen] = useState(false);\n\n // Prefer an explicit prop; otherwise read the schema shipped by\n // `Action::toArray()` so the modal renders real inputs (#213).\n const resolvedFormFields = formFields ?? action.formFields ?? [];\n\n const variant: ButtonProps['variant'] =\n VARIANT_OVERRIDE[action.variant] ?? COLOR_TO_VARIANT[action.color];\n\n const handleClick = () => {\n if (action.disabled) return;\n if (action.requiresConfirmation) {\n setConfirmOpen(true);\n } else if (action.form && action.form.length > 0) {\n setFormOpen(true);\n } else {\n onInvoke();\n }\n };\n\n const handleConfirm = () => {\n setConfirmOpen(false);\n if (action.form && action.form.length > 0) {\n setFormOpen(true);\n } else {\n onInvoke();\n }\n };\n\n const handleFormSubmit = (values: Record<string, unknown>) => {\n onInvoke(values);\n setFormOpen(false);\n };\n\n return (\n <>\n <Button\n variant={variant}\n size={size}\n disabled={action.disabled === true || processing}\n title={action.tooltip}\n onClick={handleClick}\n className={className}\n >\n {action.label}\n </Button>\n {action.requiresConfirmation && (\n <ConfirmDialog\n open={confirmOpen}\n onOpenChange={setConfirmOpen}\n config={action.confirmation}\n onConfirm={handleConfirm}\n processing={processing}\n />\n )}\n {action.form && action.form.length > 0 && (\n <ActionFormModal\n open={formOpen}\n onOpenChange={setFormOpen}\n action={action}\n fields={resolvedFormFields}\n onSubmit={handleFormSubmit}\n processing={processing}\n errors={errors}\n />\n )}\n </>\n );\n}\n","/**\n * `<ActionMenu>` — renders a list of actions inline (≤ `inlineThreshold`)\n * or collapses them into a shadcn (Radix) dropdown menu when the list grows.\n *\n * Each menu item delegates to `<ActionButton>` so confirmation /\n * form-modal behaviour is consistent across surfaces. The dropdown\n * trigger is rendered as a ghost icon button by default; callers can\n * supply a custom `trigger` slot.\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { ActionSchema } from '@arqel-dev/types/actions';\nimport type { FieldSchema } from '@arqel-dev/types/fields';\nimport { type ReactNode, useState } from 'react';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '../shadcn/ui/dropdown-menu.js';\nimport { Sheet, SheetContent, SheetHeader, SheetTitle } from '../shadcn/ui/sheet.js';\nimport { cn } from '../utils/cn.js';\nimport { ActionButton } from './ActionButton.js';\nimport { ActionFormModal } from './ActionFormModal.js';\nimport { Button } from './Button.js';\nimport { ConfirmDialog } from './ConfirmDialog.js';\n\n/** Does this action need a gate (confirm/form) before invoking? */\nfunction actionHasForm(action: ActionSchema): boolean {\n return Array.isArray(action.form) && action.form.length > 0;\n}\n\nexport interface ActionMenuProps {\n actions: ActionSchema[];\n /**\n * Optional per-action override map keyed by action name. When an action\n * is absent here, `<ActionButton>` falls back to the action's own\n * `formFields` payload shipped by `Action::toArray()` (#213).\n */\n formFieldsByAction?: Record<string, FieldSchema[]>;\n onInvoke: (action: ActionSchema, formValues?: Record<string, unknown>) => void;\n inlineThreshold?: number;\n processing?: boolean;\n trigger?: ReactNode;\n className?: string;\n}\n\nexport function ActionMenu({\n actions,\n formFieldsByAction = {},\n onInvoke,\n inlineThreshold = 3,\n processing = false,\n trigger,\n className,\n}: ActionMenuProps) {\n const t = useArqelTranslations();\n // Which dropdown action (if any) is currently driving a confirm dialog /\n // form modal. The modals are mounted as siblings of the menu so they\n // survive the menu closing on `onSelect`.\n const [confirmAction, setConfirmAction] = useState<ActionSchema | null>(null);\n const [formAction, setFormAction] = useState<ActionSchema | null>(null);\n // The mobile bottom-sheet is controlled so selecting an item can close it\n // (mirroring the Dropdown's onSelect auto-close).\n const [sheetOpen, setSheetOpen] = useState(false);\n\n if (actions.length === 0) return null;\n\n if (actions.length <= inlineThreshold) {\n return (\n <div className={cn('flex flex-wrap items-center gap-1', className)}>\n {actions.map((action) => (\n <ActionButton\n key={action.name}\n action={action}\n formFields={formFieldsByAction[action.name]}\n onInvoke={(values) => onInvoke(action, values)}\n processing={processing}\n size=\"sm\"\n // 44px touch target on mobile (WCAG 2.5.5); dense 32px on >=md.\n className=\"h-11 md:h-8\"\n />\n ))}\n </div>\n );\n }\n\n const handleSelect = (action: ActionSchema) => {\n if (action.disabled) return;\n // Mirror ActionButton's gating: confirmation first, then form, else\n // invoke directly. The modals open as siblings of the (now closing)\n // menu so destructive/form actions never fire without a gate (#229,\n // #231).\n if (action.requiresConfirmation) {\n setConfirmAction(action);\n } else if (actionHasForm(action)) {\n setFormAction(action);\n } else {\n onInvoke(action);\n }\n };\n\n const handleConfirm = () => {\n const action = confirmAction;\n setConfirmAction(null);\n if (!action) return;\n if (actionHasForm(action)) {\n setFormAction(action);\n } else {\n onInvoke(action);\n }\n };\n\n // One visual trigger shared by both presentation surfaces. The Dropdown\n // wraps it via DropdownMenuTrigger (asChild); the Sheet opens it on click.\n const triggerNode = trigger ?? (\n <Button variant=\"ghost\" size=\"icon-touch\" aria-label={t('arqel.actions.menu', 'Actions')}>\n ⋯\n </Button>\n );\n\n return (\n <>\n {/* Desktop (>=md): the Radix dropdown popper, unchanged. */}\n <div data-arqel-action-dropdown=\"\" className=\"hidden md:contents\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>{triggerNode}</DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n sideOffset={6}\n className={cn('min-w-[12rem]', className)}\n >\n {actions.map((action) => (\n <DropdownMenuItem\n key={action.name}\n disabled={action.disabled === true}\n variant={action.color === 'destructive' ? 'destructive' : 'default'}\n onSelect={() => handleSelect(action)}\n >\n {action.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n\n {/* Mobile (<md): a full-width bottom sheet with >=44px items. */}\n <div className=\"md:hidden\">\n <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>\n <button\n type=\"button\"\n aria-label={t('arqel.actions.menu', 'Actions')}\n className=\"inline-flex size-11 items-center justify-center rounded-md text-lg hover:bg-accent\"\n onClick={() => setSheetOpen(true)}\n >\n ⋯\n </button>\n <SheetContent\n side=\"bottom\"\n className=\"max-h-[80vh] gap-0 overflow-y-auto pb-[env(safe-area-inset-bottom)]\"\n >\n <SheetHeader className=\"px-4 pt-4 pb-2\">\n <SheetTitle>{t('arqel.actions.menu', 'Actions')}</SheetTitle>\n </SheetHeader>\n <div className=\"flex flex-col px-2 pb-2\">\n {actions.map((action) => (\n <button\n key={action.name}\n type=\"button\"\n data-arqel-sheet-action=\"\"\n disabled={action.disabled === true}\n className={cn(\n 'flex min-h-11 w-full items-center rounded-md px-3 text-left text-sm hover:bg-accent disabled:pointer-events-none disabled:opacity-50',\n action.color === 'destructive' && 'text-destructive',\n )}\n onClick={() => {\n setSheetOpen(false);\n handleSelect(action);\n }}\n >\n {action.label}\n </button>\n ))}\n </div>\n </SheetContent>\n </Sheet>\n </div>\n\n {confirmAction && (\n <ConfirmDialog\n open\n onOpenChange={(next) => {\n if (!next) setConfirmAction(null);\n }}\n config={confirmAction.confirmation}\n onConfirm={handleConfirm}\n processing={processing}\n />\n )}\n {formAction && (\n <ActionFormModal\n open\n onOpenChange={(next) => {\n if (!next) setFormAction(null);\n }}\n action={formAction}\n fields={formFieldsByAction[formAction.name] ?? formAction.formFields ?? []}\n onSubmit={(values) => {\n const action = formAction;\n setFormAction(null);\n onInvoke(action, values);\n }}\n processing={processing}\n />\n )}\n </>\n );\n}\n"]}
|
|
@@ -58,6 +58,37 @@ function DropdownMenuItem({
|
|
|
58
58
|
}
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
|
+
function DropdownMenuLabel({
|
|
62
|
+
className,
|
|
63
|
+
inset,
|
|
64
|
+
...props
|
|
65
|
+
}) {
|
|
66
|
+
return /* @__PURE__ */ jsx(
|
|
67
|
+
DropdownMenu$1.Label,
|
|
68
|
+
{
|
|
69
|
+
"data-slot": "dropdown-menu-label",
|
|
70
|
+
"data-inset": inset,
|
|
71
|
+
className: cn(
|
|
72
|
+
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
|
73
|
+
className
|
|
74
|
+
),
|
|
75
|
+
...props
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
function DropdownMenuSeparator({
|
|
80
|
+
className,
|
|
81
|
+
...props
|
|
82
|
+
}) {
|
|
83
|
+
return /* @__PURE__ */ jsx(
|
|
84
|
+
DropdownMenu$1.Separator,
|
|
85
|
+
{
|
|
86
|
+
"data-slot": "dropdown-menu-separator",
|
|
87
|
+
className: cn("-mx-1 my-1 h-px bg-border", className),
|
|
88
|
+
...props
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
}
|
|
61
92
|
function Sheet({ ...props }) {
|
|
62
93
|
return /* @__PURE__ */ jsx(Dialog.Root, { "data-slot": "sheet", ...props });
|
|
63
94
|
}
|
|
@@ -152,6 +183,6 @@ function SheetDescription({
|
|
|
152
183
|
);
|
|
153
184
|
}
|
|
154
185
|
|
|
155
|
-
export { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle };
|
|
156
|
-
//# sourceMappingURL=chunk-
|
|
157
|
-
//# sourceMappingURL=chunk-
|
|
186
|
+
export { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle };
|
|
187
|
+
//# sourceMappingURL=chunk-VKZY6J6G.js.map
|
|
188
|
+
//# sourceMappingURL=chunk-VKZY6J6G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shadcn/ui/dropdown-menu.tsx","../src/shadcn/ui/sheet.tsx"],"names":["DropdownMenuPrimitive","jsx","SheetPrimitive","jsxs"],"mappings":"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","file":"chunk-VKZY6J6G.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\"\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/utils/cn\"\n\nfunction DropdownMenu({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />\n}\n\nfunction DropdownMenuPortal({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {\n return (\n <DropdownMenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />\n )\n}\n\nfunction DropdownMenuTrigger({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n return (\n <DropdownMenuPrimitive.Trigger\n data-slot=\"dropdown-menu-trigger\"\n {...props}\n />\n )\n}\n\nfunction DropdownMenuContent({\n className,\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(\n \"z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n )\n}\n\nfunction DropdownMenuGroup({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\n return (\n <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />\n )\n}\n\nfunction DropdownMenuItem({\n className,\n inset,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean\n variant?: \"default\" | \"destructive\"\n}) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked ?? false}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n )\n}\n\nfunction DropdownMenuRadioGroup({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {\n return (\n <DropdownMenuPrimitive.RadioGroup\n data-slot=\"dropdown-menu-radio-group\"\n {...props}\n />\n )\n}\n\nfunction DropdownMenuRadioItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {\n return (\n <DropdownMenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n )\n}\n\nfunction DropdownMenuLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean\n}) {\n return (\n <DropdownMenuPrimitive.Label\n data-slot=\"dropdown-menu-label\"\n data-inset={inset}\n className={cn(\n \"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuShortcut({\n className,\n ...props\n}: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n className={cn(\n \"ml-auto text-xs tracking-widest text-muted-foreground\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DropdownMenuSub({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />\n}\n\nfunction DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean\n}) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto size-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n )\n}\n\nfunction DropdownMenuSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n return (\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n \"z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n DropdownMenu,\n DropdownMenuPortal,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuLabel,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubTrigger,\n DropdownMenuSubContent,\n}\n","import * as React from \"react\"\nimport { XIcon } from \"lucide-react\"\nimport { Dialog as SheetPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/utils/cn\"\n\nfunction Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {\n return <SheetPrimitive.Root data-slot=\"sheet\" {...props} />\n}\n\nfunction SheetTrigger({\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {\n return <SheetPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />\n}\n\nfunction SheetClose({\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Close>) {\n return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />\n}\n\nfunction SheetPortal({\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Portal>) {\n return <SheetPrimitive.Portal data-slot=\"sheet-portal\" {...props} />\n}\n\nfunction SheetOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {\n return (\n <SheetPrimitive.Overlay\n data-slot=\"sheet-overlay\"\n className={cn(\n \"fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction SheetContent({\n className,\n children,\n side = \"right\",\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Content> & {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\"\n showCloseButton?: boolean\n}) {\n return (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n data-slot=\"sheet-content\"\n className={cn(\n \"fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500\",\n side === \"right\" &&\n \"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm\",\n side === \"left\" &&\n \"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm\",\n side === \"top\" &&\n \"inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n side === \"bottom\" &&\n \"inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <SheetPrimitive.Close className=\"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary\">\n <XIcon className=\"size-4\" />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n )}\n </SheetPrimitive.Content>\n </SheetPortal>\n )\n}\n\nfunction SheetHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(\"flex flex-col gap-1.5 p-4\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetTitle({\n className,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Title>) {\n return (\n <SheetPrimitive.Title\n data-slot=\"sheet-title\"\n className={cn(\"font-semibold text-foreground\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetDescription({\n className,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Description>) {\n return (\n <SheetPrimitive.Description\n data-slot=\"sheet-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Sheet,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cn } from './chunk-B7ULUJVO.js';
|
|
2
|
-
import { useArqelTranslations } from '@arqel-dev/react/utils';
|
|
2
|
+
import { useArqelTranslations, useArqelLocale } from '@arqel-dev/react/utils';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
import { useMemo } from 'react';
|
|
4
5
|
|
|
5
6
|
function resolveColumnSpan(columnSpan) {
|
|
6
7
|
if (columnSpan === void 0) return void 0;
|
|
@@ -72,6 +73,7 @@ var COLOR_STROKE = {
|
|
|
72
73
|
info: "stroke-sky-500"
|
|
73
74
|
};
|
|
74
75
|
function Sparkline({ points, color }) {
|
|
76
|
+
const t = useArqelTranslations();
|
|
75
77
|
if (points.length < 2) return null;
|
|
76
78
|
const min = Math.min(...points);
|
|
77
79
|
const max = Math.max(...points);
|
|
@@ -89,7 +91,7 @@ function Sparkline({ points, color }) {
|
|
|
89
91
|
preserveAspectRatio: "none",
|
|
90
92
|
className: cn("mt-3 h-8 w-full", COLOR_STROKE[color]),
|
|
91
93
|
role: "img",
|
|
92
|
-
"aria-label": "Trend sparkline",
|
|
94
|
+
"aria-label": t("arqel.aria.stat_sparkline", "Trend sparkline"),
|
|
93
95
|
"data-testid": "stat-card-sparkline",
|
|
94
96
|
children: /* @__PURE__ */ jsx(
|
|
95
97
|
"polyline",
|
|
@@ -107,9 +109,12 @@ function Sparkline({ points, color }) {
|
|
|
107
109
|
}
|
|
108
110
|
function StatCard({ widget, className, columnSpan }) {
|
|
109
111
|
const { heading, description, value, statDescription, descriptionIcon, color, chart, url } = widget;
|
|
112
|
+
const locale = useArqelLocale();
|
|
113
|
+
const numberFormatter = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
114
|
+
const displayValue = typeof value === "number" && Number.isFinite(value) ? numberFormatter.format(value) : value ?? "\u2014";
|
|
110
115
|
const hasChart = Array.isArray(chart) && chart.length >= 2;
|
|
111
116
|
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
112
|
-
/* @__PURE__ */ jsx("div", { className: cn("text-4xl font-bold tabular-nums", COLOR_TEXT[color]), children:
|
|
117
|
+
/* @__PURE__ */ jsx("div", { className: cn("text-4xl font-bold tabular-nums", COLOR_TEXT[color]), children: displayValue }),
|
|
113
118
|
statDescription && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
114
119
|
descriptionIcon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-testid": "stat-card-icon", children: descriptionIcon }),
|
|
115
120
|
/* @__PURE__ */ jsx("span", { children: statDescription })
|
|
@@ -138,5 +143,5 @@ function StatCard({ widget, className, columnSpan }) {
|
|
|
138
143
|
}
|
|
139
144
|
|
|
140
145
|
export { StatCard, WidgetWrapper };
|
|
141
|
-
//# sourceMappingURL=chunk-
|
|
142
|
-
//# sourceMappingURL=chunk-
|
|
146
|
+
//# sourceMappingURL=chunk-WMOBLT3Z.js.map
|
|
147
|
+
//# sourceMappingURL=chunk-WMOBLT3Z.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/widgets/WidgetWrapper.tsx","../src/widgets/StatCard.tsx"],"names":["useArqelTranslations","jsx","jsxs"],"mappings":"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","file":"chunk-WMOBLT3Z.js","sourcesContent":["/**\n * `<WidgetWrapper>` — common chrome for dashboard widgets.\n *\n * Provides:\n * - Heading / description header.\n * - Loading skeleton (CLS-safe placeholder while deferred widgets fetch).\n * - Error state (`role=\"alert\"`) with optional retry action.\n * - `aria-label`-ed `<section>` so widgets are individually navigable.\n *\n * Used by `<StatCard>` (and shortly `<ChartCard>` / `<TableCard>`) so the\n * outer card chrome is consistent across widget types.\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { ReactNode } from 'react';\nimport { cn } from '../utils/cn.js';\n\nexport interface WidgetWrapperProps {\n heading?: string | undefined;\n description?: string | undefined;\n loading?: boolean | undefined;\n error?: Error | null | undefined;\n onRetry?: (() => void) | undefined;\n /** Tailwind grid column span (number → `col-span-{n}`, or raw class). */\n columnSpan?: number | string | undefined;\n className?: string | undefined;\n children: ReactNode;\n}\n\nfunction resolveColumnSpan(columnSpan?: number | string): string | undefined {\n if (columnSpan === undefined) return undefined;\n if (typeof columnSpan === 'number') return `col-span-${columnSpan}`;\n return columnSpan;\n}\n\nexport function WidgetWrapper({\n heading,\n description,\n loading = false,\n error = null,\n onRetry,\n columnSpan,\n className,\n children,\n}: WidgetWrapperProps) {\n const t = useArqelTranslations();\n const baseClass = cn(\n 'rounded border bg-background p-4',\n resolveColumnSpan(columnSpan),\n className,\n );\n\n if (loading) {\n return (\n <section\n aria-label={heading}\n aria-busy=\"true\"\n className={baseClass}\n data-widget-state=\"loading\"\n >\n <div className=\"animate-pulse h-32 bg-muted rounded\" />\n </section>\n );\n }\n\n if (error) {\n return (\n <section aria-label={heading} className={baseClass} data-widget-state=\"error\">\n <div role=\"alert\" className=\"flex flex-col gap-2 text-sm text-destructive\">\n <p>{error.message}</p>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"self-start rounded border px-2 py-1 text-xs text-foreground hover:bg-muted\"\n >\n {t('arqel.actions.retry', 'Retry')}\n </button>\n )}\n </div>\n </section>\n );\n }\n\n return (\n <section aria-label={heading} className={baseClass} data-widget-state=\"ready\">\n {heading && <h2 className=\"text-sm font-medium text-muted-foreground\">{heading}</h2>}\n {description && <p className=\"mt-1 text-xs text-muted-foreground\">{description}</p>}\n <div className={cn(heading || description ? 'mt-3' : undefined)}>{children}</div>\n </section>\n );\n}\n","/**\n * `<StatCard>` — KPI / \"big number\" widget.\n *\n * Renders the payload produced by `Arqel\\Widgets\\StatWidget::data()`:\n *\n * {\n * name, type: 'stat', heading?, description?,\n * value, statDescription?, descriptionIcon?,\n * color, icon?, chart?: number[], url?\n * }\n *\n * Includes a tiny inline-SVG sparkline (no Recharts dep) when `chart`\n * is set. ChartCard / TableCard (Recharts-based) ship in a separate\n * cluster; this component is intentionally Recharts-free so the\n * common \"big number\" case stays bundle-cheap.\n */\n\nimport { useArqelLocale, useArqelTranslations } from '@arqel-dev/react/utils';\nimport { type ReactNode, useMemo } from 'react';\nimport { cn } from '../utils/cn.js';\nimport { WidgetWrapper } from './WidgetWrapper.js';\n\nexport type StatCardColor = 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info';\n\nexport interface StatCardWidget {\n name: string;\n type: 'stat';\n heading?: string;\n description?: string;\n value: string | number | null;\n statDescription?: string | null;\n descriptionIcon?: string | null;\n color: StatCardColor;\n icon?: string | null;\n chart?: number[] | null;\n url?: string | null;\n}\n\nexport interface StatCardProps {\n widget: StatCardWidget;\n className?: string | undefined;\n columnSpan?: number | string | undefined;\n}\n\nconst COLOR_TEXT: Record<StatCardColor, string> = {\n primary: 'text-primary',\n secondary: 'text-secondary-foreground',\n success: 'text-green-600',\n warning: 'text-amber-600',\n danger: 'text-red-600',\n info: 'text-sky-600',\n};\n\nconst COLOR_STROKE: Record<StatCardColor, string> = {\n primary: 'stroke-primary',\n secondary: 'stroke-secondary-foreground',\n success: 'stroke-green-500',\n warning: 'stroke-amber-500',\n danger: 'stroke-red-500',\n info: 'stroke-sky-500',\n};\n\ninterface SparklineProps {\n points: number[];\n color: StatCardColor;\n}\n\nfunction Sparkline({ points, color }: SparklineProps) {\n const t = useArqelTranslations();\n if (points.length < 2) return null;\n\n const min = Math.min(...points);\n const max = Math.max(...points);\n const range = max - min || 1;\n const stepX = 100 / (points.length - 1);\n\n const coords = points\n .map((value, index) => {\n const x = index * stepX;\n // Invert Y so larger values render higher in the viewBox.\n const y = 30 - ((value - min) / range) * 30;\n return `${x.toFixed(2)},${y.toFixed(2)}`;\n })\n .join(' ');\n\n return (\n <svg\n viewBox=\"0 0 100 30\"\n preserveAspectRatio=\"none\"\n className={cn('mt-3 h-8 w-full', COLOR_STROKE[color])}\n role=\"img\"\n aria-label={t('arqel.aria.stat_sparkline', 'Trend sparkline')}\n data-testid=\"stat-card-sparkline\"\n >\n <polyline\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n points={coords}\n />\n </svg>\n );\n}\n\nexport function StatCard({ widget, className, columnSpan }: StatCardProps) {\n const { heading, description, value, statDescription, descriptionIcon, color, chart, url } =\n widget;\n const locale = useArqelLocale();\n const numberFormatter = useMemo(() => new Intl.NumberFormat(locale), [locale]);\n\n // Numeric KPI values are grouped/decimal-formatted for the active locale\n // (e.g. `1234567` → `1.234.567` under pt-BR). Strings pass through verbatim\n // so server-formatted values (currency, percentages) stay intact.\n const displayValue =\n typeof value === 'number' && Number.isFinite(value)\n ? numberFormatter.format(value)\n : (value ?? '—');\n\n const hasChart = Array.isArray(chart) && chart.length >= 2;\n\n const body: ReactNode = (\n <>\n <div className={cn('text-4xl font-bold tabular-nums', COLOR_TEXT[color])}>{displayValue}</div>\n {statDescription && (\n <div className=\"mt-2 flex items-center gap-1 text-xs text-muted-foreground\">\n {descriptionIcon && (\n <span aria-hidden=\"true\" data-testid=\"stat-card-icon\">\n {/* Icon name passed through; an icon registry resolves it app-side. */}\n {descriptionIcon}\n </span>\n )}\n <span>{statDescription}</span>\n </div>\n )}\n {hasChart && <Sparkline points={chart as number[]} color={color} />}\n </>\n );\n\n const inner = url ? (\n <a\n href={url}\n className=\"block rounded outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n data-testid=\"stat-card-link\"\n >\n {body}\n </a>\n ) : (\n body\n );\n\n return (\n <WidgetWrapper\n heading={heading}\n description={description}\n columnSpan={columnSpan}\n className={className}\n >\n {inner}\n </WidgetWrapper>\n );\n}\n"]}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { colorFor } from './chunk-L3JBBN4H.js';
|
|
2
|
+
import { useArqelLocale, useArqelTranslations } from '@arqel-dev/react/utils';
|
|
3
|
+
import { useMemo } from 'react';
|
|
2
4
|
import { ResponsiveContainer, PieChart as PieChart$1, Pie, Cell, Tooltip, Legend } from 'recharts';
|
|
3
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
6
|
|
|
@@ -9,9 +11,12 @@ function PieChart({
|
|
|
9
11
|
innerRadius = 0,
|
|
10
12
|
testId = "chart-pie"
|
|
11
13
|
}) {
|
|
14
|
+
const locale = useArqelLocale();
|
|
15
|
+
const t = useArqelTranslations();
|
|
16
|
+
const nf = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
12
17
|
const first = chartData.datasets[0];
|
|
13
18
|
const slices = (first?.data ?? []).map((value, i) => ({
|
|
14
|
-
name: chartData.labels[i] ??
|
|
19
|
+
name: chartData.labels[i] ?? t("arqel.chart.slice_fallback", "Slice :number", { number: i + 1 }),
|
|
15
20
|
value
|
|
16
21
|
}));
|
|
17
22
|
return /* @__PURE__ */ jsx("div", { "data-testid": testId, style: { width: "100%", height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(PieChart$1, { children: [
|
|
@@ -23,15 +28,15 @@ function PieChart({
|
|
|
23
28
|
nameKey: "name",
|
|
24
29
|
innerRadius,
|
|
25
30
|
outerRadius: "80%",
|
|
26
|
-
label:
|
|
31
|
+
label: (entry) => nf.format(Number(entry.value)),
|
|
27
32
|
children: slices.map((slice, i) => /* @__PURE__ */ jsx(Cell, { fill: colorFor(first ?? { label: "", data: [] }, i) }, slice.name))
|
|
28
33
|
}
|
|
29
34
|
),
|
|
30
|
-
/* @__PURE__ */ jsx(Tooltip, {}),
|
|
35
|
+
/* @__PURE__ */ jsx(Tooltip, { formatter: (v) => nf.format(Number(v)) }),
|
|
31
36
|
showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null
|
|
32
37
|
] }) }) });
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
export { PieChart };
|
|
36
|
-
//# sourceMappingURL=chunk-
|
|
37
|
-
//# sourceMappingURL=chunk-
|
|
41
|
+
//# sourceMappingURL=chunk-WZRPGPLH.js.map
|
|
42
|
+
//# sourceMappingURL=chunk-WZRPGPLH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/widgets/charts/PieChart.tsx"],"names":["RechartsPieChart"],"mappings":";;;;;;AAiBO,SAAS,QAAA,CAAS;AAAA,EACvB,SAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA,GAAc,CAAA;AAAA,EACd,MAAA,GAAS;AACX,CAAA,EAAkB;AAChB,EAAA,MAAM,SAAS,cAAA,EAAe;AAC9B,EAAA,MAAM,IAAI,oBAAA,EAAqB;AAC/B,EAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,MAAM,IAAI,IAAA,CAAK,aAAa,MAAM,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAChE,EAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,QAAA,CAAS,CAAC,CAAA;AAClC,EAAA,MAAM,MAAA,GAAA,CAAU,OAAO,IAAA,IAAQ,IAAI,GAAA,CAAI,CAAC,OAAO,CAAA,MAAO;AAAA,IACpD,IAAA,EACE,SAAA,CAAU,MAAA,CAAO,CAAC,CAAA,IAAK,CAAA,CAAE,4BAAA,EAA8B,eAAA,EAAiB,EAAE,MAAA,EAAQ,CAAA,GAAI,CAAA,EAAG,CAAA;AAAA,IAC3F;AAAA,GACF,CAAE,CAAA;AAEF,EAAA,2BACG,KAAA,EAAA,EAAI,aAAA,EAAa,MAAA,EAAQ,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAO,EACvD,8BAAC,mBAAA,EAAA,EAAoB,KAAA,EAAM,QAAO,MAAA,EAAO,MAAA,EACvC,+BAACA,UAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,MAAA;AAAA,QACN,OAAA,EAAQ,OAAA;AAAA,QACR,OAAA,EAAQ,MAAA;AAAA,QACR,WAAA;AAAA,QACA,WAAA,EAAY,KAAA;AAAA,QACZ,KAAA,EAAO,CAAC,KAAA,KAAU,EAAA,CAAG,OAAO,MAAA,CAAO,KAAA,CAAM,KAAK,CAAC,CAAA;AAAA,QAE9C,QAAA,EAAA,MAAA,CAAO,IAAI,CAAC,KAAA,EAAO,sBAClB,GAAA,CAAC,IAAA,EAAA,EAAsB,MAAM,QAAA,CAAS,KAAA,IAAS,EAAE,KAAA,EAAO,EAAA,EAAI,MAAM,EAAC,IAAK,CAAC,CAAA,EAAA,EAA9D,KAAA,CAAM,IAA2D,CAC7E;AAAA;AAAA,KACH;AAAA,oBACA,GAAA,CAAC,OAAA,EAAA,EAAQ,SAAA,EAAW,CAAC,CAAA,KAAM,GAAG,MAAA,CAAO,MAAA,CAAO,CAAC,CAAC,CAAA,EAAG,CAAA;AAAA,IAChD,UAAA,mBAAa,GAAA,CAAC,MAAA,EAAA,EAAO,CAAA,GAAK;AAAA,GAAA,EAC7B,GACF,CAAA,EACF,CAAA;AAEJ","file":"chunk-WZRPGPLH.js","sourcesContent":["import { useArqelLocale, useArqelTranslations } from '@arqel-dev/react/utils';\nimport { useMemo } from 'react';\nimport {\n Cell,\n Legend,\n Pie,\n PieChart as RechartsPieChart,\n ResponsiveContainer,\n Tooltip,\n} from 'recharts';\nimport { type ChartSubProps, colorFor } from '../types';\n\nexport interface PieChartProps extends ChartSubProps {\n innerRadius?: number | undefined;\n testId?: string | undefined;\n}\n\nexport function PieChart({\n chartData,\n height,\n showLegend,\n innerRadius = 0,\n testId = 'chart-pie',\n}: PieChartProps) {\n const locale = useArqelLocale();\n const t = useArqelTranslations();\n const nf = useMemo(() => new Intl.NumberFormat(locale), [locale]);\n const first = chartData.datasets[0];\n const slices = (first?.data ?? []).map((value, i) => ({\n name:\n chartData.labels[i] ?? t('arqel.chart.slice_fallback', 'Slice :number', { number: i + 1 }),\n value,\n }));\n\n return (\n <div data-testid={testId} style={{ width: '100%', height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsPieChart>\n <Pie\n data={slices}\n dataKey=\"value\"\n nameKey=\"name\"\n innerRadius={innerRadius}\n outerRadius=\"80%\"\n label={(entry) => nf.format(Number(entry.value))}\n >\n {slices.map((slice, i) => (\n <Cell key={slice.name} fill={colorFor(first ?? { label: '', data: [] }, i)} />\n ))}\n </Pie>\n <Tooltip formatter={(v) => nf.format(Number(v))} />\n {showLegend ? <Legend /> : null}\n </RechartsPieChart>\n </ResponsiveContainer>\n </div>\n );\n}\n"]}
|