@marktiderman/genesis-ui 2.15.0 → 2.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/SKILL.md +2 -1
- package/dist/{AppShell-HCXpi6nv.d.ts → AppShell-DDyVqNmJ.d.ts} +12 -2
- package/dist/{chunk-46TZGMFW.js → chunk-24NUW3QN.js} +22 -22
- package/dist/chunk-24NUW3QN.js.map +1 -0
- package/dist/{chunk-DTQHAZWN.js → chunk-3FKF2SN4.js} +19 -5
- package/dist/chunk-3FKF2SN4.js.map +1 -0
- package/dist/{chunk-25XEXYGC.js → chunk-OT6KKSD2.js} +20 -20
- package/dist/chunk-OT6KKSD2.js.map +1 -0
- package/dist/{chunk-STZVZUYO.js → chunk-TQ7OJDSU.js} +8 -6
- package/dist/chunk-TQ7OJDSU.js.map +1 -0
- package/dist/chunk-YCQAYWMX.js +192 -0
- package/dist/chunk-YCQAYWMX.js.map +1 -0
- package/dist/components/data/index.d.ts +6 -6
- package/dist/components/data/index.js +170 -123
- package/dist/components/data/index.js.map +1 -1
- package/dist/components/layout/detail-page.d.ts +1 -1
- package/dist/components/layout/index.d.ts +27 -11
- package/dist/components/layout/index.js +73 -23
- package/dist/components/layout/index.js.map +1 -1
- package/dist/components/layout/settings-page.d.ts +1 -1
- package/dist/components/patterns/index.js +3 -2
- package/dist/components/patterns/sortable-list.js +2 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +2 -1
- package/dist/hooks/use-resource-page.d.ts +1 -1
- package/dist/index.d.ts +176 -2
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/{split-Dk9f4Mi0.d.ts → split-D-uYckQ_.d.ts} +1 -1
- package/dist/{use-resource-page-DaJzuq3M.d.ts → use-resource-page-BA1P7xea.d.ts} +1 -1
- package/dist/{use-resource-table-easmRDQI.d.ts → use-resource-table-WDqz7oQ-.d.ts} +17 -3
- package/package.json +2 -2
- package/dist/chunk-25XEXYGC.js.map +0 -1
- package/dist/chunk-46TZGMFW.js.map +0 -1
- package/dist/chunk-DTQHAZWN.js.map +0 -1
- package/dist/chunk-STZVZUYO.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { AppShell, PageHeader } from '../../chunk-
|
|
3
|
-
export { ViewSettings, ViewToggle } from '../../chunk-
|
|
2
|
+
import { AppShell, PageHeader } from '../../chunk-3FKF2SN4.js';
|
|
3
|
+
export { ViewSettings, ViewToggle } from '../../chunk-24NUW3QN.js';
|
|
4
4
|
import { slots_exports } from '../../chunk-WALJMVEF.js';
|
|
5
5
|
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from '../../chunk-N2ZFNOOT.js';
|
|
6
6
|
import { Progress } from '../../chunk-W5Y3LG5K.js';
|
|
@@ -30,8 +30,8 @@ import '../../chunk-F47U5KNW.js';
|
|
|
30
30
|
import { DetailPage } from '../../chunk-AFZ76Q26.js';
|
|
31
31
|
import '../../chunk-D2LIXA2Y.js';
|
|
32
32
|
import '../../chunk-AVFZ4P4B.js';
|
|
33
|
-
import { showUndoToast, showWriteErrorToast, useResourceTableModel } from '../../chunk-
|
|
34
|
-
export { showUndoToast, showWriteErrorToast, useResourceTable } from '../../chunk-
|
|
33
|
+
import { showUndoToast, showWriteErrorToast, useResourceTableModel } from '../../chunk-TQ7OJDSU.js';
|
|
34
|
+
export { showUndoToast, showWriteErrorToast, useResourceTable } from '../../chunk-TQ7OJDSU.js';
|
|
35
35
|
import { titleCase } from '../../chunk-RKPS7ERS.js';
|
|
36
36
|
import '../../chunk-DIV4BYXX.js';
|
|
37
37
|
export { useSavedViews } from '../../chunk-GCQFVMPI.js';
|
|
@@ -41,6 +41,7 @@ export { CommandPalette, useCommandPaletteHotkey } from '../../chunk-CNTWQSUT.js
|
|
|
41
41
|
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '../../chunk-SEFK4NZV.js';
|
|
42
42
|
import '../../chunk-4RAZJ6LL.js';
|
|
43
43
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from '../../chunk-7XMZVISM.js';
|
|
44
|
+
import { useUiMessages, fillMessage } from '../../chunk-YCQAYWMX.js';
|
|
44
45
|
import { cn } from '../../chunk-NZ5WBA4R.js';
|
|
45
46
|
import { DefaultLink } from '../../chunk-J45FHL7D.js';
|
|
46
47
|
export { DefaultLink, defaultNavigate, isPathActive, resolveNavigation } from '../../chunk-J45FHL7D.js';
|
|
@@ -98,6 +99,7 @@ function DataPageShell({
|
|
|
98
99
|
className,
|
|
99
100
|
header
|
|
100
101
|
}) {
|
|
102
|
+
const m = useUiMessages();
|
|
101
103
|
const entity = entityName || title.toLowerCase();
|
|
102
104
|
const collapseFilters = filtersCollapsible && filters != null && header !== null;
|
|
103
105
|
const toolbarOnly = header === "toolbar";
|
|
@@ -148,7 +150,7 @@ function DataPageShell({
|
|
|
148
150
|
// Shaped like the view it stands in for, so a list inside a panel or
|
|
149
151
|
// tab doesn't reserve a page of card placeholders.
|
|
150
152
|
/* @__PURE__ */ jsxs("div", { "data-testid": "page-loader", role: "status", "aria-busy": "true", children: [
|
|
151
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children:
|
|
153
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: m.common.loading }),
|
|
152
154
|
viewMode === "grid" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx(
|
|
153
155
|
Skeleton,
|
|
154
156
|
{
|
|
@@ -172,11 +174,12 @@ function DataPageShell({
|
|
|
172
174
|
className: "flex flex-col items-center justify-center py-16 text-center",
|
|
173
175
|
children: [
|
|
174
176
|
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-10 w-10 text-destructive mb-3", "aria-hidden": true }),
|
|
175
|
-
/* @__PURE__ */ jsx("p", { className: "mb-1 text-sm font-medium text-foreground", children:
|
|
176
|
-
/* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: errorMessage2 ||
|
|
177
|
+
/* @__PURE__ */ jsx("p", { className: "mb-1 text-sm font-medium text-foreground", children: m.page.errorTitle }),
|
|
178
|
+
/* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: errorMessage2 || fillMessage(m.page.errorBody, { entity }) }),
|
|
177
179
|
onRetry && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, children: [
|
|
178
180
|
/* @__PURE__ */ jsx(RefreshCw, { className: "mr-1 h-4 w-4", "aria-hidden": true }),
|
|
179
|
-
"
|
|
181
|
+
" ",
|
|
182
|
+
m.page.retry
|
|
180
183
|
] })
|
|
181
184
|
]
|
|
182
185
|
}
|
|
@@ -294,7 +297,7 @@ function FilterCombobox({
|
|
|
294
297
|
function DataFilters({
|
|
295
298
|
search,
|
|
296
299
|
onSearchChange,
|
|
297
|
-
searchPlaceholder
|
|
300
|
+
searchPlaceholder: searchPlaceholderProp,
|
|
298
301
|
sort,
|
|
299
302
|
onSortChange,
|
|
300
303
|
sortOptions,
|
|
@@ -311,6 +314,8 @@ function DataFilters({
|
|
|
311
314
|
showSearch = true,
|
|
312
315
|
showSort = true
|
|
313
316
|
}) {
|
|
317
|
+
const m = useUiMessages();
|
|
318
|
+
const searchPlaceholder = searchPlaceholderProp ?? m.filters.search;
|
|
314
319
|
const internalSearchRef = useRef(null);
|
|
315
320
|
const effectiveSearchRef = searchInputRef || internalSearchRef;
|
|
316
321
|
const [viewsOpen, setViewsOpen] = useState(false);
|
|
@@ -341,7 +346,7 @@ function DataFilters({
|
|
|
341
346
|
{
|
|
342
347
|
className: "flex items-center border rounded-lg overflow-x-auto w-fit max-w-full",
|
|
343
348
|
role: "group",
|
|
344
|
-
"aria-label":
|
|
349
|
+
"aria-label": m.filters.status,
|
|
345
350
|
"data-testid": "status-tabs",
|
|
346
351
|
children: statusOptions.map((o) => {
|
|
347
352
|
const pressed = statusChips.selected.includes(o.value);
|
|
@@ -390,7 +395,7 @@ function DataFilters({
|
|
|
390
395
|
showSort && /* @__PURE__ */ jsxs(Select, { value: sort, onValueChange: onSortChange, children: [
|
|
391
396
|
/* @__PURE__ */ jsxs(SelectTrigger, { className: "w-full sm:w-[180px] shrink-0", children: [
|
|
392
397
|
/* @__PURE__ */ jsx(ArrowUpDown, { className: "h-3.5 w-3.5 mr-1.5 text-muted-foreground" }),
|
|
393
|
-
/* @__PURE__ */ jsx(SelectValue, { placeholder:
|
|
398
|
+
/* @__PURE__ */ jsx(SelectValue, { placeholder: m.filters.sortBy })
|
|
394
399
|
] }),
|
|
395
400
|
/* @__PURE__ */ jsx(SelectContent, { className: "bg-popover z-50", children: sortOptions.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
|
|
396
401
|
] })
|
|
@@ -405,12 +410,8 @@ function DataFilters({
|
|
|
405
410
|
className: "text-xs h-7 gap-1.5",
|
|
406
411
|
children: [
|
|
407
412
|
/* @__PURE__ */ jsx(Bookmark, { className: "h-3 w-3" }),
|
|
408
|
-
|
|
409
|
-
savedViews.length > 0 && /* @__PURE__ */
|
|
410
|
-
"(",
|
|
411
|
-
savedViews.length,
|
|
412
|
-
")"
|
|
413
|
-
] }),
|
|
413
|
+
m.filters.views,
|
|
414
|
+
savedViews.length > 0 && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground ml-0.5", children: fillMessage(m.filters.viewCount, { count: savedViews.length }) }),
|
|
414
415
|
/* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 ml-0.5 opacity-60" })
|
|
415
416
|
]
|
|
416
417
|
}
|
|
@@ -442,7 +443,7 @@ function DataFilters({
|
|
|
442
443
|
}
|
|
443
444
|
)
|
|
444
445
|
] }, view.id)),
|
|
445
|
-
savedViews.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground px-2 py-1", children:
|
|
446
|
+
savedViews.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground px-2 py-1", children: m.filters.noViews }),
|
|
446
447
|
canSaveViews && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
447
448
|
/* @__PURE__ */ jsx("div", { className: "border-t my-1" }),
|
|
448
449
|
/* @__PURE__ */ jsxs(
|
|
@@ -455,7 +456,7 @@ function DataFilters({
|
|
|
455
456
|
},
|
|
456
457
|
children: [
|
|
457
458
|
/* @__PURE__ */ jsx(BookmarkPlus, { className: "h-3.5 w-3.5" }),
|
|
458
|
-
|
|
459
|
+
m.filters.saveCurrent
|
|
459
460
|
]
|
|
460
461
|
}
|
|
461
462
|
)
|
|
@@ -525,18 +526,18 @@ function DataFilters({
|
|
|
525
526
|
onClick: onClearAll,
|
|
526
527
|
children: [
|
|
527
528
|
/* @__PURE__ */ jsx(X, { className: "h-3 w-3 mr-1" }),
|
|
528
|
-
|
|
529
|
+
m.filters.clearAll
|
|
529
530
|
]
|
|
530
531
|
}
|
|
531
532
|
)
|
|
532
533
|
] }),
|
|
533
534
|
/* @__PURE__ */ jsx(Dialog, { open: saveDialogOpen, onOpenChange: setSaveDialogOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-sm", children: [
|
|
534
|
-
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { className: "font-display", children:
|
|
535
|
+
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { className: "font-display", children: m.filters.saveViewTitle }) }),
|
|
535
536
|
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
536
537
|
/* @__PURE__ */ jsx(
|
|
537
538
|
Input,
|
|
538
539
|
{
|
|
539
|
-
placeholder:
|
|
540
|
+
placeholder: m.filters.viewNamePlaceholder,
|
|
540
541
|
value: newViewName,
|
|
541
542
|
onChange: (e) => setNewViewName(e.target.value),
|
|
542
543
|
onKeyDown: (e) => e.key === "Enter" && handleSaveView(),
|
|
@@ -550,7 +551,7 @@ function DataFilters({
|
|
|
550
551
|
variant: "outline",
|
|
551
552
|
size: "sm",
|
|
552
553
|
onClick: () => setSaveDialogOpen(false),
|
|
553
|
-
children:
|
|
554
|
+
children: m.common.cancel
|
|
554
555
|
}
|
|
555
556
|
),
|
|
556
557
|
/* @__PURE__ */ jsx(
|
|
@@ -559,7 +560,7 @@ function DataFilters({
|
|
|
559
560
|
size: "sm",
|
|
560
561
|
onClick: handleSaveView,
|
|
561
562
|
disabled: !newViewName.trim(),
|
|
562
|
-
children:
|
|
563
|
+
children: m.common.save
|
|
563
564
|
}
|
|
564
565
|
)
|
|
565
566
|
] })
|
|
@@ -762,6 +763,7 @@ function EditableCell({
|
|
|
762
763
|
column,
|
|
763
764
|
value
|
|
764
765
|
}) {
|
|
766
|
+
const m = useUiMessages();
|
|
765
767
|
const [editing, setEditing] = useState(false);
|
|
766
768
|
const [draft, setDraft] = useState(String(value ?? ""));
|
|
767
769
|
const [saving, setSaving] = useState(false);
|
|
@@ -820,7 +822,7 @@ function EditableCell({
|
|
|
820
822
|
failedDraft.current = null;
|
|
821
823
|
setEditing(true);
|
|
822
824
|
},
|
|
823
|
-
children: column.render ? column.render(item, value) : String(value ??
|
|
825
|
+
children: column.render ? column.render(item, value) : String(value ?? m.common.empty)
|
|
824
826
|
}
|
|
825
827
|
);
|
|
826
828
|
}
|
|
@@ -857,7 +859,7 @@ function EditableCell({
|
|
|
857
859
|
type: "button",
|
|
858
860
|
className: "mt-0.5 text-[11px] text-muted-foreground underline-offset-2 hover:underline",
|
|
859
861
|
onClick: close,
|
|
860
|
-
children:
|
|
862
|
+
children: m.common.cancel
|
|
861
863
|
}
|
|
862
864
|
)
|
|
863
865
|
]
|
|
@@ -886,7 +888,7 @@ function EditableCell({
|
|
|
886
888
|
}
|
|
887
889
|
}
|
|
888
890
|
),
|
|
889
|
-
saving && /* @__PURE__ */ jsx(Spinner, { className: "absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2", "aria-label":
|
|
891
|
+
saving && /* @__PURE__ */ jsx(Spinner, { className: "absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2", "aria-label": m.common.saving })
|
|
890
892
|
] }),
|
|
891
893
|
feedback
|
|
892
894
|
] });
|
|
@@ -945,7 +947,7 @@ function DataTable({
|
|
|
945
947
|
onToggleSelect,
|
|
946
948
|
onToggleAll,
|
|
947
949
|
renderActions,
|
|
948
|
-
emptyMessage
|
|
950
|
+
emptyMessage: emptyMessageProp,
|
|
949
951
|
columnVisibility = true,
|
|
950
952
|
columnVisibilityKey,
|
|
951
953
|
resizableColumns = true,
|
|
@@ -965,6 +967,8 @@ function DataTable({
|
|
|
965
967
|
sort: sortProp,
|
|
966
968
|
onSortChange
|
|
967
969
|
}) {
|
|
970
|
+
const m = useUiMessages();
|
|
971
|
+
const emptyMessage = emptyMessageProp ?? m.table.noRecords;
|
|
968
972
|
const rowKeys = keyboardNavigation && !!onRowClick;
|
|
969
973
|
const [activeRowKey, setActiveRowKey] = useState(null);
|
|
970
974
|
const cellDensity = DENSITY_CELL[density];
|
|
@@ -1254,7 +1258,7 @@ function DataTable({
|
|
|
1254
1258
|
children: /* @__PURE__ */ jsx(
|
|
1255
1259
|
Checkbox,
|
|
1256
1260
|
{
|
|
1257
|
-
"aria-label":
|
|
1261
|
+
"aria-label": fillMessage(m.table.selectRow, { name: rowLabel(item, id) }),
|
|
1258
1262
|
checked: selected?.has(id),
|
|
1259
1263
|
onCheckedChange: () => onToggleSelect?.(id)
|
|
1260
1264
|
}
|
|
@@ -1284,7 +1288,7 @@ function DataTable({
|
|
|
1284
1288
|
column: col,
|
|
1285
1289
|
value
|
|
1286
1290
|
}
|
|
1287
|
-
) : col.render ? col.render(item, value) : /* @__PURE__ */ jsx("span", { className: "truncate block max-w-[300px]", children: String(value ??
|
|
1291
|
+
) : col.render ? col.render(item, value) : /* @__PURE__ */ jsx("span", { className: "truncate block max-w-[300px]", children: String(value ?? m.common.empty) });
|
|
1288
1292
|
if (col.key !== openKey) return content;
|
|
1289
1293
|
const openProps = {
|
|
1290
1294
|
type: "button",
|
|
@@ -1328,9 +1332,9 @@ function DataTable({
|
|
|
1328
1332
|
"button",
|
|
1329
1333
|
{
|
|
1330
1334
|
...openProps,
|
|
1331
|
-
"aria-label":
|
|
1335
|
+
"aria-label": fillMessage(m.common.openItem, { name: rowLabel(item, id) }),
|
|
1332
1336
|
className: "sr-only shrink-0 rounded px-1 text-xs text-muted-foreground focus-visible:not-sr-only focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
|
|
1333
|
-
children:
|
|
1337
|
+
children: m.common.open
|
|
1334
1338
|
}
|
|
1335
1339
|
)
|
|
1336
1340
|
] });
|
|
@@ -1363,7 +1367,7 @@ function DataTable({
|
|
|
1363
1367
|
className: "h-7 text-xs gap-1.5",
|
|
1364
1368
|
children: [
|
|
1365
1369
|
/* @__PURE__ */ jsx(Columns3, { className: "h-3.5 w-3.5" }),
|
|
1366
|
-
|
|
1370
|
+
m.table.columns
|
|
1367
1371
|
]
|
|
1368
1372
|
}
|
|
1369
1373
|
) }),
|
|
@@ -1385,7 +1389,7 @@ function DataTable({
|
|
|
1385
1389
|
) : null
|
|
1386
1390
|
),
|
|
1387
1391
|
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
1388
|
-
/* @__PURE__ */ jsx(DropdownMenuItem, { onClick: resetColumnVisibility, children:
|
|
1392
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, { onClick: resetColumnVisibility, children: m.table.resetColumns })
|
|
1389
1393
|
]
|
|
1390
1394
|
}
|
|
1391
1395
|
)
|
|
@@ -1430,7 +1434,7 @@ function DataTable({
|
|
|
1430
1434
|
children: /* @__PURE__ */ jsx(
|
|
1431
1435
|
Checkbox,
|
|
1432
1436
|
{
|
|
1433
|
-
"aria-label":
|
|
1437
|
+
"aria-label": m.table.selectAll,
|
|
1434
1438
|
checked: items.length > 0 && selected?.size === items.length,
|
|
1435
1439
|
onCheckedChange: onToggleAll
|
|
1436
1440
|
}
|
|
@@ -1506,7 +1510,11 @@ function DataTable({
|
|
|
1506
1510
|
e.preventDefault();
|
|
1507
1511
|
moveColumn(col.key, e.key === "ArrowLeft" ? { before: neighbour.key } : { after: neighbour.key });
|
|
1508
1512
|
},
|
|
1509
|
-
"aria-label": sortEntry && sortIndex > 0 ?
|
|
1513
|
+
"aria-label": sortEntry && sortIndex > 0 ? fillMessage(m.table.sortedBy, {
|
|
1514
|
+
header: String(col.header),
|
|
1515
|
+
direction: sortEntry.dir === "asc" ? m.table.ascending : m.table.descending,
|
|
1516
|
+
n: sortIndex + 1
|
|
1517
|
+
}) : void 0,
|
|
1510
1518
|
children: label
|
|
1511
1519
|
}
|
|
1512
1520
|
) : label;
|
|
@@ -1566,10 +1574,10 @@ function DataTable({
|
|
|
1566
1574
|
}
|
|
1567
1575
|
),
|
|
1568
1576
|
pagination && /* @__PURE__ */ jsxs("div", { className: "sticky bottom-0 z-10 flex flex-col sm:flex-row items-center justify-between gap-2 px-4 py-3 border-t bg-muted/30 text-xs text-muted-foreground", children: [
|
|
1569
|
-
/* @__PURE__ */ jsx("span", { children: totalItems === 0 ?
|
|
1577
|
+
/* @__PURE__ */ jsx("span", { children: totalItems === 0 ? m.table.noRows : fillMessage(m.table.range, { from: showingFrom, to: showingTo, total: totalItems }) }),
|
|
1570
1578
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1571
1579
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
1572
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
1580
|
+
/* @__PURE__ */ jsx("span", { children: m.table.rows }),
|
|
1573
1581
|
/* @__PURE__ */ jsxs(
|
|
1574
1582
|
Select,
|
|
1575
1583
|
{
|
|
@@ -1594,11 +1602,7 @@ function DataTable({
|
|
|
1594
1602
|
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5" })
|
|
1595
1603
|
}
|
|
1596
1604
|
),
|
|
1597
|
-
/* @__PURE__ */
|
|
1598
|
-
currentPage + 1,
|
|
1599
|
-
" / ",
|
|
1600
|
-
totalPages
|
|
1601
|
-
] }),
|
|
1605
|
+
/* @__PURE__ */ jsx("span", { className: "px-2 tabular-nums", children: fillMessage(m.table.pageOf, { page: currentPage + 1, pages: totalPages }) }),
|
|
1602
1606
|
/* @__PURE__ */ jsx(
|
|
1603
1607
|
Button,
|
|
1604
1608
|
{
|
|
@@ -1619,8 +1623,9 @@ function ThumbnailCell({
|
|
|
1619
1623
|
src,
|
|
1620
1624
|
alt
|
|
1621
1625
|
}) {
|
|
1626
|
+
const m = useUiMessages();
|
|
1622
1627
|
if (!src) {
|
|
1623
|
-
return /* @__PURE__ */ jsx("div", { className: "h-10 w-14 rounded bg-muted flex items-center justify-center text-[10px] text-muted-foreground", children:
|
|
1628
|
+
return /* @__PURE__ */ jsx("div", { className: "h-10 w-14 rounded bg-muted flex items-center justify-center text-[10px] text-muted-foreground", children: m.common.empty });
|
|
1624
1629
|
}
|
|
1625
1630
|
return /* @__PURE__ */ jsx(
|
|
1626
1631
|
"img",
|
|
@@ -1636,7 +1641,8 @@ function LinkCell({
|
|
|
1636
1641
|
href,
|
|
1637
1642
|
label
|
|
1638
1643
|
}) {
|
|
1639
|
-
|
|
1644
|
+
const m = useUiMessages();
|
|
1645
|
+
if (!href) return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: m.common.empty });
|
|
1640
1646
|
return /* @__PURE__ */ jsxs(
|
|
1641
1647
|
"a",
|
|
1642
1648
|
{
|
|
@@ -1660,6 +1666,7 @@ function DataBulkBar({
|
|
|
1660
1666
|
actions,
|
|
1661
1667
|
className
|
|
1662
1668
|
}) {
|
|
1669
|
+
const m = useUiMessages();
|
|
1663
1670
|
if (selected.size === 0) return null;
|
|
1664
1671
|
return /* @__PURE__ */ jsxs(
|
|
1665
1672
|
"div",
|
|
@@ -1676,10 +1683,7 @@ function DataBulkBar({
|
|
|
1676
1683
|
onCheckedChange: onToggleAll
|
|
1677
1684
|
}
|
|
1678
1685
|
),
|
|
1679
|
-
/* @__PURE__ */
|
|
1680
|
-
selected.size,
|
|
1681
|
-
" selected"
|
|
1682
|
-
] }),
|
|
1686
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", children: fillMessage(m.bulk.selected, { count: selected.size }) }),
|
|
1683
1687
|
actions.map((action) => {
|
|
1684
1688
|
const ActionIcon = action.icon;
|
|
1685
1689
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1705,7 +1709,7 @@ function DataBulkBar({
|
|
|
1705
1709
|
variant: "ghost",
|
|
1706
1710
|
onClick: onClearSelection,
|
|
1707
1711
|
className: "ml-auto text-xs text-muted-foreground",
|
|
1708
|
-
children:
|
|
1712
|
+
children: m.common.clear
|
|
1709
1713
|
}
|
|
1710
1714
|
)
|
|
1711
1715
|
]
|
|
@@ -1816,7 +1820,7 @@ var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useL
|
|
|
1816
1820
|
function KanbanBoard({
|
|
1817
1821
|
columns,
|
|
1818
1822
|
renderCard,
|
|
1819
|
-
emptyMessage
|
|
1823
|
+
emptyMessage: emptyMessageProp,
|
|
1820
1824
|
className,
|
|
1821
1825
|
getKey,
|
|
1822
1826
|
onMoveItem,
|
|
@@ -1826,9 +1830,17 @@ function KanbanBoard({
|
|
|
1826
1830
|
const [dragging, setDragging] = useState(null);
|
|
1827
1831
|
const [over, setOver] = useState(null);
|
|
1828
1832
|
const [grabbed, setGrabbed] = useState(null);
|
|
1833
|
+
const m = useUiMessages();
|
|
1834
|
+
const k = m.kanban;
|
|
1835
|
+
const emptyMessage = emptyMessageProp ?? k.noItems;
|
|
1829
1836
|
const { message: announcement, announce } = useAnnouncer();
|
|
1830
1837
|
const handles = useRef(/* @__PURE__ */ new Map());
|
|
1831
1838
|
const refocus = useRef(null);
|
|
1839
|
+
const refocusMove = useRef(0);
|
|
1840
|
+
const [, setSettledTick] = useState(0);
|
|
1841
|
+
const refocusFrom = useRef(null);
|
|
1842
|
+
const refocusSettled = useRef(false);
|
|
1843
|
+
const moves = useRef(0);
|
|
1832
1844
|
const instructionsId = useId();
|
|
1833
1845
|
const keyOf = (item, index) => getKey ? getKey(item) : String(index);
|
|
1834
1846
|
const movable = Boolean(onMoveItem && getKey);
|
|
@@ -1842,10 +1854,17 @@ function KanbanBoard({
|
|
|
1842
1854
|
};
|
|
1843
1855
|
const labelOf = (key) => {
|
|
1844
1856
|
const found = find(key);
|
|
1845
|
-
return found && getItemLabel ? getItemLabel(found.item) :
|
|
1857
|
+
return found && getItemLabel ? getItemLabel(found.item) : k.card;
|
|
1846
1858
|
};
|
|
1847
1859
|
const columnLabel = (key) => columns.find((c) => c.key === key)?.label ?? key;
|
|
1848
1860
|
const move = (item, to, name) => {
|
|
1861
|
+
const id = ++moves.current;
|
|
1862
|
+
const key = getKey?.(item);
|
|
1863
|
+
if (key !== void 0 && refocus.current === key) {
|
|
1864
|
+
refocusMove.current = id;
|
|
1865
|
+
refocusFrom.current = find(key)?.column.key ?? null;
|
|
1866
|
+
refocusSettled.current = false;
|
|
1867
|
+
}
|
|
1849
1868
|
let result;
|
|
1850
1869
|
try {
|
|
1851
1870
|
result = onMoveItem?.(item, to);
|
|
@@ -1853,8 +1872,19 @@ function KanbanBoard({
|
|
|
1853
1872
|
result = Promise.reject(error);
|
|
1854
1873
|
}
|
|
1855
1874
|
void Promise.resolve(result).then(
|
|
1856
|
-
() =>
|
|
1857
|
-
|
|
1875
|
+
() => {
|
|
1876
|
+
if (key !== void 0 && refocus.current === key && refocusMove.current === id) {
|
|
1877
|
+
refocusSettled.current = true;
|
|
1878
|
+
setSettledTick((n) => n + 1);
|
|
1879
|
+
}
|
|
1880
|
+
announce(fillMessage(k.moved, { name, column: columnLabel(to) }));
|
|
1881
|
+
},
|
|
1882
|
+
() => {
|
|
1883
|
+
if (key !== void 0 && refocus.current === key && refocusMove.current === id) {
|
|
1884
|
+
refocus.current = null;
|
|
1885
|
+
}
|
|
1886
|
+
announce(fillMessage(k.moveFailed, { name }));
|
|
1887
|
+
}
|
|
1858
1888
|
);
|
|
1859
1889
|
};
|
|
1860
1890
|
const moveKey = (key, to) => {
|
|
@@ -1871,22 +1901,20 @@ function KanbanBoard({
|
|
|
1871
1901
|
};
|
|
1872
1902
|
const pickUp = (key, from) => {
|
|
1873
1903
|
setGrabbed({ key, from, target: from });
|
|
1874
|
-
announce(
|
|
1875
|
-
`${labelOf(key)} picked up in ${columnLabel(from)}. Use the left and right arrow keys to choose a column, Space to drop, Escape to cancel.`
|
|
1876
|
-
);
|
|
1904
|
+
announce(fillMessage(k.pickedUp, { name: labelOf(key), column: columnLabel(from) }));
|
|
1877
1905
|
};
|
|
1878
1906
|
const aim = (index) => {
|
|
1879
1907
|
if (!grabbed) return;
|
|
1880
1908
|
const column = columns[Math.max(0, Math.min(columns.length - 1, index))];
|
|
1881
1909
|
if (!column || column.key === grabbed.target) return;
|
|
1882
1910
|
setGrabbed({ ...grabbed, target: column.key });
|
|
1883
|
-
announce(
|
|
1911
|
+
announce(fillMessage(k.over, { column: column.label, n: columns.indexOf(column) + 1, count: columns.length }));
|
|
1884
1912
|
};
|
|
1885
1913
|
const drop = () => {
|
|
1886
1914
|
if (!grabbed) return;
|
|
1887
1915
|
setGrabbed(null);
|
|
1888
1916
|
if (grabbed.target === grabbed.from) {
|
|
1889
|
-
announce(
|
|
1917
|
+
announce(fillMessage(k.droppedBack, { name: labelOf(grabbed.key), column: columnLabel(grabbed.from) }));
|
|
1890
1918
|
return;
|
|
1891
1919
|
}
|
|
1892
1920
|
refocus.current = grabbed.key;
|
|
@@ -1895,7 +1923,7 @@ function KanbanBoard({
|
|
|
1895
1923
|
const cancel = () => {
|
|
1896
1924
|
if (!grabbed) return;
|
|
1897
1925
|
setGrabbed(null);
|
|
1898
|
-
announce(
|
|
1926
|
+
announce(fillMessage(k.cancelled, { name: labelOf(grabbed.key), column: columnLabel(grabbed.from) }));
|
|
1899
1927
|
};
|
|
1900
1928
|
useEffect(() => {
|
|
1901
1929
|
if (!grabbed) return;
|
|
@@ -1918,17 +1946,20 @@ function KanbanBoard({
|
|
|
1918
1946
|
useIsomorphicLayoutEffect(() => {
|
|
1919
1947
|
const key = refocus.current;
|
|
1920
1948
|
if (!key) return;
|
|
1921
|
-
const
|
|
1922
|
-
if (!
|
|
1923
|
-
if (handle.isConnected && document.activeElement === handle) {
|
|
1949
|
+
const found = find(key);
|
|
1950
|
+
if (!found) {
|
|
1924
1951
|
refocus.current = null;
|
|
1925
1952
|
return;
|
|
1926
1953
|
}
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
refocus.current = null;
|
|
1954
|
+
if (found.column.key === refocusFrom.current) {
|
|
1955
|
+
if (refocusSettled.current) refocus.current = null;
|
|
1956
|
+
return;
|
|
1931
1957
|
}
|
|
1958
|
+
const handle = handles.current.get(key);
|
|
1959
|
+
if (!handle) return;
|
|
1960
|
+
const active = document.activeElement;
|
|
1961
|
+
if (active === null || active === document.body) handle.focus();
|
|
1962
|
+
refocus.current = null;
|
|
1932
1963
|
});
|
|
1933
1964
|
const onHandleKeyDown = (event, key, from) => {
|
|
1934
1965
|
const delta = event.key === "ArrowLeft" ? -1 : event.key === "ArrowRight" ? 1 : 0;
|
|
@@ -1962,7 +1993,7 @@ function KanbanBoard({
|
|
|
1962
1993
|
if (event.pointerType === "mouse" || grabbed || dragging) return;
|
|
1963
1994
|
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
1964
1995
|
setDragging({ key, from, pointer: event.pointerId });
|
|
1965
|
-
announce(
|
|
1996
|
+
announce(fillMessage(k.pickedUpTouch, { name: labelOf(key), column: columnLabel(from) }));
|
|
1966
1997
|
};
|
|
1967
1998
|
const onHandlePointerMove = (event) => {
|
|
1968
1999
|
if (dragging?.pointer !== event.pointerId) return;
|
|
@@ -1976,7 +2007,7 @@ function KanbanBoard({
|
|
|
1976
2007
|
setDragging(null);
|
|
1977
2008
|
setOver(null);
|
|
1978
2009
|
if (to && to !== from) moveKey(key, to);
|
|
1979
|
-
else announce(
|
|
2010
|
+
else announce(fillMessage(k.droppedBack, { name: labelOf(key), column: columnLabel(from) }));
|
|
1980
2011
|
};
|
|
1981
2012
|
const onHandlePointerCancel = (event) => {
|
|
1982
2013
|
if (dragging?.pointer !== event.pointerId) return;
|
|
@@ -2026,7 +2057,7 @@ function KanbanBoard({
|
|
|
2026
2057
|
] }),
|
|
2027
2058
|
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 min-h-[80px]", children: column.items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center rounded-lg border border-dashed border-border p-6 text-xs text-muted-foreground", children: emptyMessage }) : column.items.map((item, i) => {
|
|
2028
2059
|
const key = keyOf(item, i);
|
|
2029
|
-
const name = getItemLabel?.(item) ??
|
|
2060
|
+
const name = getItemLabel?.(item) ?? k.cardInLabel;
|
|
2030
2061
|
const isGrabbed = grabbed?.key === key;
|
|
2031
2062
|
return /* @__PURE__ */ jsxs(
|
|
2032
2063
|
"div",
|
|
@@ -2058,7 +2089,7 @@ function KanbanBoard({
|
|
|
2058
2089
|
if (node) handles.current.set(key, node);
|
|
2059
2090
|
else handles.current.delete(key);
|
|
2060
2091
|
},
|
|
2061
|
-
"aria-label":
|
|
2092
|
+
"aria-label": fillMessage(k.pickUp, { name }),
|
|
2062
2093
|
"aria-describedby": instructionsId,
|
|
2063
2094
|
"data-grabbed": isGrabbed || void 0,
|
|
2064
2095
|
className: cn(
|
|
@@ -2082,19 +2113,19 @@ function KanbanBoard({
|
|
|
2082
2113
|
/* @__PURE__ */ jsx(
|
|
2083
2114
|
DropdownMenuTrigger,
|
|
2084
2115
|
{
|
|
2085
|
-
"aria-label":
|
|
2116
|
+
"aria-label": fillMessage(k.move, { name }),
|
|
2086
2117
|
className: "absolute right-1.5 top-1.5 rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
2087
2118
|
children: /* @__PURE__ */ jsx(ArrowRightLeft, { className: "h-3.5 w-3.5", "aria-hidden": true })
|
|
2088
2119
|
}
|
|
2089
2120
|
),
|
|
2090
2121
|
/* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
|
|
2091
|
-
/* @__PURE__ */ jsx(DropdownMenuLabel, { children:
|
|
2122
|
+
/* @__PURE__ */ jsx(DropdownMenuLabel, { children: k.moveTo }),
|
|
2092
2123
|
columns.filter((c) => c.key !== column.key).map((c) => /* @__PURE__ */ jsx(
|
|
2093
2124
|
DropdownMenuItem,
|
|
2094
2125
|
{
|
|
2095
2126
|
onSelect: () => {
|
|
2096
2127
|
refocus.current = key;
|
|
2097
|
-
move(item, c.key, getItemLabel?.(item) ??
|
|
2128
|
+
move(item, c.key, getItemLabel?.(item) ?? k.card);
|
|
2098
2129
|
},
|
|
2099
2130
|
children: c.label
|
|
2100
2131
|
},
|
|
@@ -2116,7 +2147,7 @@ function KanbanBoard({
|
|
|
2116
2147
|
}
|
|
2117
2148
|
),
|
|
2118
2149
|
movable && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2119
|
-
/* @__PURE__ */ jsx("p", { id: instructionsId, hidden: true, children:
|
|
2150
|
+
/* @__PURE__ */ jsx("p", { id: instructionsId, hidden: true, children: k.instructions }),
|
|
2120
2151
|
/* @__PURE__ */ jsx("div", { "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: announcement })
|
|
2121
2152
|
] })
|
|
2122
2153
|
] });
|
|
@@ -2152,15 +2183,15 @@ var dialogMaxWidthClasses = {
|
|
|
2152
2183
|
function keyToLabel(key) {
|
|
2153
2184
|
return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2154
2185
|
}
|
|
2155
|
-
function formatValue(value) {
|
|
2156
|
-
if (value === null || value === void 0) return
|
|
2157
|
-
if (typeof value === "boolean") return value ?
|
|
2186
|
+
function formatValue(value, m) {
|
|
2187
|
+
if (value === null || value === void 0) return m.common.empty;
|
|
2188
|
+
if (typeof value === "boolean") return value ? m.common.yes : m.common.no;
|
|
2158
2189
|
if (value instanceof Date) return value.toLocaleDateString();
|
|
2159
2190
|
if (typeof value === "object") return JSON.stringify(value);
|
|
2160
2191
|
return String(value);
|
|
2161
2192
|
}
|
|
2162
|
-
function renderTitleBlock(layout, title, subtitle) {
|
|
2163
|
-
const titleText = title ||
|
|
2193
|
+
function renderTitleBlock(layout, title, subtitle, fallbackTitle) {
|
|
2194
|
+
const titleText = title || fallbackTitle;
|
|
2164
2195
|
if (layout === "dialog") {
|
|
2165
2196
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2166
2197
|
/* @__PURE__ */ jsx(DialogTitle, { className: cn("text-sm font-semibold truncate", !title && "sr-only"), children: titleText }),
|
|
@@ -2203,6 +2234,7 @@ function DetailPanel({
|
|
|
2203
2234
|
width = "md",
|
|
2204
2235
|
layout = "sheet"
|
|
2205
2236
|
}) {
|
|
2237
|
+
const m = useUiMessages();
|
|
2206
2238
|
if (!open || !item) return null;
|
|
2207
2239
|
const renderContent = () => {
|
|
2208
2240
|
if (children) {
|
|
@@ -2213,7 +2245,7 @@ function DetailPanel({
|
|
|
2213
2245
|
index > 0 && /* @__PURE__ */ jsx(Separator, {}),
|
|
2214
2246
|
/* @__PURE__ */ jsxs("div", { className: "py-3", children: [
|
|
2215
2247
|
/* @__PURE__ */ jsx("dt", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: field.label }),
|
|
2216
|
-
/* @__PURE__ */ jsx("dd", { className: "mt-1 text-sm text-foreground", children: field.render ? field.render(item[field.key], item) : formatValue(item[field.key]) })
|
|
2248
|
+
/* @__PURE__ */ jsx("dd", { className: "mt-1 text-sm text-foreground", children: field.render ? field.render(item[field.key], item) : formatValue(item[field.key], m) })
|
|
2217
2249
|
] })
|
|
2218
2250
|
] }, field.key)) });
|
|
2219
2251
|
}
|
|
@@ -2222,12 +2254,12 @@ function DetailPanel({
|
|
|
2222
2254
|
index > 0 && /* @__PURE__ */ jsx(Separator, {}),
|
|
2223
2255
|
/* @__PURE__ */ jsxs("div", { className: "py-3", children: [
|
|
2224
2256
|
/* @__PURE__ */ jsx("dt", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: keyToLabel(key) }),
|
|
2225
|
-
/* @__PURE__ */ jsx("dd", { className: "mt-1 text-sm text-foreground", children: formatValue(value) })
|
|
2257
|
+
/* @__PURE__ */ jsx("dd", { className: "mt-1 text-sm text-foreground", children: formatValue(value, m) })
|
|
2226
2258
|
] })
|
|
2227
2259
|
] }, key)) });
|
|
2228
2260
|
};
|
|
2229
2261
|
const header = /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: [
|
|
2230
|
-
/* @__PURE__ */ jsx("div", { className: "min-w-0", children: renderTitleBlock(layout, title, subtitle) }),
|
|
2262
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0", children: renderTitleBlock(layout, title, subtitle, m.detail.fallbackTitle) }),
|
|
2231
2263
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
2232
2264
|
onEdit && /* @__PURE__ */ jsx(
|
|
2233
2265
|
Button,
|
|
@@ -2237,7 +2269,7 @@ function DetailPanel({
|
|
|
2237
2269
|
size: "icon",
|
|
2238
2270
|
className: "h-8 w-8",
|
|
2239
2271
|
onClick: () => onEdit(item),
|
|
2240
|
-
"aria-label":
|
|
2272
|
+
"aria-label": m.common.edit,
|
|
2241
2273
|
children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
|
|
2242
2274
|
}
|
|
2243
2275
|
),
|
|
@@ -2249,7 +2281,7 @@ function DetailPanel({
|
|
|
2249
2281
|
size: "icon",
|
|
2250
2282
|
className: "h-8 w-8 text-destructive hover:text-destructive",
|
|
2251
2283
|
onClick: () => onDelete(item),
|
|
2252
|
-
"aria-label":
|
|
2284
|
+
"aria-label": m.common.delete,
|
|
2253
2285
|
children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
|
|
2254
2286
|
}
|
|
2255
2287
|
),
|
|
@@ -2261,7 +2293,7 @@ function DetailPanel({
|
|
|
2261
2293
|
size: "icon",
|
|
2262
2294
|
className: "h-8 w-8",
|
|
2263
2295
|
onClick: onClose,
|
|
2264
|
-
"aria-label":
|
|
2296
|
+
"aria-label": m.common.close,
|
|
2265
2297
|
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
2266
2298
|
}
|
|
2267
2299
|
)
|
|
@@ -2323,7 +2355,7 @@ function DetailPanel({
|
|
|
2323
2355
|
"div",
|
|
2324
2356
|
{
|
|
2325
2357
|
role: "region",
|
|
2326
|
-
"aria-label": title ||
|
|
2358
|
+
"aria-label": title || m.detail.fallbackTitle,
|
|
2327
2359
|
className: cn(
|
|
2328
2360
|
"flex flex-col rounded-lg border border-border bg-card",
|
|
2329
2361
|
widthClasses[width]
|
|
@@ -3329,12 +3361,17 @@ function ConfirmDeleteDialog({
|
|
|
3329
3361
|
open,
|
|
3330
3362
|
onOpenChange,
|
|
3331
3363
|
onConfirm,
|
|
3332
|
-
title
|
|
3364
|
+
title: titleProp,
|
|
3333
3365
|
kind = "permanent",
|
|
3334
|
-
description
|
|
3335
|
-
confirmLabel
|
|
3336
|
-
cancelLabel
|
|
3366
|
+
description: descriptionProp,
|
|
3367
|
+
confirmLabel: confirmLabelProp,
|
|
3368
|
+
cancelLabel: cancelLabelProp
|
|
3337
3369
|
}) {
|
|
3370
|
+
const m = useUiMessages();
|
|
3371
|
+
const title = titleProp ?? m.confirmDelete.title;
|
|
3372
|
+
const description = descriptionProp ?? (kind === "recycle" ? m.confirmDelete.recycle : m.confirmDelete.permanent);
|
|
3373
|
+
const confirmLabel = confirmLabelProp ?? m.common.delete;
|
|
3374
|
+
const cancelLabel = cancelLabelProp ?? m.common.cancel;
|
|
3338
3375
|
const [pending, setPending] = useState(false);
|
|
3339
3376
|
const [error, setError] = useState(null);
|
|
3340
3377
|
useEffect(() => {
|
|
@@ -3407,7 +3444,7 @@ function ResourcePage(props) {
|
|
|
3407
3444
|
className,
|
|
3408
3445
|
columnVisibility,
|
|
3409
3446
|
columnVisibilityKey,
|
|
3410
|
-
createLabel
|
|
3447
|
+
createLabel: createLabelProp,
|
|
3411
3448
|
detail = "panel",
|
|
3412
3449
|
detailFields,
|
|
3413
3450
|
detailWidth = "md",
|
|
@@ -3422,9 +3459,9 @@ function ResourcePage(props) {
|
|
|
3422
3459
|
formMode,
|
|
3423
3460
|
formSteps,
|
|
3424
3461
|
gridCols: gridColsProp,
|
|
3425
|
-
header:
|
|
3462
|
+
header: headerProp,
|
|
3426
3463
|
icon,
|
|
3427
|
-
keyboardNavigation =
|
|
3464
|
+
keyboardNavigation = props.variant !== "embedded",
|
|
3428
3465
|
mutationMode: mutationModeProp,
|
|
3429
3466
|
onCreate,
|
|
3430
3467
|
onDelete,
|
|
@@ -3457,6 +3494,10 @@ function ResourcePage(props) {
|
|
|
3457
3494
|
title,
|
|
3458
3495
|
toolbar
|
|
3459
3496
|
} = props;
|
|
3497
|
+
const m = useUiMessages();
|
|
3498
|
+
const createLabel = createLabelProp ?? m.page.create;
|
|
3499
|
+
const embedded = props.variant === "embedded";
|
|
3500
|
+
const headerMode = headerProp ?? (embedded ? "toolbar" : "page");
|
|
3460
3501
|
const {
|
|
3461
3502
|
actions,
|
|
3462
3503
|
allows,
|
|
@@ -3525,6 +3566,8 @@ function ResourcePage(props) {
|
|
|
3525
3566
|
useBuiltInDelete,
|
|
3526
3567
|
useBuiltInEdit
|
|
3527
3568
|
} = useResourceTableModel(props);
|
|
3569
|
+
const wantsFilters = !!(filtersProp?.length || props.statusFilter || props.searchFields?.length || savedViews?.length || onSaveView || // Search or a status filter; a sort (a column click) isn't a filter.
|
|
3570
|
+
page.search !== "" || page.statusValue.length > 0);
|
|
3528
3571
|
const showCreateButton = (onCreate || useBuiltInCreate) && allows("create");
|
|
3529
3572
|
const createButton = showCreateButton ? /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: handleCreate, children: [
|
|
3530
3573
|
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-1" }),
|
|
@@ -3574,7 +3617,7 @@ function ResourcePage(props) {
|
|
|
3574
3617
|
/* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base truncate", children: titleVal }) }),
|
|
3575
3618
|
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
3576
3619
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
3577
|
-
badgeCol && /* @__PURE__ */ jsx("span", { children: badgeCol.render ? badgeCol.render(item, badgeVal, actions) : badgeVal != null ? String(badgeVal) :
|
|
3620
|
+
badgeCol && /* @__PURE__ */ jsx("span", { children: badgeCol.render ? badgeCol.render(item, badgeVal, actions) : badgeVal != null ? String(badgeVal) : m.common.empty }),
|
|
3578
3621
|
priceVal != null && /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: String(priceVal) })
|
|
3579
3622
|
] }),
|
|
3580
3623
|
metaCols.map((col) => {
|
|
@@ -3583,7 +3626,7 @@ function ResourcePage(props) {
|
|
|
3583
3626
|
"p",
|
|
3584
3627
|
{
|
|
3585
3628
|
className: "text-xs text-muted-foreground truncate",
|
|
3586
|
-
children: col.render ? col.render(item, val, actions) : val != null ? String(val) :
|
|
3629
|
+
children: col.render ? col.render(item, val, actions) : val != null ? String(val) : m.common.empty
|
|
3587
3630
|
},
|
|
3588
3631
|
col.key
|
|
3589
3632
|
);
|
|
@@ -3606,7 +3649,7 @@ function ResourcePage(props) {
|
|
|
3606
3649
|
/* @__PURE__ */ jsx("span", { className: "font-medium text-sm truncate", children: titleVal }),
|
|
3607
3650
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: cols.slice(1, 4).map((col) => {
|
|
3608
3651
|
const val = item[col.key];
|
|
3609
|
-
return /* @__PURE__ */ jsx("span", { children: col.render ? col.render(item, val, actions) : val != null ? String(val) :
|
|
3652
|
+
return /* @__PURE__ */ jsx("span", { children: col.render ? col.render(item, val, actions) : val != null ? String(val) : m.common.empty }, col.key);
|
|
3610
3653
|
}) })
|
|
3611
3654
|
]
|
|
3612
3655
|
}
|
|
@@ -3645,7 +3688,7 @@ function ResourcePage(props) {
|
|
|
3645
3688
|
...headerMode === "toolbar" ? { header: "toolbar" } : {},
|
|
3646
3689
|
filters: (0, slots_exports.renderSlot)(
|
|
3647
3690
|
DataFilters,
|
|
3648
|
-
slots?.filters,
|
|
3691
|
+
slots?.filters !== void 0 ? slots.filters : embedded && !wantsFilters ? null : void 0,
|
|
3649
3692
|
{
|
|
3650
3693
|
search: page.filterConfig.search,
|
|
3651
3694
|
onSearchChange: handleSearchChange,
|
|
@@ -3667,6 +3710,16 @@ function ResourcePage(props) {
|
|
|
3667
3710
|
slotProps?.filters
|
|
3668
3711
|
),
|
|
3669
3712
|
...shellSlotProps,
|
|
3713
|
+
...embedded && headerMode === "toolbar" ? {
|
|
3714
|
+
// Only with the toolbar header: an explicit `header="page"` keeps the full header.
|
|
3715
|
+
headerOptions: {
|
|
3716
|
+
showCount: false,
|
|
3717
|
+
showViewSettings: false,
|
|
3718
|
+
showViewToggle: !!renderCardProp,
|
|
3719
|
+
configurable: false,
|
|
3720
|
+
...shellSlotProps?.headerOptions
|
|
3721
|
+
}
|
|
3722
|
+
} : {},
|
|
3670
3723
|
className: cn(className, shellSlotProps?.className),
|
|
3671
3724
|
children: [
|
|
3672
3725
|
bulkActions && bulkActions.length > 0 && (0, slots_exports.renderSlot)(
|
|
@@ -3733,7 +3786,7 @@ function ResourcePage(props) {
|
|
|
3733
3786
|
),
|
|
3734
3787
|
...openOnCardClick && renderCardProp && detail !== "none" ? {
|
|
3735
3788
|
onItemClick: handleRowClick,
|
|
3736
|
-
getItemLabel: (item) =>
|
|
3789
|
+
getItemLabel: (item) => fillMessage(m.common.openItem, { name: titleOf(item) }).trim()
|
|
3737
3790
|
} : {},
|
|
3738
3791
|
renderCard: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultCard(item),
|
|
3739
3792
|
renderListItem: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultListItem(item)
|
|
@@ -3750,7 +3803,7 @@ function ResourcePage(props) {
|
|
|
3750
3803
|
"aria-busy": loadMore.loading || void 0,
|
|
3751
3804
|
onClick: loadMore.hasMore && !loadMore.loading ? loadMore.load : void 0,
|
|
3752
3805
|
testID: "resource-page-load-more",
|
|
3753
|
-
children: loadMore.loading ?
|
|
3806
|
+
children: loadMore.loading ? m.common.loading : loadMore.hasMore ? m.page.loadMore : fillMessage(m.page.allLoaded, { loaded: loadMore.loaded.toLocaleString() })
|
|
3754
3807
|
}
|
|
3755
3808
|
),
|
|
3756
3809
|
/* @__PURE__ */ jsx(
|
|
@@ -3760,24 +3813,23 @@ function ResourcePage(props) {
|
|
|
3760
3813
|
"aria-live": "polite",
|
|
3761
3814
|
className: "sr-only",
|
|
3762
3815
|
"data-testid": "resource-page-load-status",
|
|
3763
|
-
children: loadMore.lastLoaded > 0 ?
|
|
3816
|
+
children: loadMore.lastLoaded > 0 ? fillMessage(m.page.loadedMore, {
|
|
3817
|
+
added: loadMore.lastLoaded.toLocaleString(),
|
|
3818
|
+
loaded: loadMore.loaded.toLocaleString()
|
|
3819
|
+
}) : ""
|
|
3764
3820
|
}
|
|
3765
3821
|
)
|
|
3766
3822
|
] }),
|
|
3767
|
-
clientTruncated && /* @__PURE__ */
|
|
3823
|
+
clientTruncated && /* @__PURE__ */ jsx(
|
|
3768
3824
|
"p",
|
|
3769
3825
|
{
|
|
3770
3826
|
role: "status",
|
|
3771
3827
|
className: "px-3 py-2 text-sm text-muted-foreground",
|
|
3772
3828
|
"data-testid": "resource-page-truncated",
|
|
3773
|
-
children:
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
" ",
|
|
3778
|
-
displayTotal.toLocaleString(),
|
|
3779
|
-
" records."
|
|
3780
|
-
]
|
|
3829
|
+
children: fillMessage(m.page.truncated, {
|
|
3830
|
+
shown: displayData.length.toLocaleString(),
|
|
3831
|
+
total: displayTotal.toLocaleString()
|
|
3832
|
+
})
|
|
3781
3833
|
}
|
|
3782
3834
|
),
|
|
3783
3835
|
isServerControlled && pagerOnPageChange && pagerMisconfigured && /* @__PURE__ */ jsxs(
|
|
@@ -3808,7 +3860,7 @@ function ResourcePage(props) {
|
|
|
3808
3860
|
isServerControlled && pagerOnPageChange && !pagerMisconfigured && /* @__PURE__ */ jsxs(
|
|
3809
3861
|
"nav",
|
|
3810
3862
|
{
|
|
3811
|
-
"aria-label":
|
|
3863
|
+
"aria-label": m.page.pagination,
|
|
3812
3864
|
"aria-busy": resourceServerPaging && resourceHook.list.isPlaceholderData || void 0,
|
|
3813
3865
|
className: "flex flex-col sm:flex-row items-center justify-between gap-2 px-3 py-2 text-sm",
|
|
3814
3866
|
"data-testid": "resource-page-pagination",
|
|
@@ -3819,7 +3871,7 @@ function ResourcePage(props) {
|
|
|
3819
3871
|
className: "text-muted-foreground",
|
|
3820
3872
|
"data-testid": "resource-page-range",
|
|
3821
3873
|
"aria-live": "polite",
|
|
3822
|
-
children: displayTotal === 0 ?
|
|
3874
|
+
children: displayTotal === 0 ? m.page.noRecords : fillMessage(m.page.range, { from: rangeFrom, to: rangeTo, total: displayTotal })
|
|
3823
3875
|
}
|
|
3824
3876
|
),
|
|
3825
3877
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -3833,21 +3885,16 @@ function ResourcePage(props) {
|
|
|
3833
3885
|
testID: "resource-page-prev",
|
|
3834
3886
|
children: [
|
|
3835
3887
|
/* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4 mr-1", "aria-hidden": "true" }),
|
|
3836
|
-
|
|
3888
|
+
m.page.previous
|
|
3837
3889
|
]
|
|
3838
3890
|
}
|
|
3839
3891
|
),
|
|
3840
|
-
/* @__PURE__ */
|
|
3892
|
+
/* @__PURE__ */ jsx(
|
|
3841
3893
|
"span",
|
|
3842
3894
|
{
|
|
3843
3895
|
className: "text-muted-foreground",
|
|
3844
3896
|
"data-testid": "resource-page-indicator",
|
|
3845
|
-
children:
|
|
3846
|
-
"Page ",
|
|
3847
|
-
currentPage,
|
|
3848
|
-
" of ",
|
|
3849
|
-
totalPages
|
|
3850
|
-
]
|
|
3897
|
+
children: fillMessage(m.page.pageOf, { page: currentPage, pages: totalPages })
|
|
3851
3898
|
}
|
|
3852
3899
|
),
|
|
3853
3900
|
/* @__PURE__ */ jsxs(
|
|
@@ -3859,7 +3906,7 @@ function ResourcePage(props) {
|
|
|
3859
3906
|
onClick: () => pagerOnPageChange(currentPage + 1),
|
|
3860
3907
|
testID: "resource-page-next",
|
|
3861
3908
|
children: [
|
|
3862
|
-
|
|
3909
|
+
m.page.next,
|
|
3863
3910
|
/* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 ml-1", "aria-hidden": "true" })
|
|
3864
3911
|
]
|
|
3865
3912
|
}
|
|
@@ -3876,10 +3923,10 @@ function ResourcePage(props) {
|
|
|
3876
3923
|
item: {},
|
|
3877
3924
|
open: detailOpen,
|
|
3878
3925
|
onClose: closeDetail,
|
|
3879
|
-
title:
|
|
3926
|
+
title: m.page.notFoundTitle,
|
|
3880
3927
|
width: detailWidth,
|
|
3881
3928
|
layout: detail === "modal" ? "dialog" : "sheet",
|
|
3882
|
-
children: () => /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", "data-testid": "resource-page-link-missing", children: isDataReadError(linked.error) && linked.error.outcome === "forbidden" ? linked.error.message :
|
|
3929
|
+
children: () => /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", "data-testid": "resource-page-link-missing", children: isDataReadError(linked.error) && linked.error.outcome === "forbidden" ? linked.error.message : m.page.notFoundBody })
|
|
3883
3930
|
} : {
|
|
3884
3931
|
item: selectedItem,
|
|
3885
3932
|
open: detailOpen,
|