@marktiderman/genesis-ui 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-GDFNAHI4.js → chunk-IXGAEECT.js} +4 -2
- package/dist/chunk-IXGAEECT.js.map +1 -0
- package/dist/components/data/index.d.ts +48 -4
- package/dist/components/data/index.js +257 -77
- package/dist/components/data/index.js.map +1 -1
- package/dist/components/sheet-grid/sheet-grid.d.ts +108 -0
- package/dist/components/sheet-grid/sheet-grid.js +434 -0
- package/dist/components/sheet-grid/sheet-grid.js.map +1 -0
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/use-resource-page.d.ts +1 -1
- package/dist/hooks/use-resource-page.js +1 -1
- package/dist/index.js +4 -4
- package/dist/{use-resource-page-DTQS011Q.d.ts → use-resource-page-Dd2VUv30.d.ts} +15 -1
- package/package.json +16 -5
- package/dist/chunk-GDFNAHI4.js.map +0 -1
|
@@ -11,7 +11,7 @@ import { Progress } from '../../chunk-W5Y3LG5K.js';
|
|
|
11
11
|
import { Input } from '../../chunk-QSXVL5UB.js';
|
|
12
12
|
import { Checkbox } from '../../chunk-SEBU6JCK.js';
|
|
13
13
|
import { Card, CardContent, CardHeader, CardTitle } from '../../chunk-FN3FZVUR.js';
|
|
14
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuItem } from '../../chunk-QJ6P2DW6.js';
|
|
14
|
+
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuItem, DropdownMenuLabel } from '../../chunk-QJ6P2DW6.js';
|
|
15
15
|
import '../../chunk-B2GIBUQD.js';
|
|
16
16
|
import { Badge } from '../../chunk-NPNADZHM.js';
|
|
17
17
|
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../chunk-FK42ZGGV.js';
|
|
@@ -27,7 +27,7 @@ import '../../chunk-F47U5KNW.js';
|
|
|
27
27
|
import { DetailPage } from '../../chunk-AFZ76Q26.js';
|
|
28
28
|
import '../../chunk-D2LIXA2Y.js';
|
|
29
29
|
import '../../chunk-AVFZ4P4B.js';
|
|
30
|
-
import { titleCase, useResourcePage } from '../../chunk-
|
|
30
|
+
import { titleCase, useResourcePage } from '../../chunk-IXGAEECT.js';
|
|
31
31
|
export { useSavedViews } from '../../chunk-GCQFVMPI.js';
|
|
32
32
|
import '../../chunk-FTTNGGLA.js';
|
|
33
33
|
import { useViewSettings } from '../../chunk-OZ7ZAYCS.js';
|
|
@@ -39,7 +39,7 @@ import { DefaultLink, defaultNavigate } from '../../chunk-CDYZRZ6U.js';
|
|
|
39
39
|
export { DefaultLink, defaultNavigate, isPathActive, resolveNavigation } from '../../chunk-CDYZRZ6U.js';
|
|
40
40
|
import { useKeyboardNavigation } from '../../chunk-YJ6MPS6U.js';
|
|
41
41
|
import { useIsMobile } from '../../chunk-VFO3I7PV.js';
|
|
42
|
-
import { AlertTriangle, RefreshCw, ChevronDown, Check, Search, X, ArrowUpDown, Bookmark, Trash2, BookmarkPlus, Loader2, Columns3, ArrowUp, ArrowDown, ChevronLeft, ChevronRight, ExternalLink, Pencil, ChevronsUpDown, ImageIcon, Upload, AlertCircle, FileIcon, Plus } from 'lucide-react';
|
|
42
|
+
import { AlertTriangle, RefreshCw, ChevronDown, Check, Search, X, ArrowUpDown, Bookmark, Trash2, BookmarkPlus, Loader2, Columns3, ArrowUp, ArrowDown, ChevronLeft, ChevronRight, ExternalLink, ArrowRightLeft, Pencil, ChevronsUpDown, ImageIcon, Upload, AlertCircle, FileIcon, Plus } from 'lucide-react';
|
|
43
43
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
44
44
|
import { useState, useRef, useEffect, useCallback, useMemo, Fragment as Fragment$1 } from 'react';
|
|
45
45
|
import { formatDistanceToNow, format } from 'date-fns';
|
|
@@ -61,7 +61,7 @@ function DataPageShell({
|
|
|
61
61
|
emptyMessage,
|
|
62
62
|
emptyAction,
|
|
63
63
|
hasError = false,
|
|
64
|
-
errorMessage,
|
|
64
|
+
errorMessage: errorMessage2,
|
|
65
65
|
onRetry,
|
|
66
66
|
count,
|
|
67
67
|
totalCount,
|
|
@@ -148,7 +148,7 @@ function DataPageShell({
|
|
|
148
148
|
) : hasError ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
|
|
149
149
|
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-10 w-10 text-destructive mb-3" }),
|
|
150
150
|
/* @__PURE__ */ jsx("p", { className: "mb-1 text-sm font-medium text-foreground", children: "Something went wrong" }),
|
|
151
|
-
/* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children:
|
|
151
|
+
/* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: errorMessage2 || `Failed to load ${entity}s. Please try again.` }),
|
|
152
152
|
onRetry && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, children: [
|
|
153
153
|
/* @__PURE__ */ jsx(RefreshCw, { className: "mr-1 h-4 w-4" }),
|
|
154
154
|
" Retry"
|
|
@@ -279,7 +279,9 @@ function DataFilters({
|
|
|
279
279
|
canSaveViews = false,
|
|
280
280
|
onViewSaved,
|
|
281
281
|
onClearAll,
|
|
282
|
-
searchInputRef
|
|
282
|
+
searchInputRef,
|
|
283
|
+
showSearch = true,
|
|
284
|
+
showSort = true
|
|
283
285
|
}) {
|
|
284
286
|
const internalSearchRef = useRef(null);
|
|
285
287
|
const effectiveSearchRef = searchInputRef || internalSearchRef;
|
|
@@ -335,8 +337,8 @@ function DataFilters({
|
|
|
335
337
|
})
|
|
336
338
|
}
|
|
337
339
|
),
|
|
338
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row gap-2", children: [
|
|
339
|
-
/* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
|
|
340
|
+
(showSearch || showSort) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row gap-2", children: [
|
|
341
|
+
showSearch && /* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
|
|
340
342
|
/* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
|
|
341
343
|
/* @__PURE__ */ jsx(
|
|
342
344
|
Input,
|
|
@@ -357,7 +359,7 @@ function DataFilters({
|
|
|
357
359
|
}
|
|
358
360
|
)
|
|
359
361
|
] }),
|
|
360
|
-
/* @__PURE__ */ jsxs(Select, { value: sort, onValueChange: onSortChange, children: [
|
|
362
|
+
showSort && /* @__PURE__ */ jsxs(Select, { value: sort, onValueChange: onSortChange, children: [
|
|
361
363
|
/* @__PURE__ */ jsxs(SelectTrigger, { className: "w-full sm:w-[180px] shrink-0", children: [
|
|
362
364
|
/* @__PURE__ */ jsx(ArrowUpDown, { className: "h-3.5 w-3.5 mr-1.5 text-muted-foreground" }),
|
|
363
365
|
/* @__PURE__ */ jsx(SelectValue, { placeholder: "Sort by..." })
|
|
@@ -639,6 +641,9 @@ function getCellClasses(col) {
|
|
|
639
641
|
const hide = col.hideBelow ? col.hideBelow === "sm" ? "hidden sm:table-cell" : col.hideBelow === "md" ? "hidden md:table-cell" : col.hideBelow === "lg" ? "hidden lg:table-cell" : "hidden xl:table-cell" : "";
|
|
640
642
|
return cn(col.width, hide);
|
|
641
643
|
}
|
|
644
|
+
function errorMessage(error) {
|
|
645
|
+
return error instanceof Error ? error.message : String(error);
|
|
646
|
+
}
|
|
642
647
|
function EditableCell({
|
|
643
648
|
item,
|
|
644
649
|
column,
|
|
@@ -646,13 +651,49 @@ function EditableCell({
|
|
|
646
651
|
}) {
|
|
647
652
|
const [editing, setEditing] = useState(false);
|
|
648
653
|
const [draft, setDraft] = useState(String(value ?? ""));
|
|
654
|
+
const [saving, setSaving] = useState(false);
|
|
655
|
+
const [error, setError] = useState(null);
|
|
656
|
+
const inFlight = useRef(false);
|
|
657
|
+
const failedDraft = useRef(null);
|
|
649
658
|
const editable = column.editable;
|
|
659
|
+
const close = useCallback(() => {
|
|
660
|
+
setEditing(false);
|
|
661
|
+
setError(null);
|
|
662
|
+
failedDraft.current = null;
|
|
663
|
+
}, []);
|
|
650
664
|
const commit = useCallback(
|
|
651
665
|
(v) => {
|
|
652
|
-
|
|
653
|
-
|
|
666
|
+
if (inFlight.current) return;
|
|
667
|
+
let result;
|
|
668
|
+
try {
|
|
669
|
+
result = editable.onSave(item, v);
|
|
670
|
+
} catch (e) {
|
|
671
|
+
setError(errorMessage(e));
|
|
672
|
+
failedDraft.current = v;
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
if (!result || typeof result.then !== "function") {
|
|
676
|
+
close();
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
inFlight.current = true;
|
|
680
|
+
setSaving(true);
|
|
681
|
+
setError(null);
|
|
682
|
+
result.then(
|
|
683
|
+
() => {
|
|
684
|
+
inFlight.current = false;
|
|
685
|
+
setSaving(false);
|
|
686
|
+
close();
|
|
687
|
+
},
|
|
688
|
+
(e) => {
|
|
689
|
+
inFlight.current = false;
|
|
690
|
+
setSaving(false);
|
|
691
|
+
setError(errorMessage(e));
|
|
692
|
+
failedDraft.current = v;
|
|
693
|
+
}
|
|
694
|
+
);
|
|
654
695
|
},
|
|
655
|
-
[editable, item]
|
|
696
|
+
[editable, item, close]
|
|
656
697
|
);
|
|
657
698
|
if (!editing) {
|
|
658
699
|
return /* @__PURE__ */ jsx(
|
|
@@ -662,32 +703,80 @@ function EditableCell({
|
|
|
662
703
|
onClick: (e) => {
|
|
663
704
|
e.stopPropagation();
|
|
664
705
|
setDraft(String(value ?? ""));
|
|
706
|
+
setError(null);
|
|
707
|
+
failedDraft.current = null;
|
|
665
708
|
setEditing(true);
|
|
666
709
|
},
|
|
667
710
|
children: column.render ? column.render(item, value) : String(value ?? "\u2014")
|
|
668
711
|
}
|
|
669
712
|
);
|
|
670
713
|
}
|
|
714
|
+
const feedback = error ? /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-1 text-[11px] leading-snug text-destructive", children: error }) : null;
|
|
671
715
|
if (editable.type === "select" && editable.options) {
|
|
672
|
-
return /* @__PURE__ */
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
716
|
+
return /* @__PURE__ */ jsxs(
|
|
717
|
+
"div",
|
|
718
|
+
{
|
|
719
|
+
onClick: (e) => e.stopPropagation(),
|
|
720
|
+
onKeyDown: (e) => {
|
|
721
|
+
if (e.key === "Escape" && !inFlight.current) close();
|
|
722
|
+
},
|
|
723
|
+
"aria-busy": saving || void 0,
|
|
724
|
+
children: [
|
|
725
|
+
/* @__PURE__ */ jsxs(
|
|
726
|
+
Select,
|
|
727
|
+
{
|
|
728
|
+
value: draft,
|
|
729
|
+
disabled: saving,
|
|
730
|
+
onValueChange: (v) => {
|
|
731
|
+
setDraft(v);
|
|
732
|
+
commit(v);
|
|
733
|
+
},
|
|
734
|
+
children: [
|
|
735
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-7 text-xs w-28", "aria-invalid": error ? true : void 0, children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
736
|
+
/* @__PURE__ */ jsx(SelectContent, { children: editable.options.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt, children: opt }, opt)) })
|
|
737
|
+
]
|
|
738
|
+
}
|
|
739
|
+
),
|
|
740
|
+
feedback,
|
|
741
|
+
error && /* @__PURE__ */ jsx(
|
|
742
|
+
"button",
|
|
743
|
+
{
|
|
744
|
+
type: "button",
|
|
745
|
+
className: "mt-0.5 text-[11px] text-muted-foreground underline-offset-2 hover:underline",
|
|
746
|
+
onClick: close,
|
|
747
|
+
children: "Cancel"
|
|
748
|
+
}
|
|
749
|
+
)
|
|
750
|
+
]
|
|
688
751
|
}
|
|
689
|
-
|
|
690
|
-
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
return /* @__PURE__ */ jsxs("div", { onClick: (e) => e.stopPropagation(), "aria-busy": saving || void 0, children: [
|
|
755
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
756
|
+
/* @__PURE__ */ jsx(
|
|
757
|
+
Input,
|
|
758
|
+
{
|
|
759
|
+
autoFocus: true,
|
|
760
|
+
className: cn("h-7 text-xs w-full", saving && "pr-7 opacity-70"),
|
|
761
|
+
value: draft,
|
|
762
|
+
readOnly: saving,
|
|
763
|
+
"aria-invalid": error ? true : void 0,
|
|
764
|
+
onChange: (e) => setDraft(e.target.value),
|
|
765
|
+
onBlur: () => {
|
|
766
|
+
if (inFlight.current) return;
|
|
767
|
+
if (failedDraft.current === draft) close();
|
|
768
|
+
else commit(draft);
|
|
769
|
+
},
|
|
770
|
+
onKeyDown: (e) => {
|
|
771
|
+
if (e.key === "Enter") commit(draft);
|
|
772
|
+
if (e.key === "Escape" && !inFlight.current) close();
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
),
|
|
776
|
+
saving && /* @__PURE__ */ jsx(Spinner, { className: "absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2", "aria-label": "Saving" })
|
|
777
|
+
] }),
|
|
778
|
+
feedback
|
|
779
|
+
] });
|
|
691
780
|
}
|
|
692
781
|
function ResizeHandle({
|
|
693
782
|
onResize,
|
|
@@ -1327,12 +1416,39 @@ var statusColorMap = {
|
|
|
1327
1416
|
destructive: { color: "text-destructive", bgColor: "bg-destructive/15" },
|
|
1328
1417
|
primary: { color: "text-primary", bgColor: "bg-primary/15" }
|
|
1329
1418
|
};
|
|
1419
|
+
var DRAG_TYPE = "application/x-genesis-kanban-item";
|
|
1330
1420
|
function KanbanBoard({
|
|
1331
1421
|
columns,
|
|
1332
1422
|
renderCard,
|
|
1333
1423
|
emptyMessage = "No items",
|
|
1334
|
-
className
|
|
1424
|
+
className,
|
|
1425
|
+
getKey,
|
|
1426
|
+
onMoveItem,
|
|
1427
|
+
getItemLabel,
|
|
1428
|
+
renderColumnFooter
|
|
1335
1429
|
}) {
|
|
1430
|
+
const [dragging, setDragging] = useState(null);
|
|
1431
|
+
const [over, setOver] = useState(null);
|
|
1432
|
+
const keyOf = (item, index) => getKey ? getKey(item) : String(index);
|
|
1433
|
+
const move = (item, to) => {
|
|
1434
|
+
void Promise.resolve(onMoveItem?.(item, to)).catch(() => {
|
|
1435
|
+
});
|
|
1436
|
+
};
|
|
1437
|
+
const onDrop = (event, to) => {
|
|
1438
|
+
event.preventDefault();
|
|
1439
|
+
setOver(null);
|
|
1440
|
+
const key = event.dataTransfer.getData(DRAG_TYPE) || dragging?.key;
|
|
1441
|
+
setDragging(null);
|
|
1442
|
+
if (!key || !getKey) return;
|
|
1443
|
+
for (const column of columns) {
|
|
1444
|
+
const item = column.items.find((i) => getKey(i) === key);
|
|
1445
|
+
if (item) {
|
|
1446
|
+
if (column.key !== to) move(item, to);
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
const movable = Boolean(onMoveItem && getKey);
|
|
1336
1452
|
return /* @__PURE__ */ jsx(
|
|
1337
1453
|
"div",
|
|
1338
1454
|
{
|
|
@@ -1342,20 +1458,80 @@ function KanbanBoard({
|
|
|
1342
1458
|
),
|
|
1343
1459
|
children: columns.map((column) => {
|
|
1344
1460
|
const resolved = column.status ? statusColorMap[column.status] : { color: column.color ?? "", bgColor: column.bgColor ?? "" };
|
|
1345
|
-
return /* @__PURE__ */ jsxs(
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1461
|
+
return /* @__PURE__ */ jsxs(
|
|
1462
|
+
"section",
|
|
1463
|
+
{
|
|
1464
|
+
"aria-label": column.label,
|
|
1465
|
+
"data-kanban-column": column.key,
|
|
1466
|
+
className: cn(
|
|
1467
|
+
"flex flex-col gap-3 rounded-lg",
|
|
1468
|
+
movable && over === column.key && dragging?.from !== column.key && "bg-accent/40 ring-2 ring-primary/40"
|
|
1469
|
+
),
|
|
1470
|
+
onDragOver: movable ? (e) => {
|
|
1471
|
+
if (!dragging) return;
|
|
1472
|
+
e.preventDefault();
|
|
1473
|
+
e.dataTransfer.dropEffect = "move";
|
|
1474
|
+
if (over !== column.key) setOver(column.key);
|
|
1475
|
+
} : void 0,
|
|
1476
|
+
onDragLeave: movable ? (e) => {
|
|
1477
|
+
if (!e.currentTarget.contains(e.relatedTarget)) setOver(null);
|
|
1478
|
+
} : void 0,
|
|
1479
|
+
onDrop: movable ? (e) => onDrop(e, column.key) : void 0,
|
|
1480
|
+
children: [
|
|
1481
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1482
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-sm font-semibold", resolved.color), children: column.label }),
|
|
1483
|
+
/* @__PURE__ */ jsx(
|
|
1484
|
+
Badge,
|
|
1485
|
+
{
|
|
1486
|
+
variant: "secondary",
|
|
1487
|
+
className: cn("text-[10px] px-1.5 py-0", resolved.bgColor),
|
|
1488
|
+
children: column.total ?? column.items.length
|
|
1489
|
+
}
|
|
1490
|
+
)
|
|
1491
|
+
] }),
|
|
1492
|
+
/* @__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) => {
|
|
1493
|
+
const key = keyOf(item, i);
|
|
1494
|
+
return /* @__PURE__ */ jsxs(
|
|
1495
|
+
"div",
|
|
1496
|
+
{
|
|
1497
|
+
className: cn("animate-fade-in", movable && "group relative", dragging?.key === key && "opacity-50"),
|
|
1498
|
+
style: { animationDelay: `${i * 30}ms`, animationFillMode: "both" },
|
|
1499
|
+
draggable: movable || void 0,
|
|
1500
|
+
onDragStart: movable ? (e) => {
|
|
1501
|
+
e.dataTransfer.setData(DRAG_TYPE, key);
|
|
1502
|
+
e.dataTransfer.effectAllowed = "move";
|
|
1503
|
+
setDragging({ key, from: column.key });
|
|
1504
|
+
} : void 0,
|
|
1505
|
+
onDragEnd: movable ? () => {
|
|
1506
|
+
setDragging(null);
|
|
1507
|
+
setOver(null);
|
|
1508
|
+
} : void 0,
|
|
1509
|
+
children: [
|
|
1510
|
+
renderCard(item),
|
|
1511
|
+
movable && columns.length > 1 && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
1512
|
+
/* @__PURE__ */ jsx(
|
|
1513
|
+
DropdownMenuTrigger,
|
|
1514
|
+
{
|
|
1515
|
+
"aria-label": `Move ${getItemLabel?.(item) ?? "card"}`,
|
|
1516
|
+
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",
|
|
1517
|
+
children: /* @__PURE__ */ jsx(ArrowRightLeft, { className: "h-3.5 w-3.5", "aria-hidden": true })
|
|
1518
|
+
}
|
|
1519
|
+
),
|
|
1520
|
+
/* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
|
|
1521
|
+
/* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Move to" }),
|
|
1522
|
+
columns.filter((c) => c.key !== column.key).map((c) => /* @__PURE__ */ jsx(DropdownMenuItem, { onSelect: () => move(item, c.key), children: c.label }, c.key))
|
|
1523
|
+
] })
|
|
1524
|
+
] })
|
|
1525
|
+
]
|
|
1526
|
+
},
|
|
1527
|
+
key
|
|
1528
|
+
);
|
|
1529
|
+
}) }),
|
|
1530
|
+
renderColumnFooter?.(column)
|
|
1531
|
+
]
|
|
1532
|
+
},
|
|
1533
|
+
column.key
|
|
1534
|
+
);
|
|
1359
1535
|
})
|
|
1360
1536
|
}
|
|
1361
1537
|
);
|
|
@@ -2036,15 +2212,15 @@ function ResourceFormFieldRenderer({
|
|
|
2036
2212
|
name: field.key,
|
|
2037
2213
|
control,
|
|
2038
2214
|
render: ({ field: f, fieldState }) => {
|
|
2039
|
-
const
|
|
2215
|
+
const errorMessage2 = fieldState.error?.message;
|
|
2040
2216
|
const ariaProps = {
|
|
2041
|
-
"aria-invalid": !!
|
|
2042
|
-
"aria-describedby":
|
|
2217
|
+
"aria-invalid": !!errorMessage2 || void 0,
|
|
2218
|
+
"aria-describedby": errorMessage2 ? `${field.key}-error` : field.description ? `${field.key}-desc` : void 0,
|
|
2043
2219
|
"aria-required": field.required || void 0
|
|
2044
2220
|
};
|
|
2045
2221
|
if (fieldType === "switch") {
|
|
2046
2222
|
return /* @__PURE__ */ jsxs(FormField, { className: "flex flex-row items-center justify-between rounded-lg border p-4", children: [
|
|
2047
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: /* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2223
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: /* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, children: label }) }),
|
|
2048
2224
|
/* @__PURE__ */ jsx(
|
|
2049
2225
|
Switch,
|
|
2050
2226
|
{
|
|
@@ -2068,14 +2244,14 @@ function ResourceFormFieldRenderer({
|
|
|
2068
2244
|
}
|
|
2069
2245
|
),
|
|
2070
2246
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1 leading-none", children: [
|
|
2071
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2072
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2247
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, children: label }),
|
|
2248
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2073
2249
|
] })
|
|
2074
2250
|
] });
|
|
2075
2251
|
}
|
|
2076
2252
|
if (fieldType === "select" && field.options) {
|
|
2077
2253
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2078
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2254
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2079
2255
|
/* @__PURE__ */ jsxs(
|
|
2080
2256
|
Select,
|
|
2081
2257
|
{
|
|
@@ -2095,12 +2271,12 @@ function ResourceFormFieldRenderer({
|
|
|
2095
2271
|
}
|
|
2096
2272
|
),
|
|
2097
2273
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2098
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2274
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2099
2275
|
] });
|
|
2100
2276
|
}
|
|
2101
2277
|
if (fieldType === "relation" && field.relation) {
|
|
2102
2278
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2103
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2279
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2104
2280
|
/* @__PURE__ */ jsx(
|
|
2105
2281
|
RelationField,
|
|
2106
2282
|
{
|
|
@@ -2111,12 +2287,12 @@ function ResourceFormFieldRenderer({
|
|
|
2111
2287
|
}
|
|
2112
2288
|
),
|
|
2113
2289
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2114
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2290
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2115
2291
|
] });
|
|
2116
2292
|
}
|
|
2117
2293
|
if (fieldType === "file" || fieldType === "image") {
|
|
2118
2294
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2119
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2295
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2120
2296
|
/* @__PURE__ */ jsx(
|
|
2121
2297
|
FileUploadField,
|
|
2122
2298
|
{
|
|
@@ -2127,12 +2303,12 @@ function ResourceFormFieldRenderer({
|
|
|
2127
2303
|
}
|
|
2128
2304
|
),
|
|
2129
2305
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2130
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2306
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2131
2307
|
] });
|
|
2132
2308
|
}
|
|
2133
2309
|
if (fieldType === "textarea") {
|
|
2134
2310
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2135
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2311
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2136
2312
|
/* @__PURE__ */ jsx(
|
|
2137
2313
|
Textarea,
|
|
2138
2314
|
{
|
|
@@ -2146,12 +2322,12 @@ function ResourceFormFieldRenderer({
|
|
|
2146
2322
|
}
|
|
2147
2323
|
),
|
|
2148
2324
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2149
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2325
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2150
2326
|
] });
|
|
2151
2327
|
}
|
|
2152
2328
|
if (fieldType === "number") {
|
|
2153
2329
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2154
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2330
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2155
2331
|
/* @__PURE__ */ jsx(
|
|
2156
2332
|
Input,
|
|
2157
2333
|
{
|
|
@@ -2170,11 +2346,11 @@ function ResourceFormFieldRenderer({
|
|
|
2170
2346
|
}
|
|
2171
2347
|
),
|
|
2172
2348
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2173
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2349
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2174
2350
|
] });
|
|
2175
2351
|
}
|
|
2176
2352
|
return /* @__PURE__ */ jsxs(FormField, { children: [
|
|
2177
|
-
/* @__PURE__ */ jsx(FormLabel, { error: !!
|
|
2353
|
+
/* @__PURE__ */ jsx(FormLabel, { error: !!errorMessage2, required: field.required, children: label }),
|
|
2178
2354
|
/* @__PURE__ */ jsx(
|
|
2179
2355
|
Input,
|
|
2180
2356
|
{
|
|
@@ -2188,7 +2364,7 @@ function ResourceFormFieldRenderer({
|
|
|
2188
2364
|
}
|
|
2189
2365
|
),
|
|
2190
2366
|
field.description && /* @__PURE__ */ jsx(FormDescription, { id: `${field.key}-desc`, children: field.description }),
|
|
2191
|
-
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children:
|
|
2367
|
+
/* @__PURE__ */ jsx(FormError, { id: `${field.key}-error`, children: errorMessage2 })
|
|
2192
2368
|
] });
|
|
2193
2369
|
}
|
|
2194
2370
|
}
|
|
@@ -2475,6 +2651,7 @@ function ResourcePage(props) {
|
|
|
2475
2651
|
subtitle,
|
|
2476
2652
|
icon,
|
|
2477
2653
|
columns: columnsProp,
|
|
2654
|
+
getKey: getKeyProp,
|
|
2478
2655
|
detail = "panel",
|
|
2479
2656
|
detailWidth = "md",
|
|
2480
2657
|
titleField: titleFieldProp,
|
|
@@ -2495,7 +2672,7 @@ function ResourcePage(props) {
|
|
|
2495
2672
|
onSearchChange,
|
|
2496
2673
|
loading: loadingProp,
|
|
2497
2674
|
error: errorProp,
|
|
2498
|
-
errorMessage,
|
|
2675
|
+
errorMessage: errorMessage2,
|
|
2499
2676
|
onRetry: onRetryProp,
|
|
2500
2677
|
savedViews,
|
|
2501
2678
|
onLoadView,
|
|
@@ -2526,6 +2703,7 @@ function ResourcePage(props) {
|
|
|
2526
2703
|
renderTable: renderTableProp,
|
|
2527
2704
|
gridCols: gridColsProp,
|
|
2528
2705
|
keyboardNavigation = true,
|
|
2706
|
+
capabilities,
|
|
2529
2707
|
toolbar,
|
|
2530
2708
|
emptyState,
|
|
2531
2709
|
resourceOptions,
|
|
@@ -2592,6 +2770,10 @@ function ResourcePage(props) {
|
|
|
2592
2770
|
resourceHook.remove,
|
|
2593
2771
|
resourceHook.list
|
|
2594
2772
|
]);
|
|
2773
|
+
const keyOf = useCallback(
|
|
2774
|
+
(item) => getKeyProp ? getKeyProp(item) : String(item.id ?? ""),
|
|
2775
|
+
[getKeyProp]
|
|
2776
|
+
);
|
|
2595
2777
|
const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
|
|
2596
2778
|
const handleToggleSelect = useCallback((id) => {
|
|
2597
2779
|
setSelectedIds((prev) => {
|
|
@@ -2604,13 +2786,9 @@ function ResourcePage(props) {
|
|
|
2604
2786
|
const handleToggleAll = useCallback(() => {
|
|
2605
2787
|
setSelectedIds((prev) => {
|
|
2606
2788
|
if (prev.size === page.data.length) return /* @__PURE__ */ new Set();
|
|
2607
|
-
return new Set(
|
|
2608
|
-
page.data.map(
|
|
2609
|
-
(item) => String(item.id ?? "")
|
|
2610
|
-
)
|
|
2611
|
-
);
|
|
2789
|
+
return new Set(page.data.map(keyOf));
|
|
2612
2790
|
});
|
|
2613
|
-
}, [page.data]);
|
|
2791
|
+
}, [page.data, keyOf]);
|
|
2614
2792
|
const handleClearSelection = useCallback(() => {
|
|
2615
2793
|
setSelectedIds(/* @__PURE__ */ new Set());
|
|
2616
2794
|
}, []);
|
|
@@ -2646,6 +2824,8 @@ function ResourcePage(props) {
|
|
|
2646
2824
|
header: col.header ?? col.key,
|
|
2647
2825
|
sortable: col.sortable ?? true,
|
|
2648
2826
|
hideBelow: col.hideBelow,
|
|
2827
|
+
editable: col.editable,
|
|
2828
|
+
accessor: col.accessor,
|
|
2649
2829
|
render: col.render ? (item, value) => col.render(item, value, actions) : void 0
|
|
2650
2830
|
})) : page.tableColumns;
|
|
2651
2831
|
if (!isServerControlled) return base;
|
|
@@ -2765,9 +2945,7 @@ function ResourcePage(props) {
|
|
|
2765
2945
|
onToggleSelect: hasBulkSelect ? (index) => {
|
|
2766
2946
|
const item = page.data[index];
|
|
2767
2947
|
if (item) {
|
|
2768
|
-
handleToggleSelect(
|
|
2769
|
-
String(item.id ?? "")
|
|
2770
|
-
);
|
|
2948
|
+
handleToggleSelect(keyOf(item));
|
|
2771
2949
|
}
|
|
2772
2950
|
} : void 0,
|
|
2773
2951
|
// Supplied only while there is an open detail to close — the hook
|
|
@@ -2881,7 +3059,7 @@ function ResourcePage(props) {
|
|
|
2881
3059
|
isLoading,
|
|
2882
3060
|
hasError,
|
|
2883
3061
|
isEmpty,
|
|
2884
|
-
errorMessage,
|
|
3062
|
+
errorMessage: errorMessage2,
|
|
2885
3063
|
onRetry: isClientMode ? () => resourceHook.list.refetch() : onRetryProp,
|
|
2886
3064
|
viewMode: page.viewMode,
|
|
2887
3065
|
onViewModeChange: page.setViewMode,
|
|
@@ -2911,7 +3089,9 @@ function ResourcePage(props) {
|
|
|
2911
3089
|
onSaveView,
|
|
2912
3090
|
onDeleteView,
|
|
2913
3091
|
canSaveViews,
|
|
2914
|
-
onClearAll: page.hasActiveFilters ? handleClearFilters : void 0
|
|
3092
|
+
onClearAll: page.hasActiveFilters ? handleClearFilters : void 0,
|
|
3093
|
+
showSearch: capabilities?.search !== false,
|
|
3094
|
+
showSort: capabilities?.sort !== false
|
|
2915
3095
|
},
|
|
2916
3096
|
slotProps?.filters
|
|
2917
3097
|
),
|
|
@@ -2946,7 +3126,7 @@ function ResourcePage(props) {
|
|
|
2946
3126
|
items: page.data,
|
|
2947
3127
|
viewMode: page.viewMode,
|
|
2948
3128
|
gridCols: gridColsProp,
|
|
2949
|
-
getKey:
|
|
3129
|
+
getKey: keyOf,
|
|
2950
3130
|
focusedIndex: keyboardNav.focusedIndex,
|
|
2951
3131
|
keyboardContainerRef: keyboardNav.containerRef,
|
|
2952
3132
|
renderTable: (items) => renderTableProp ? renderTableProp(items) : renderSlot(
|
|
@@ -2955,7 +3135,7 @@ function ResourcePage(props) {
|
|
|
2955
3135
|
{
|
|
2956
3136
|
items,
|
|
2957
3137
|
columns: tableColumns,
|
|
2958
|
-
getKey:
|
|
3138
|
+
getKey: keyOf,
|
|
2959
3139
|
onRowClick: detail !== "none" ? handleRowClick : void 0,
|
|
2960
3140
|
selectable: !!bulkActions && bulkActions.length > 0,
|
|
2961
3141
|
selected: selectedIds,
|