@bmsuisse/datagrid 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +277 -60
- package/dist/index.js +628 -453
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1488,7 +1488,11 @@ function EnumEditor({
|
|
|
1488
1488
|
}
|
|
1489
1489
|
|
|
1490
1490
|
// src/edit/NumberEditor.tsx
|
|
1491
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
|
|
1491
1492
|
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1493
|
+
function textFromValue(value) {
|
|
1494
|
+
return value === null || value === void 0 ? "" : String(value);
|
|
1495
|
+
}
|
|
1492
1496
|
function NumberEditor({
|
|
1493
1497
|
column,
|
|
1494
1498
|
rowId,
|
|
@@ -1498,6 +1502,13 @@ function NumberEditor({
|
|
|
1498
1502
|
autoFocus
|
|
1499
1503
|
}) {
|
|
1500
1504
|
const errorId = editErrorId(rowId, column.id);
|
|
1505
|
+
const [text, setText] = useState5(() => textFromValue(value));
|
|
1506
|
+
const lastEmittedRef = useRef4(value);
|
|
1507
|
+
useEffect4(() => {
|
|
1508
|
+
if (value === lastEmittedRef.current) return;
|
|
1509
|
+
lastEmittedRef.current = value;
|
|
1510
|
+
setText(textFromValue(value));
|
|
1511
|
+
}, [value]);
|
|
1501
1512
|
return /* @__PURE__ */ jsxs12("div", { children: [
|
|
1502
1513
|
/* @__PURE__ */ jsx19(
|
|
1503
1514
|
Input,
|
|
@@ -1509,8 +1520,13 @@ function NumberEditor({
|
|
|
1509
1520
|
"aria-describedby": error ? errorId : void 0,
|
|
1510
1521
|
"data-testid": `edit-${rowId}-${column.id}`,
|
|
1511
1522
|
autoFocus,
|
|
1512
|
-
value:
|
|
1513
|
-
onChange: (event) =>
|
|
1523
|
+
value: text,
|
|
1524
|
+
onChange: (event) => {
|
|
1525
|
+
setText(event.target.value);
|
|
1526
|
+
const next = event.target.value === "" ? null : Number(event.target.value);
|
|
1527
|
+
lastEmittedRef.current = next;
|
|
1528
|
+
onChange(next);
|
|
1529
|
+
}
|
|
1514
1530
|
}
|
|
1515
1531
|
),
|
|
1516
1532
|
error && /* @__PURE__ */ jsx19(EditFieldError, { id: errorId, message: error })
|
|
@@ -1565,18 +1581,246 @@ function renderDefaultEditWidget(column, rowId, value, onChange, error, autoFocu
|
|
|
1565
1581
|
}
|
|
1566
1582
|
}
|
|
1567
1583
|
|
|
1584
|
+
// src/edit/editingState.ts
|
|
1585
|
+
import { useEffect as useEffect5, useRef as useRef5, useState as useState6 } from "react";
|
|
1586
|
+
function editValuesEqual(a, b) {
|
|
1587
|
+
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
|
|
1588
|
+
return a === b;
|
|
1589
|
+
}
|
|
1590
|
+
function useEditingState(editing, getRowId) {
|
|
1591
|
+
const [pendingEdits, setPendingEdits] = useState6(/* @__PURE__ */ new Map());
|
|
1592
|
+
const [editErrors, setEditErrors] = useState6(/* @__PURE__ */ new Map());
|
|
1593
|
+
const hasEditErrors = editErrors.size > 0;
|
|
1594
|
+
const [activeRowId, setActiveRowId] = useState6(null);
|
|
1595
|
+
const [autoFocusTarget, setAutoFocusTarget] = useState6(null);
|
|
1596
|
+
function activateRow(rowId, columnId) {
|
|
1597
|
+
setActiveRowId(rowId);
|
|
1598
|
+
setAutoFocusTarget({ rowId, columnId });
|
|
1599
|
+
}
|
|
1600
|
+
useEffect5(() => {
|
|
1601
|
+
if (autoFocusTarget) setAutoFocusTarget(null);
|
|
1602
|
+
}, [autoFocusTarget]);
|
|
1603
|
+
function commitEdit(column, row, value) {
|
|
1604
|
+
const rowId = getRowId(row);
|
|
1605
|
+
const baseline = getColumnValue(column, row);
|
|
1606
|
+
setPendingEdits((prev) => {
|
|
1607
|
+
const next = new Map(prev);
|
|
1608
|
+
const values = new Map(next.get(rowId)?.values);
|
|
1609
|
+
if (editValuesEqual(value, baseline)) values.delete(column.id);
|
|
1610
|
+
else values.set(column.id, value);
|
|
1611
|
+
if (values.size === 0) next.delete(rowId);
|
|
1612
|
+
else next.set(rowId, { row, values });
|
|
1613
|
+
return next;
|
|
1614
|
+
});
|
|
1615
|
+
const message = column.validateEdit?.(value, row);
|
|
1616
|
+
setEditErrors((prev) => {
|
|
1617
|
+
const next = new Map(prev);
|
|
1618
|
+
const rowErrors = new Map(next.get(rowId));
|
|
1619
|
+
if (message) rowErrors.set(column.id, message);
|
|
1620
|
+
else rowErrors.delete(column.id);
|
|
1621
|
+
if (rowErrors.size === 0) next.delete(rowId);
|
|
1622
|
+
else next.set(rowId, rowErrors);
|
|
1623
|
+
return next;
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
async function handleSaveEdits() {
|
|
1627
|
+
if (!editing || pendingEdits.size === 0) return;
|
|
1628
|
+
const snapshot = pendingEdits;
|
|
1629
|
+
const edited = [...snapshot.entries()].map(([rowId, entry]) => ({
|
|
1630
|
+
rowId,
|
|
1631
|
+
row: entry.row,
|
|
1632
|
+
values: Object.fromEntries(entry.values)
|
|
1633
|
+
}));
|
|
1634
|
+
try {
|
|
1635
|
+
await editing.onSave(edited);
|
|
1636
|
+
} catch {
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
const snapshotRowIds = new Set(snapshot.keys());
|
|
1640
|
+
const survivedRowIds = /* @__PURE__ */ new Set();
|
|
1641
|
+
const clearedErrorKeys = [];
|
|
1642
|
+
setPendingEdits((prev) => {
|
|
1643
|
+
const next = new Map(prev);
|
|
1644
|
+
for (const [rowId, snapshotEntry] of snapshot) {
|
|
1645
|
+
const current = next.get(rowId);
|
|
1646
|
+
if (!current) continue;
|
|
1647
|
+
const values = new Map(current.values);
|
|
1648
|
+
for (const [columnId, snapshotValue] of snapshotEntry.values) {
|
|
1649
|
+
if (values.has(columnId) && editValuesEqual(values.get(columnId), snapshotValue)) {
|
|
1650
|
+
values.delete(columnId);
|
|
1651
|
+
clearedErrorKeys.push({ rowId, columnId });
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
if (values.size === 0) {
|
|
1655
|
+
next.delete(rowId);
|
|
1656
|
+
} else {
|
|
1657
|
+
next.set(rowId, { row: current.row, values });
|
|
1658
|
+
survivedRowIds.add(rowId);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
return next;
|
|
1662
|
+
});
|
|
1663
|
+
setEditErrors((prev) => {
|
|
1664
|
+
if (clearedErrorKeys.length === 0) return prev;
|
|
1665
|
+
const next = new Map(prev);
|
|
1666
|
+
for (const { rowId, columnId } of clearedErrorKeys) {
|
|
1667
|
+
const rowErrors = next.get(rowId);
|
|
1668
|
+
if (!rowErrors) continue;
|
|
1669
|
+
const nextRowErrors = new Map(rowErrors);
|
|
1670
|
+
nextRowErrors.delete(columnId);
|
|
1671
|
+
if (nextRowErrors.size === 0) next.delete(rowId);
|
|
1672
|
+
else next.set(rowId, nextRowErrors);
|
|
1673
|
+
}
|
|
1674
|
+
return next;
|
|
1675
|
+
});
|
|
1676
|
+
setActiveRowId(
|
|
1677
|
+
(prev) => prev !== null && snapshotRowIds.has(prev) && !survivedRowIds.has(prev) ? null : prev
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
function handleDiscardEdits() {
|
|
1681
|
+
setPendingEdits(/* @__PURE__ */ new Map());
|
|
1682
|
+
setEditErrors(/* @__PURE__ */ new Map());
|
|
1683
|
+
setActiveRowId(null);
|
|
1684
|
+
editing?.onDiscard?.();
|
|
1685
|
+
}
|
|
1686
|
+
const ctx = editing ? { pendingEdits, editErrors, activeRowId, autoFocusTarget, getRowId, onEdit: commitEdit, onActivateRow: activateRow } : void 0;
|
|
1687
|
+
const ctxRef = useRef5(void 0);
|
|
1688
|
+
ctxRef.current = ctx;
|
|
1689
|
+
return { pendingEdits, editErrors, hasEditErrors, ctx, ctxRef, handleSaveEdits, handleDiscardEdits };
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
// src/edit/renderEditableCell.tsx
|
|
1693
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1694
|
+
function renderEditableCell(column, row, rawValue, ctx) {
|
|
1695
|
+
const staticContent = (value2) => column.cell ? column.cell(value2, row) : defaultFormat(column, value2);
|
|
1696
|
+
if (!ctx || !isEditable(column, row)) return staticContent(rawValue);
|
|
1697
|
+
const rowId = ctx.getRowId(row);
|
|
1698
|
+
const pendingRow = ctx.pendingEdits.get(rowId);
|
|
1699
|
+
const value = pendingRow?.values.has(column.id) ? pendingRow.values.get(column.id) : rawValue;
|
|
1700
|
+
if (ctx.activeRowId === rowId) {
|
|
1701
|
+
const error = ctx.editErrors.get(rowId)?.get(column.id);
|
|
1702
|
+
const autoFocus = ctx.autoFocusTarget?.rowId === rowId && ctx.autoFocusTarget.columnId === column.id;
|
|
1703
|
+
const onChange = (next) => ctx.onEdit(column, row, next);
|
|
1704
|
+
const editor = column.renderEditCell ? column.renderEditCell(value, row, onChange, error, autoFocus) : renderDefaultEditWidget(column, rowId, value, onChange, error, autoFocus);
|
|
1705
|
+
return /* @__PURE__ */ jsx22(
|
|
1706
|
+
"div",
|
|
1707
|
+
{
|
|
1708
|
+
onKeyDown: (event) => {
|
|
1709
|
+
if (event.key !== "Escape") return;
|
|
1710
|
+
if (!event.currentTarget.contains(event.target)) return;
|
|
1711
|
+
event.stopPropagation();
|
|
1712
|
+
ctx.onEdit(column, row, getColumnValue(column, row));
|
|
1713
|
+
},
|
|
1714
|
+
children: editor
|
|
1715
|
+
}
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1718
|
+
return /* @__PURE__ */ jsx22(
|
|
1719
|
+
"span",
|
|
1720
|
+
{
|
|
1721
|
+
role: "button",
|
|
1722
|
+
tabIndex: 0,
|
|
1723
|
+
"data-testid": `cell-${rowId}-${column.id}`,
|
|
1724
|
+
className: "block w-full cursor-text",
|
|
1725
|
+
onClick: (event) => {
|
|
1726
|
+
stopRowClick(event);
|
|
1727
|
+
ctx.onActivateRow(rowId, column.id);
|
|
1728
|
+
},
|
|
1729
|
+
onKeyDown: (event) => {
|
|
1730
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
1731
|
+
event.preventDefault();
|
|
1732
|
+
stopRowClick(event);
|
|
1733
|
+
ctx.onActivateRow(rowId, column.id);
|
|
1734
|
+
},
|
|
1735
|
+
children: staticContent(value)
|
|
1736
|
+
}
|
|
1737
|
+
);
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// src/edit/EditingBar.tsx
|
|
1741
|
+
import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1742
|
+
function resolveEditLabel(label, changedRowCount, fallback) {
|
|
1743
|
+
if (label === void 0) return fallback(changedRowCount);
|
|
1744
|
+
return typeof label === "function" ? label(changedRowCount) : label;
|
|
1745
|
+
}
|
|
1746
|
+
function describeErrorRows(editErrors) {
|
|
1747
|
+
const rowIds = [...editErrors.keys()];
|
|
1748
|
+
const shown = rowIds.slice(0, 3);
|
|
1749
|
+
const suffix = rowIds.length > shown.length ? `, and ${rowIds.length - shown.length} more` : "";
|
|
1750
|
+
return `row${rowIds.length === 1 ? "" : "s"} ${shown.join(", ")}${suffix}`;
|
|
1751
|
+
}
|
|
1752
|
+
function EditingBar({
|
|
1753
|
+
editing,
|
|
1754
|
+
pendingRowCount,
|
|
1755
|
+
editErrors,
|
|
1756
|
+
onSave,
|
|
1757
|
+
onDiscard,
|
|
1758
|
+
testIdPrefix = "datagrid"
|
|
1759
|
+
}) {
|
|
1760
|
+
const hasEditErrors = editErrors.size > 0;
|
|
1761
|
+
return /* @__PURE__ */ jsxs14(
|
|
1762
|
+
"div",
|
|
1763
|
+
{
|
|
1764
|
+
"data-testid": `${testIdPrefix}-edit-bar`,
|
|
1765
|
+
className: "flex shrink-0 flex-wrap items-center justify-between gap-2 rounded-md border bg-muted/50 px-3 py-2 text-sm",
|
|
1766
|
+
children: [
|
|
1767
|
+
/* @__PURE__ */ jsxs14("span", { "aria-live": "polite", children: [
|
|
1768
|
+
pendingRowCount,
|
|
1769
|
+
" row",
|
|
1770
|
+
pendingRowCount === 1 ? "" : "s",
|
|
1771
|
+
" changed",
|
|
1772
|
+
hasEditErrors && /* @__PURE__ */ jsxs14("span", { className: "ml-2 text-destructive", children: [
|
|
1773
|
+
"Fix the highlighted errors before saving (",
|
|
1774
|
+
describeErrorRows(editErrors),
|
|
1775
|
+
")."
|
|
1776
|
+
] })
|
|
1777
|
+
] }),
|
|
1778
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex gap-2", children: [
|
|
1779
|
+
/* @__PURE__ */ jsx23(
|
|
1780
|
+
Button,
|
|
1781
|
+
{
|
|
1782
|
+
type: "button",
|
|
1783
|
+
variant: "outline",
|
|
1784
|
+
size: "sm",
|
|
1785
|
+
"data-testid": `${testIdPrefix}-discard-edits`,
|
|
1786
|
+
disabled: editing.saving,
|
|
1787
|
+
onClick: onDiscard,
|
|
1788
|
+
children: resolveEditLabel(editing.discardLabel, pendingRowCount, () => "Discard")
|
|
1789
|
+
}
|
|
1790
|
+
),
|
|
1791
|
+
/* @__PURE__ */ jsx23(
|
|
1792
|
+
Button,
|
|
1793
|
+
{
|
|
1794
|
+
type: "button",
|
|
1795
|
+
size: "sm",
|
|
1796
|
+
"data-testid": `${testIdPrefix}-save-edits`,
|
|
1797
|
+
disabled: editing.saving || hasEditErrors,
|
|
1798
|
+
onClick: onSave,
|
|
1799
|
+
children: resolveEditLabel(
|
|
1800
|
+
editing.saveLabel,
|
|
1801
|
+
pendingRowCount,
|
|
1802
|
+
(count) => `Save ${count} change${count === 1 ? "" : "s"}`
|
|
1803
|
+
)
|
|
1804
|
+
}
|
|
1805
|
+
)
|
|
1806
|
+
] })
|
|
1807
|
+
]
|
|
1808
|
+
}
|
|
1809
|
+
);
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1568
1812
|
// src/column-selector/ColumnSelector.tsx
|
|
1569
1813
|
import { Columns3 } from "lucide-react";
|
|
1570
|
-
import { useEffect as
|
|
1814
|
+
import { useEffect as useEffect6 } from "react";
|
|
1571
1815
|
|
|
1572
1816
|
// src/components/ui/dialog.tsx
|
|
1573
1817
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
1574
1818
|
import { X } from "lucide-react";
|
|
1575
1819
|
import { forwardRef as forwardRef6 } from "react";
|
|
1576
|
-
import { jsx as
|
|
1820
|
+
import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1577
1821
|
var Dialog = DialogPrimitive.Root;
|
|
1578
1822
|
var DialogTrigger = DialogPrimitive.Trigger;
|
|
1579
|
-
var DialogOverlay = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */
|
|
1823
|
+
var DialogOverlay = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
|
|
1580
1824
|
DialogPrimitive.Overlay,
|
|
1581
1825
|
{
|
|
1582
1826
|
ref,
|
|
@@ -1585,9 +1829,9 @@ var DialogOverlay = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ *
|
|
|
1585
1829
|
}
|
|
1586
1830
|
));
|
|
1587
1831
|
DialogOverlay.displayName = "DialogOverlay";
|
|
1588
|
-
var DialogContent = forwardRef6(({ className, children, ...props }, ref) => /* @__PURE__ */
|
|
1589
|
-
/* @__PURE__ */
|
|
1590
|
-
/* @__PURE__ */
|
|
1832
|
+
var DialogContent = forwardRef6(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs15(DialogPrimitive.Portal, { children: [
|
|
1833
|
+
/* @__PURE__ */ jsx24(DialogOverlay, {}),
|
|
1834
|
+
/* @__PURE__ */ jsxs15(
|
|
1591
1835
|
DialogPrimitive.Content,
|
|
1592
1836
|
{
|
|
1593
1837
|
ref,
|
|
@@ -1598,9 +1842,9 @@ var DialogContent = forwardRef6(({ className, children, ...props }, ref) => /* @
|
|
|
1598
1842
|
...props,
|
|
1599
1843
|
children: [
|
|
1600
1844
|
children,
|
|
1601
|
-
/* @__PURE__ */
|
|
1602
|
-
/* @__PURE__ */
|
|
1603
|
-
/* @__PURE__ */
|
|
1845
|
+
/* @__PURE__ */ jsxs15(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100", children: [
|
|
1846
|
+
/* @__PURE__ */ jsx24(X, { className: "h-4 w-4" }),
|
|
1847
|
+
/* @__PURE__ */ jsx24("span", { className: "sr-only", children: "Close" })
|
|
1604
1848
|
] })
|
|
1605
1849
|
]
|
|
1606
1850
|
}
|
|
@@ -1610,10 +1854,10 @@ DialogContent.displayName = "DialogContent";
|
|
|
1610
1854
|
var DialogHeader = ({
|
|
1611
1855
|
className,
|
|
1612
1856
|
...props
|
|
1613
|
-
}) => /* @__PURE__ */
|
|
1614
|
-
var DialogTitle = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */
|
|
1857
|
+
}) => /* @__PURE__ */ jsx24("div", { className: cn("flex flex-col gap-1.5", className), ...props });
|
|
1858
|
+
var DialogTitle = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
|
|
1615
1859
|
DialogTitle.displayName = "DialogTitle";
|
|
1616
|
-
var DialogDescription = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */
|
|
1860
|
+
var DialogDescription = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
|
|
1617
1861
|
DialogPrimitive.Description,
|
|
1618
1862
|
{
|
|
1619
1863
|
ref,
|
|
@@ -1681,7 +1925,7 @@ function canHideGroup(columns, visibility, groupColumnIds) {
|
|
|
1681
1925
|
}
|
|
1682
1926
|
|
|
1683
1927
|
// src/column-selector/ColumnSelector.tsx
|
|
1684
|
-
import { jsx as
|
|
1928
|
+
import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1685
1929
|
function ColumnSelector({
|
|
1686
1930
|
columns,
|
|
1687
1931
|
visibility,
|
|
@@ -1691,7 +1935,7 @@ function ColumnSelector({
|
|
|
1691
1935
|
open,
|
|
1692
1936
|
onOpenChange
|
|
1693
1937
|
}) {
|
|
1694
|
-
|
|
1938
|
+
useEffect6(() => {
|
|
1695
1939
|
if (!persistKey) return;
|
|
1696
1940
|
const restored = readPersistedVisibility(persistKey);
|
|
1697
1941
|
if (restored) onVisibilityChange({ ...visibility, ...restored });
|
|
@@ -1706,20 +1950,20 @@ function ColumnSelector({
|
|
|
1706
1950
|
}
|
|
1707
1951
|
const totalVisible = countVisible(columns, visibility);
|
|
1708
1952
|
const groups = groupColumns(columns);
|
|
1709
|
-
return /* @__PURE__ */
|
|
1710
|
-
/* @__PURE__ */
|
|
1711
|
-
/* @__PURE__ */
|
|
1712
|
-
/* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
group && /* @__PURE__ */
|
|
1953
|
+
return /* @__PURE__ */ jsxs16(Dialog, { open, onOpenChange, children: [
|
|
1954
|
+
/* @__PURE__ */ jsx25(DialogTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx25(Button, { variant: "outline", size: "icon", "aria-label": "Choose columns", children: /* @__PURE__ */ jsx25(Columns3, { className: "h-4 w-4", "aria-hidden": true }) }) }),
|
|
1955
|
+
/* @__PURE__ */ jsxs16(DialogContent, { className: "w-fit max-w-[calc(100vw-2rem)] sm:max-w-2xl", children: [
|
|
1956
|
+
/* @__PURE__ */ jsx25(DialogHeader, { children: /* @__PURE__ */ jsx25(DialogTitle, { children: "Columns" }) }),
|
|
1957
|
+
/* @__PURE__ */ jsx25("div", { className: "flex flex-wrap gap-x-8 gap-y-5", children: groups.map(({ group, columns: groupCols }) => /* @__PURE__ */ jsxs16("div", { className: "flex min-w-32 flex-col gap-1.5", children: [
|
|
1958
|
+
group && /* @__PURE__ */ jsx25("span", { className: "border-b pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: group }),
|
|
1715
1959
|
groupCols.map((column) => {
|
|
1716
1960
|
const visible = isColumnVisible(visibility, column.id);
|
|
1717
|
-
return /* @__PURE__ */
|
|
1961
|
+
return /* @__PURE__ */ jsxs16(
|
|
1718
1962
|
"label",
|
|
1719
1963
|
{
|
|
1720
1964
|
className: "flex items-center gap-2 rounded-md px-1.5 py-1 text-sm hover:bg-accent/50 focus-within:bg-accent/50",
|
|
1721
1965
|
children: [
|
|
1722
|
-
/* @__PURE__ */
|
|
1966
|
+
/* @__PURE__ */ jsx25(
|
|
1723
1967
|
Checkbox,
|
|
1724
1968
|
{
|
|
1725
1969
|
checked: visible,
|
|
@@ -1756,10 +2000,10 @@ import { useMemo as useMemo3 } from "react";
|
|
|
1756
2000
|
// src/components/ui/dropdown-menu.tsx
|
|
1757
2001
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
1758
2002
|
import { forwardRef as forwardRef7 } from "react";
|
|
1759
|
-
import { jsx as
|
|
2003
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
1760
2004
|
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
1761
2005
|
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
1762
|
-
var DropdownMenuContent = forwardRef7(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */
|
|
2006
|
+
var DropdownMenuContent = forwardRef7(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx26(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx26(
|
|
1763
2007
|
DropdownMenuPrimitive.Content,
|
|
1764
2008
|
{
|
|
1765
2009
|
ref,
|
|
@@ -1772,7 +2016,7 @@ var DropdownMenuContent = forwardRef7(({ className, sideOffset = 4, ...props },
|
|
|
1772
2016
|
}
|
|
1773
2017
|
) }));
|
|
1774
2018
|
DropdownMenuContent.displayName = "DropdownMenuContent";
|
|
1775
|
-
var DropdownMenuItem = forwardRef7(({ className, danger, ...props }, ref) => /* @__PURE__ */
|
|
2019
|
+
var DropdownMenuItem = forwardRef7(({ className, danger, ...props }, ref) => /* @__PURE__ */ jsx26(
|
|
1776
2020
|
DropdownMenuPrimitive.Item,
|
|
1777
2021
|
{
|
|
1778
2022
|
ref,
|
|
@@ -1785,7 +2029,7 @@ var DropdownMenuItem = forwardRef7(({ className, danger, ...props }, ref) => /*
|
|
|
1785
2029
|
}
|
|
1786
2030
|
));
|
|
1787
2031
|
DropdownMenuItem.displayName = "DropdownMenuItem";
|
|
1788
|
-
var DropdownMenuSeparator = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */
|
|
2032
|
+
var DropdownMenuSeparator = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx26(
|
|
1789
2033
|
DropdownMenuPrimitive.Separator,
|
|
1790
2034
|
{
|
|
1791
2035
|
ref,
|
|
@@ -1794,7 +2038,7 @@ var DropdownMenuSeparator = forwardRef7(({ className, ...props }, ref) => /* @__
|
|
|
1794
2038
|
}
|
|
1795
2039
|
));
|
|
1796
2040
|
DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
|
|
1797
|
-
var DropdownMenuLabel = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */
|
|
2041
|
+
var DropdownMenuLabel = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx26(
|
|
1798
2042
|
DropdownMenuPrimitive.Label,
|
|
1799
2043
|
{
|
|
1800
2044
|
ref,
|
|
@@ -1805,16 +2049,16 @@ var DropdownMenuLabel = forwardRef7(({ className, ...props }, ref) => /* @__PURE
|
|
|
1805
2049
|
DropdownMenuLabel.displayName = "DropdownMenuLabel";
|
|
1806
2050
|
|
|
1807
2051
|
// src/menu/ActionsMenu.tsx
|
|
1808
|
-
import { jsx as
|
|
2052
|
+
import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1809
2053
|
function ActionsMenu({ items, ctx, triggerLabel }) {
|
|
1810
2054
|
const resolved = useMemo3(
|
|
1811
2055
|
() => resolveMenuItems(items, ctx),
|
|
1812
2056
|
[items, ctx.row, ctx.selectedRows]
|
|
1813
2057
|
);
|
|
1814
2058
|
if (resolved.length === 0) return null;
|
|
1815
|
-
return /* @__PURE__ */
|
|
1816
|
-
/* @__PURE__ */
|
|
1817
|
-
/* @__PURE__ */
|
|
2059
|
+
return /* @__PURE__ */ jsxs17(DropdownMenu, { children: [
|
|
2060
|
+
/* @__PURE__ */ jsx27(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx27(Button, { variant: "ghost", size: "icon", "aria-label": triggerLabel, onClick: stopRowClick, children: /* @__PURE__ */ jsx27(MoreVertical, { className: "h-4 w-4", "aria-hidden": true }) }) }),
|
|
2061
|
+
/* @__PURE__ */ jsx27(DropdownMenuContent, { align: "end", children: resolved.map((item) => /* @__PURE__ */ jsx27(
|
|
1818
2062
|
DropdownMenuItem,
|
|
1819
2063
|
{
|
|
1820
2064
|
danger: item.danger,
|
|
@@ -1832,7 +2076,59 @@ import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon as FunnelIcon7 } from "@
|
|
|
1832
2076
|
import { columnResizingFeature, columnSizingFeature, flexRender, tableFeatures, useTable } from "@tanstack/react-table";
|
|
1833
2077
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
1834
2078
|
import { ChevronRight, Loader2 } from "lucide-react";
|
|
1835
|
-
import { Fragment as Fragment2, useEffect as
|
|
2079
|
+
import { Fragment as Fragment2, useEffect as useEffect9, useMemo as useMemo7, useRef as useRef8, useState as useState10 } from "react";
|
|
2080
|
+
|
|
2081
|
+
// src/hooks/useGroupExpansion.ts
|
|
2082
|
+
import { useCallback as useCallback2, useState as useState7 } from "react";
|
|
2083
|
+
function useGroupExpansion(defaultGroupsExpanded, controlledExpandedGroups, onExpandedGroupsChange) {
|
|
2084
|
+
const [internalExpandedGroups, setInternalExpandedGroups] = useState7({});
|
|
2085
|
+
const expandedGroups = controlledExpandedGroups ?? internalExpandedGroups;
|
|
2086
|
+
const updateExpandedGroups = useCallback2(
|
|
2087
|
+
(next) => {
|
|
2088
|
+
setInternalExpandedGroups(next);
|
|
2089
|
+
onExpandedGroupsChange?.(next);
|
|
2090
|
+
},
|
|
2091
|
+
[onExpandedGroupsChange]
|
|
2092
|
+
);
|
|
2093
|
+
const isGroupExpanded = useCallback2(
|
|
2094
|
+
(key) => expandedGroups[key] ?? defaultGroupsExpanded,
|
|
2095
|
+
[expandedGroups, defaultGroupsExpanded]
|
|
2096
|
+
);
|
|
2097
|
+
const toggleGroupExpanded = useCallback2(
|
|
2098
|
+
(key) => {
|
|
2099
|
+
updateExpandedGroups({ ...expandedGroups, [key]: !isGroupExpanded(key) });
|
|
2100
|
+
},
|
|
2101
|
+
[expandedGroups, isGroupExpanded, updateExpandedGroups]
|
|
2102
|
+
);
|
|
2103
|
+
return { expandedGroups, isGroupExpanded, toggleGroupExpanded };
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
// src/hooks/useStickyGroupHeaderTop.ts
|
|
2107
|
+
import { useLayoutEffect, useRef as useRef6, useState as useState8 } from "react";
|
|
2108
|
+
function useStickyGroupHeaderTop(enabled) {
|
|
2109
|
+
const theadRef = useRef6(null);
|
|
2110
|
+
const [groupHeaderTop, setGroupHeaderTop] = useState8(0);
|
|
2111
|
+
useLayoutEffect(() => {
|
|
2112
|
+
if (!enabled) return;
|
|
2113
|
+
const theadEl = theadRef.current;
|
|
2114
|
+
if (!theadEl) return;
|
|
2115
|
+
const updateHeight = () => setGroupHeaderTop(theadEl.getBoundingClientRect().height);
|
|
2116
|
+
updateHeight();
|
|
2117
|
+
const observer = new ResizeObserver(updateHeight);
|
|
2118
|
+
observer.observe(theadEl);
|
|
2119
|
+
return () => observer.disconnect();
|
|
2120
|
+
}, [enabled]);
|
|
2121
|
+
return { theadRef, groupHeaderTop };
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
// src/hooks/useVisibleColumns.ts
|
|
2125
|
+
import { useMemo as useMemo4 } from "react";
|
|
2126
|
+
function useVisibleColumns(columns, columnVisibility) {
|
|
2127
|
+
return useMemo4(
|
|
2128
|
+
() => columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,
|
|
2129
|
+
[columns, columnVisibility]
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
1836
2132
|
|
|
1837
2133
|
// src/lib/structuralColumns.ts
|
|
1838
2134
|
var SELECTION_COLUMN_WIDTH = 44;
|
|
@@ -1875,20 +2171,20 @@ function processClientData(data, state) {
|
|
|
1875
2171
|
}
|
|
1876
2172
|
|
|
1877
2173
|
// src/grid/useGridState.ts
|
|
1878
|
-
import { useCallback as
|
|
2174
|
+
import { useCallback as useCallback3, useEffect as useEffect8, useMemo as useMemo6, useState as useState9 } from "react";
|
|
1879
2175
|
|
|
1880
2176
|
// src/hooks/useDebouncedCallback.ts
|
|
1881
|
-
import { useEffect as
|
|
2177
|
+
import { useEffect as useEffect7, useMemo as useMemo5, useRef as useRef7 } from "react";
|
|
1882
2178
|
function useDebouncedCallback(callback, delayMs) {
|
|
1883
|
-
const callbackRef =
|
|
2179
|
+
const callbackRef = useRef7(callback);
|
|
1884
2180
|
callbackRef.current = callback;
|
|
1885
|
-
const timeoutRef =
|
|
1886
|
-
|
|
2181
|
+
const timeoutRef = useRef7(void 0);
|
|
2182
|
+
useEffect7(() => {
|
|
1887
2183
|
return () => {
|
|
1888
2184
|
clearTimeout(timeoutRef.current);
|
|
1889
2185
|
};
|
|
1890
2186
|
}, []);
|
|
1891
|
-
return
|
|
2187
|
+
return useMemo5(() => {
|
|
1892
2188
|
return {
|
|
1893
2189
|
run: (...args) => {
|
|
1894
2190
|
clearTimeout(timeoutRef.current);
|
|
@@ -1913,10 +2209,10 @@ function nextSortDir(current, descFirst) {
|
|
|
1913
2209
|
return void 0;
|
|
1914
2210
|
}
|
|
1915
2211
|
function useGridState(dataSource, initialState, externalState) {
|
|
1916
|
-
const [state, setState] =
|
|
2212
|
+
const [state, setState] = useState9(
|
|
1917
2213
|
() => externalState ?? { ...DEFAULT_STATE, ...initialState }
|
|
1918
2214
|
);
|
|
1919
|
-
const notifyRaw =
|
|
2215
|
+
const notifyRaw = useCallback3(
|
|
1920
2216
|
(next) => {
|
|
1921
2217
|
if (dataSource.mode === "server") dataSource.onStateChange(next);
|
|
1922
2218
|
},
|
|
@@ -1926,26 +2222,26 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
1926
2222
|
notifyRaw,
|
|
1927
2223
|
FILTER_CHANGE_DEBOUNCE_MS
|
|
1928
2224
|
);
|
|
1929
|
-
const notifyNow =
|
|
2225
|
+
const notifyNow = useCallback3(
|
|
1930
2226
|
(next) => {
|
|
1931
2227
|
cancelDebouncedNotify();
|
|
1932
2228
|
notifyRaw(next);
|
|
1933
2229
|
},
|
|
1934
2230
|
[notifyRaw, cancelDebouncedNotify]
|
|
1935
2231
|
);
|
|
1936
|
-
|
|
2232
|
+
useEffect8(() => {
|
|
1937
2233
|
if (!externalState) return;
|
|
1938
2234
|
cancelDebouncedNotify();
|
|
1939
2235
|
setState(externalState);
|
|
1940
2236
|
}, [externalState, cancelDebouncedNotify]);
|
|
1941
|
-
const filtersByColumn =
|
|
2237
|
+
const filtersByColumn = useMemo6(() => {
|
|
1942
2238
|
const map = /* @__PURE__ */ new Map();
|
|
1943
2239
|
for (const child of state.filter?.filters ?? []) {
|
|
1944
2240
|
if ("operator" in child) map.set(fieldKey(child.field), child);
|
|
1945
2241
|
}
|
|
1946
2242
|
return map;
|
|
1947
2243
|
}, [state.filter]);
|
|
1948
|
-
const setColumnFilter =
|
|
2244
|
+
const setColumnFilter = useCallback3(
|
|
1949
2245
|
(columnId, next) => {
|
|
1950
2246
|
setState((prev) => {
|
|
1951
2247
|
const remaining = (prev.filter?.filters ?? []).filter(
|
|
@@ -1963,7 +2259,7 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
1963
2259
|
},
|
|
1964
2260
|
[notifyDebounced]
|
|
1965
2261
|
);
|
|
1966
|
-
const toggleSort =
|
|
2262
|
+
const toggleSort = useCallback3(
|
|
1967
2263
|
(columnId, additive, descFirst = false) => {
|
|
1968
2264
|
setState((prev) => {
|
|
1969
2265
|
const existing = prev.sort.find((s) => s.field === columnId);
|
|
@@ -1978,7 +2274,7 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
1978
2274
|
},
|
|
1979
2275
|
[notifyNow]
|
|
1980
2276
|
);
|
|
1981
|
-
const setPage =
|
|
2277
|
+
const setPage = useCallback3(
|
|
1982
2278
|
(page) => {
|
|
1983
2279
|
setState((prev) => {
|
|
1984
2280
|
const nextState = { ...prev, page };
|
|
@@ -1988,7 +2284,7 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
1988
2284
|
},
|
|
1989
2285
|
[notifyNow]
|
|
1990
2286
|
);
|
|
1991
|
-
const setPageSize =
|
|
2287
|
+
const setPageSize = useCallback3(
|
|
1992
2288
|
(pageSize) => {
|
|
1993
2289
|
setState((prev) => {
|
|
1994
2290
|
const nextState = { ...prev, pageSize, page: 0 };
|
|
@@ -2002,7 +2298,7 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
2002
2298
|
}
|
|
2003
2299
|
|
|
2004
2300
|
// src/grid/DataGrid.tsx
|
|
2005
|
-
import { Fragment as Fragment3, jsx as
|
|
2301
|
+
import { Fragment as Fragment3, jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2006
2302
|
var gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });
|
|
2007
2303
|
var DEFAULT_COLUMN_SIZE = 150;
|
|
2008
2304
|
var DETAIL_COLUMN_WIDTH = 44;
|
|
@@ -2039,71 +2335,9 @@ function toTanstackColumns(columns, editingCtxRef) {
|
|
|
2039
2335
|
header: column.header,
|
|
2040
2336
|
size: column.width,
|
|
2041
2337
|
accessorFn: (row) => getColumnValue(column, row),
|
|
2042
|
-
cell: (info) =>
|
|
2043
|
-
const row = info.row.original;
|
|
2044
|
-
const editingCtx = editingCtxRef.current;
|
|
2045
|
-
const staticContent = (value) => column.cell ? column.cell(value, row) : defaultFormat(column, value);
|
|
2046
|
-
if (editingCtx && isEditable(column, row)) {
|
|
2047
|
-
const rowId = editingCtx.getRowId(row);
|
|
2048
|
-
const pendingRow = editingCtx.pendingEdits.get(rowId);
|
|
2049
|
-
const value = pendingRow?.values.has(column.id) ? pendingRow.values.get(column.id) : info.getValue();
|
|
2050
|
-
if (editingCtx.activeRowId === rowId) {
|
|
2051
|
-
const error = editingCtx.editErrors.get(rowId)?.get(column.id);
|
|
2052
|
-
const autoFocus = editingCtx.autoFocusTarget?.rowId === rowId && editingCtx.autoFocusTarget.columnId === column.id;
|
|
2053
|
-
const onChange = (next) => editingCtx.onEdit(column, row, next);
|
|
2054
|
-
const editor = column.renderEditCell ? column.renderEditCell(value, row, onChange, error, autoFocus) : renderDefaultEditWidget(column, rowId, value, onChange, error, autoFocus);
|
|
2055
|
-
return /* @__PURE__ */ jsx26(
|
|
2056
|
-
"div",
|
|
2057
|
-
{
|
|
2058
|
-
onKeyDown: (event) => {
|
|
2059
|
-
if (event.key !== "Escape") return;
|
|
2060
|
-
if (!event.currentTarget.contains(event.target)) return;
|
|
2061
|
-
event.stopPropagation();
|
|
2062
|
-
editingCtx.onEdit(column, row, getColumnValue(column, row));
|
|
2063
|
-
},
|
|
2064
|
-
children: editor
|
|
2065
|
-
}
|
|
2066
|
-
);
|
|
2067
|
-
}
|
|
2068
|
-
return /* @__PURE__ */ jsx26(
|
|
2069
|
-
"span",
|
|
2070
|
-
{
|
|
2071
|
-
role: "button",
|
|
2072
|
-
tabIndex: 0,
|
|
2073
|
-
"data-testid": `cell-${rowId}-${column.id}`,
|
|
2074
|
-
className: "block w-full cursor-text",
|
|
2075
|
-
onClick: (event) => {
|
|
2076
|
-
stopRowClick(event);
|
|
2077
|
-
editingCtx.onActivateRow(rowId, column.id);
|
|
2078
|
-
},
|
|
2079
|
-
onKeyDown: (event) => {
|
|
2080
|
-
if (event.key !== "Enter" && event.key !== " ") return;
|
|
2081
|
-
event.preventDefault();
|
|
2082
|
-
stopRowClick(event);
|
|
2083
|
-
editingCtx.onActivateRow(rowId, column.id);
|
|
2084
|
-
},
|
|
2085
|
-
children: staticContent(value)
|
|
2086
|
-
}
|
|
2087
|
-
);
|
|
2088
|
-
}
|
|
2089
|
-
return staticContent(info.getValue());
|
|
2090
|
-
}
|
|
2338
|
+
cell: (info) => renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current)
|
|
2091
2339
|
}));
|
|
2092
2340
|
}
|
|
2093
|
-
function editValuesEqual(a, b) {
|
|
2094
|
-
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
|
|
2095
|
-
return a === b;
|
|
2096
|
-
}
|
|
2097
|
-
function resolveEditLabel(label, changedRowCount, fallback) {
|
|
2098
|
-
if (label === void 0) return fallback(changedRowCount);
|
|
2099
|
-
return typeof label === "function" ? label(changedRowCount) : label;
|
|
2100
|
-
}
|
|
2101
|
-
function describeErrorRows(editErrors) {
|
|
2102
|
-
const rowIds = [...editErrors.keys()];
|
|
2103
|
-
const shown = rowIds.slice(0, 3);
|
|
2104
|
-
const suffix = rowIds.length > shown.length ? `, and ${rowIds.length - shown.length} more` : "";
|
|
2105
|
-
return `row${rowIds.length === 1 ? "" : "s"} ${shown.join(", ")}${suffix}`;
|
|
2106
|
-
}
|
|
2107
2341
|
function DataGrid({
|
|
2108
2342
|
columns,
|
|
2109
2343
|
dataSource,
|
|
@@ -2140,19 +2374,19 @@ function DataGrid({
|
|
|
2140
2374
|
initialState,
|
|
2141
2375
|
gridState
|
|
2142
2376
|
);
|
|
2143
|
-
const [internalSelectedIds, setInternalSelectedIds] =
|
|
2377
|
+
const [internalSelectedIds, setInternalSelectedIds] = useState10(/* @__PURE__ */ new Set());
|
|
2144
2378
|
const selectedIds = controlledSelectedIds ?? internalSelectedIds;
|
|
2145
2379
|
function updateSelectedIds(next) {
|
|
2146
2380
|
setInternalSelectedIds(next);
|
|
2147
2381
|
onSelectedIdsChange?.(next);
|
|
2148
2382
|
}
|
|
2149
|
-
const [internalColumnSizing, setInternalColumnSizing] =
|
|
2383
|
+
const [internalColumnSizing, setInternalColumnSizing] = useState10({});
|
|
2150
2384
|
const columnSizing = controlledColumnSizing ?? internalColumnSizing;
|
|
2151
2385
|
function updateColumnSizing(next) {
|
|
2152
2386
|
setInternalColumnSizing(next);
|
|
2153
2387
|
onColumnSizingChange?.(next);
|
|
2154
2388
|
}
|
|
2155
|
-
const [expandedIds, setExpandedIds] =
|
|
2389
|
+
const [expandedIds, setExpandedIds] = useState10(/* @__PURE__ */ new Set());
|
|
2156
2390
|
function toggleRowExpanded(id) {
|
|
2157
2391
|
setExpandedIds((prev) => {
|
|
2158
2392
|
const next = new Set(prev);
|
|
@@ -2161,124 +2395,22 @@ function DataGrid({
|
|
|
2161
2395
|
return next;
|
|
2162
2396
|
});
|
|
2163
2397
|
}
|
|
2164
|
-
const
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
}
|
|
2173
|
-
function toggleGroupExpanded(key) {
|
|
2174
|
-
updateExpandedGroups({ ...expandedGroupsState, [key]: !isGroupExpanded(key) });
|
|
2175
|
-
}
|
|
2176
|
-
const [pendingEdits, setPendingEdits] = useState6(/* @__PURE__ */ new Map());
|
|
2177
|
-
const [editErrors, setEditErrors] = useState6(/* @__PURE__ */ new Map());
|
|
2178
|
-
const hasEditErrors = editErrors.size > 0;
|
|
2179
|
-
const [activeRowId, setActiveRowId] = useState6(null);
|
|
2180
|
-
const [autoFocusTarget, setAutoFocusTarget] = useState6(null);
|
|
2181
|
-
function activateRow(rowId, columnId) {
|
|
2182
|
-
setActiveRowId(rowId);
|
|
2183
|
-
setAutoFocusTarget({ rowId, columnId });
|
|
2184
|
-
}
|
|
2185
|
-
useEffect7(() => {
|
|
2186
|
-
if (autoFocusTarget) setAutoFocusTarget(null);
|
|
2187
|
-
}, [autoFocusTarget]);
|
|
2188
|
-
function commitEdit(column, row, value) {
|
|
2189
|
-
const rowId = getRowId(row);
|
|
2190
|
-
const baseline = getColumnValue(column, row);
|
|
2191
|
-
setPendingEdits((prev) => {
|
|
2192
|
-
const next = new Map(prev);
|
|
2193
|
-
const values = new Map(next.get(rowId)?.values);
|
|
2194
|
-
if (editValuesEqual(value, baseline)) values.delete(column.id);
|
|
2195
|
-
else values.set(column.id, value);
|
|
2196
|
-
if (values.size === 0) next.delete(rowId);
|
|
2197
|
-
else next.set(rowId, { row, values });
|
|
2198
|
-
return next;
|
|
2199
|
-
});
|
|
2200
|
-
const message = column.validateEdit?.(value, row);
|
|
2201
|
-
setEditErrors((prev) => {
|
|
2202
|
-
const next = new Map(prev);
|
|
2203
|
-
const rowErrors = new Map(next.get(rowId));
|
|
2204
|
-
if (message) rowErrors.set(column.id, message);
|
|
2205
|
-
else rowErrors.delete(column.id);
|
|
2206
|
-
if (rowErrors.size === 0) next.delete(rowId);
|
|
2207
|
-
else next.set(rowId, rowErrors);
|
|
2208
|
-
return next;
|
|
2209
|
-
});
|
|
2210
|
-
}
|
|
2211
|
-
async function handleSaveEdits() {
|
|
2212
|
-
if (!editing || pendingEdits.size === 0) return;
|
|
2213
|
-
const snapshot = pendingEdits;
|
|
2214
|
-
const edited = [...snapshot.entries()].map(([rowId, entry]) => ({
|
|
2215
|
-
rowId,
|
|
2216
|
-
row: entry.row,
|
|
2217
|
-
values: Object.fromEntries(entry.values)
|
|
2218
|
-
}));
|
|
2219
|
-
try {
|
|
2220
|
-
await editing.onSave(edited);
|
|
2221
|
-
} catch {
|
|
2222
|
-
return;
|
|
2223
|
-
}
|
|
2224
|
-
const snapshotRowIds = new Set(snapshot.keys());
|
|
2225
|
-
const survivedRowIds = /* @__PURE__ */ new Set();
|
|
2226
|
-
const clearedErrorKeys = [];
|
|
2227
|
-
setPendingEdits((prev) => {
|
|
2228
|
-
const next = new Map(prev);
|
|
2229
|
-
for (const [rowId, snapshotEntry] of snapshot) {
|
|
2230
|
-
const current = next.get(rowId);
|
|
2231
|
-
if (!current) continue;
|
|
2232
|
-
const values = new Map(current.values);
|
|
2233
|
-
for (const [columnId, snapshotValue] of snapshotEntry.values) {
|
|
2234
|
-
if (values.has(columnId) && editValuesEqual(values.get(columnId), snapshotValue)) {
|
|
2235
|
-
values.delete(columnId);
|
|
2236
|
-
clearedErrorKeys.push({ rowId, columnId });
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
if (values.size === 0) next.delete(rowId);
|
|
2240
|
-
else {
|
|
2241
|
-
next.set(rowId, { row: current.row, values });
|
|
2242
|
-
survivedRowIds.add(rowId);
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
return next;
|
|
2246
|
-
});
|
|
2247
|
-
setEditErrors((prev) => {
|
|
2248
|
-
if (clearedErrorKeys.length === 0) return prev;
|
|
2249
|
-
const next = new Map(prev);
|
|
2250
|
-
for (const { rowId, columnId } of clearedErrorKeys) {
|
|
2251
|
-
const rowErrors = next.get(rowId);
|
|
2252
|
-
if (!rowErrors) continue;
|
|
2253
|
-
const nextRowErrors = new Map(rowErrors);
|
|
2254
|
-
nextRowErrors.delete(columnId);
|
|
2255
|
-
if (nextRowErrors.size === 0) next.delete(rowId);
|
|
2256
|
-
else next.set(rowId, nextRowErrors);
|
|
2257
|
-
}
|
|
2258
|
-
return next;
|
|
2259
|
-
});
|
|
2260
|
-
setActiveRowId(
|
|
2261
|
-
(prev) => prev !== null && snapshotRowIds.has(prev) && !survivedRowIds.has(prev) ? null : prev
|
|
2262
|
-
);
|
|
2263
|
-
}
|
|
2264
|
-
function handleDiscardEdits() {
|
|
2265
|
-
setPendingEdits(/* @__PURE__ */ new Map());
|
|
2266
|
-
setEditErrors(/* @__PURE__ */ new Map());
|
|
2267
|
-
setActiveRowId(null);
|
|
2268
|
-
editing?.onDiscard?.();
|
|
2269
|
-
}
|
|
2270
|
-
const editingCtxRef = useRef5(void 0);
|
|
2271
|
-
editingCtxRef.current = editing ? { pendingEdits, editErrors, activeRowId, autoFocusTarget, getRowId, onEdit: commitEdit, onActivateRow: activateRow } : void 0;
|
|
2272
|
-
const visibleColumns = useMemo6(
|
|
2273
|
-
() => columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,
|
|
2274
|
-
[columns, columnVisibility]
|
|
2398
|
+
const { isGroupExpanded, toggleGroupExpanded } = useGroupExpansion(
|
|
2399
|
+
defaultGroupsExpanded,
|
|
2400
|
+
controlledExpandedGroups,
|
|
2401
|
+
onExpandedGroupsChange
|
|
2402
|
+
);
|
|
2403
|
+
const { pendingEdits, editErrors, ctxRef: editingCtxRef, handleSaveEdits, handleDiscardEdits } = useEditingState(
|
|
2404
|
+
editing,
|
|
2405
|
+
getRowId
|
|
2275
2406
|
);
|
|
2407
|
+
const visibleColumns = useVisibleColumns(columns, columnVisibility);
|
|
2276
2408
|
const serverRowCount = dataSource.mode === "server" ? dataSource.rowCount : void 0;
|
|
2277
|
-
const { rows, rowCount } =
|
|
2409
|
+
const { rows, rowCount } = useMemo7(() => {
|
|
2278
2410
|
if (dataSource.mode === "client") return processClientData(dataSource.data, state);
|
|
2279
2411
|
return { rows: dataSource.data, rowCount: serverRowCount ?? 0 };
|
|
2280
2412
|
}, [dataSource.mode, dataSource.data, serverRowCount, state]);
|
|
2281
|
-
const selectedRows =
|
|
2413
|
+
const selectedRows = useMemo7(
|
|
2282
2414
|
() => rows.filter((row) => selectedIds.has(getRowId(row))),
|
|
2283
2415
|
[rows, selectedIds, getRowId]
|
|
2284
2416
|
);
|
|
@@ -2297,8 +2429,8 @@ function DataGrid({
|
|
|
2297
2429
|
}
|
|
2298
2430
|
updateSelectedIds(next);
|
|
2299
2431
|
}
|
|
2300
|
-
const tanstackColumns =
|
|
2301
|
-
const columnById =
|
|
2432
|
+
const tanstackColumns = useMemo7(() => toTanstackColumns(visibleColumns, editingCtxRef), [visibleColumns]);
|
|
2433
|
+
const columnById = useMemo7(() => {
|
|
2302
2434
|
const map = /* @__PURE__ */ new Map();
|
|
2303
2435
|
for (const column of visibleColumns) map.set(column.id, column);
|
|
2304
2436
|
return map;
|
|
@@ -2323,7 +2455,7 @@ function DataGrid({
|
|
|
2323
2455
|
const showDetailColumn = Boolean(renderDetail);
|
|
2324
2456
|
const totalColumnCount = visibleColumns.length + (showDetailColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0) + (showRowActionsColumn ? 1 : 0);
|
|
2325
2457
|
const hasFilterRow = visibleColumns.some((column) => isFilterable(column) && column.filterDisplay === "row");
|
|
2326
|
-
const headerRuns =
|
|
2458
|
+
const headerRuns = useMemo7(() => computeHeaderRuns(visibleColumns), [visibleColumns]);
|
|
2327
2459
|
const hasHeaderGroups = visibleColumns.some((column) => column.headerGroup);
|
|
2328
2460
|
function renderFilterWidget(column) {
|
|
2329
2461
|
const value = filtersByColumn.get(column.id);
|
|
@@ -2333,11 +2465,11 @@ function DataGrid({
|
|
|
2333
2465
|
const columnSize = (columnId) => table.getColumn(columnId)?.getSize() ?? DEFAULT_COLUMN_SIZE;
|
|
2334
2466
|
const leadingStructuralWidth = (showDetailColumn ? DETAIL_COLUMN_WIDTH : 0) + (showSelectionColumn ? SELECTION_COLUMN_WIDTH : 0);
|
|
2335
2467
|
const trailingStructuralWidth = showRowActionsColumn ? ROW_ACTIONS_COLUMN_WIDTH : 0;
|
|
2336
|
-
const leftPinnedOffsets =
|
|
2468
|
+
const leftPinnedOffsets = useMemo7(
|
|
2337
2469
|
() => pinnedOffsets(visibleColumns, "left", columnSize, leadingStructuralWidth),
|
|
2338
2470
|
[visibleColumns, columnSizing, leadingStructuralWidth]
|
|
2339
2471
|
);
|
|
2340
|
-
const rightPinnedOffsets =
|
|
2472
|
+
const rightPinnedOffsets = useMemo7(
|
|
2341
2473
|
() => pinnedOffsets(visibleColumns, "right", columnSize, trailingStructuralWidth),
|
|
2342
2474
|
[visibleColumns, columnSizing, trailingStructuralWidth]
|
|
2343
2475
|
);
|
|
@@ -2385,7 +2517,7 @@ function DataGrid({
|
|
|
2385
2517
|
style: pinnedProps?.style
|
|
2386
2518
|
};
|
|
2387
2519
|
}
|
|
2388
|
-
const bodyCellPropsByColumn =
|
|
2520
|
+
const bodyCellPropsByColumn = useMemo7(() => {
|
|
2389
2521
|
const map = /* @__PURE__ */ new Map();
|
|
2390
2522
|
for (const column of visibleColumns) map.set(column.id, bodyCellClassAndStyle(column));
|
|
2391
2523
|
return map;
|
|
@@ -2401,7 +2533,7 @@ function DataGrid({
|
|
|
2401
2533
|
);
|
|
2402
2534
|
return { className, style: pinnedProps.style };
|
|
2403
2535
|
}
|
|
2404
|
-
const leafHeaderCellPropsByColumn =
|
|
2536
|
+
const leafHeaderCellPropsByColumn = useMemo7(() => {
|
|
2405
2537
|
const map = /* @__PURE__ */ new Map();
|
|
2406
2538
|
for (const column of visibleColumns) {
|
|
2407
2539
|
map.set(
|
|
@@ -2411,7 +2543,7 @@ function DataGrid({
|
|
|
2411
2543
|
}
|
|
2412
2544
|
return map;
|
|
2413
2545
|
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
2414
|
-
const filterHeaderCellPropsByColumn =
|
|
2546
|
+
const filterHeaderCellPropsByColumn = useMemo7(() => {
|
|
2415
2547
|
const map = /* @__PURE__ */ new Map();
|
|
2416
2548
|
for (const column of visibleColumns) {
|
|
2417
2549
|
map.set(column.id, headerCellClassAndStyle(column, "border-b border-border p-2 align-top font-normal"));
|
|
@@ -2420,23 +2552,12 @@ function DataGrid({
|
|
|
2420
2552
|
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
2421
2553
|
const tableRows = table.getRowModel().rows;
|
|
2422
2554
|
const shouldVirtualize = Boolean(virtualize) && !groupBy && tableRows.length > (virtualize?.threshold ?? 100);
|
|
2423
|
-
const groupedBuckets =
|
|
2555
|
+
const groupedBuckets = useMemo7(
|
|
2424
2556
|
() => groupBy ? groupRows(tableRows, (row) => groupBy(row.original)) : void 0,
|
|
2425
2557
|
[tableRows, groupBy]
|
|
2426
2558
|
);
|
|
2427
|
-
const theadRef =
|
|
2428
|
-
const
|
|
2429
|
-
useLayoutEffect(() => {
|
|
2430
|
-
if (!groupBy) return;
|
|
2431
|
-
const theadEl = theadRef.current;
|
|
2432
|
-
if (!theadEl) return;
|
|
2433
|
-
const updateHeight = () => setGroupHeaderTop(theadEl.getBoundingClientRect().height);
|
|
2434
|
-
updateHeight();
|
|
2435
|
-
const observer = new ResizeObserver(updateHeight);
|
|
2436
|
-
observer.observe(theadEl);
|
|
2437
|
-
return () => observer.disconnect();
|
|
2438
|
-
}, [groupBy]);
|
|
2439
|
-
const scrollRef = useRef5(null);
|
|
2559
|
+
const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));
|
|
2560
|
+
const scrollRef = useRef8(null);
|
|
2440
2561
|
const virtualizer = useVirtualizer({
|
|
2441
2562
|
count: tableRows.length,
|
|
2442
2563
|
getScrollElement: () => scrollRef.current,
|
|
@@ -2448,8 +2569,8 @@ function DataGrid({
|
|
|
2448
2569
|
const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
|
|
2449
2570
|
const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
|
|
2450
2571
|
const lastVisibleIndex = virtualItems.length > 0 ? virtualItems[virtualItems.length - 1].index : -1;
|
|
2451
|
-
const notifiedForLengthRef =
|
|
2452
|
-
|
|
2572
|
+
const notifiedForLengthRef = useRef8(-1);
|
|
2573
|
+
useEffect9(() => {
|
|
2453
2574
|
if (!virtualize?.onEndReached) return;
|
|
2454
2575
|
if (lastVisibleIndex < 0 || lastVisibleIndex < tableRows.length - 1) return;
|
|
2455
2576
|
if (virtualize.hasMore === false) return;
|
|
@@ -2462,20 +2583,20 @@ function DataGrid({
|
|
|
2462
2583
|
const rowProps = getRowProps?.(row.original);
|
|
2463
2584
|
const rowTestId = getRowTestId?.(row.original) ?? `row-${row.id}`;
|
|
2464
2585
|
const isOddRow = zebra && row.index % 2 === 1;
|
|
2465
|
-
return /* @__PURE__ */
|
|
2466
|
-
/* @__PURE__ */
|
|
2586
|
+
return /* @__PURE__ */ jsxs18("tbody", { ref: measureRef, "data-index": row.index, children: [
|
|
2587
|
+
/* @__PURE__ */ jsxs18(
|
|
2467
2588
|
"tr",
|
|
2468
2589
|
{
|
|
2469
2590
|
...rowProps,
|
|
2470
2591
|
"data-testid": rowTestId,
|
|
2471
2592
|
className: rowProps?.className ? cn("divide-x divide-border", rowProps.className, isOddRow && "bg-foreground/5") : `divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
|
|
2472
2593
|
children: [
|
|
2473
|
-
showDetailColumn && /* @__PURE__ */
|
|
2594
|
+
showDetailColumn && /* @__PURE__ */ jsx28(
|
|
2474
2595
|
"td",
|
|
2475
2596
|
{
|
|
2476
2597
|
className: isOddRow ? detailCellProps.classNameOdd : detailCellProps.className,
|
|
2477
2598
|
style: detailCellProps.style,
|
|
2478
|
-
children: /* @__PURE__ */
|
|
2599
|
+
children: /* @__PURE__ */ jsx28(
|
|
2479
2600
|
Button,
|
|
2480
2601
|
{
|
|
2481
2602
|
type: "button",
|
|
@@ -2487,7 +2608,7 @@ function DataGrid({
|
|
|
2487
2608
|
stopRowClick(event);
|
|
2488
2609
|
toggleRowExpanded(row.id);
|
|
2489
2610
|
},
|
|
2490
|
-
children: /* @__PURE__ */
|
|
2611
|
+
children: /* @__PURE__ */ jsx28(
|
|
2491
2612
|
ChevronRight,
|
|
2492
2613
|
{
|
|
2493
2614
|
className: `h-4 w-4 transition-transform${isExpanded ? " rotate-90" : ""}`,
|
|
@@ -2498,12 +2619,12 @@ function DataGrid({
|
|
|
2498
2619
|
)
|
|
2499
2620
|
}
|
|
2500
2621
|
),
|
|
2501
|
-
showSelectionColumn && /* @__PURE__ */
|
|
2622
|
+
showSelectionColumn && /* @__PURE__ */ jsx28(
|
|
2502
2623
|
"td",
|
|
2503
2624
|
{
|
|
2504
2625
|
className: isOddRow ? selectionCellProps.classNameOdd : selectionCellProps.className,
|
|
2505
2626
|
style: selectionCellProps.style,
|
|
2506
|
-
children: /* @__PURE__ */
|
|
2627
|
+
children: /* @__PURE__ */ jsx28(
|
|
2507
2628
|
"input",
|
|
2508
2629
|
{
|
|
2509
2630
|
type: "checkbox",
|
|
@@ -2517,27 +2638,27 @@ function DataGrid({
|
|
|
2517
2638
|
),
|
|
2518
2639
|
row.getAllCells().map((cell) => {
|
|
2519
2640
|
const cellProps = bodyCellPropsByColumn.get(cell.column.id);
|
|
2520
|
-
return /* @__PURE__ */
|
|
2641
|
+
return /* @__PURE__ */ jsx28("td", { style: cellProps?.style, className: cellProps?.className ?? BODY_TD_BASE_CLASS, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id);
|
|
2521
2642
|
}),
|
|
2522
|
-
showRowActionsColumn && rowActions && /* @__PURE__ */
|
|
2643
|
+
showRowActionsColumn && rowActions && /* @__PURE__ */ jsx28(
|
|
2523
2644
|
"td",
|
|
2524
2645
|
{
|
|
2525
2646
|
className: isOddRow ? rowActionsCellProps.classNameOdd : rowActionsCellProps.className,
|
|
2526
2647
|
style: rowActionsCellProps.style,
|
|
2527
|
-
children: /* @__PURE__ */
|
|
2648
|
+
children: /* @__PURE__ */ jsx28(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
|
|
2528
2649
|
}
|
|
2529
2650
|
)
|
|
2530
2651
|
]
|
|
2531
2652
|
}
|
|
2532
2653
|
),
|
|
2533
|
-
showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */
|
|
2654
|
+
showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx28("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx28("td", { colSpan: totalColumnCount, className: "border-b border-border p-2", children: renderDetail(row.original) }) })
|
|
2534
2655
|
] }, row.id);
|
|
2535
2656
|
}
|
|
2536
2657
|
function renderGroupedBucket(bucket) {
|
|
2537
2658
|
const expanded = isGroupExpanded(bucket.key);
|
|
2538
2659
|
const originalRows = bucket.items.map((row) => row.original);
|
|
2539
|
-
return /* @__PURE__ */
|
|
2540
|
-
/* @__PURE__ */
|
|
2660
|
+
return /* @__PURE__ */ jsxs18(Fragment2, { children: [
|
|
2661
|
+
/* @__PURE__ */ jsx28("tbody", { children: /* @__PURE__ */ jsx28("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx28(
|
|
2541
2662
|
"td",
|
|
2542
2663
|
{
|
|
2543
2664
|
colSpan: totalColumnCount,
|
|
@@ -2546,7 +2667,7 @@ function DataGrid({
|
|
|
2546
2667
|
zebra ? "bg-muted" : "bg-background"
|
|
2547
2668
|
),
|
|
2548
2669
|
style: { top: groupHeaderTop },
|
|
2549
|
-
children: /* @__PURE__ */
|
|
2670
|
+
children: /* @__PURE__ */ jsxs18(
|
|
2550
2671
|
"button",
|
|
2551
2672
|
{
|
|
2552
2673
|
type: "button",
|
|
@@ -2554,7 +2675,7 @@ function DataGrid({
|
|
|
2554
2675
|
onClick: () => toggleGroupExpanded(bucket.key),
|
|
2555
2676
|
"aria-expanded": expanded,
|
|
2556
2677
|
children: [
|
|
2557
|
-
/* @__PURE__ */
|
|
2678
|
+
/* @__PURE__ */ jsx28(
|
|
2558
2679
|
ChevronRight,
|
|
2559
2680
|
{
|
|
2560
2681
|
className: `h-4 w-4 shrink-0 transition-transform${expanded ? " rotate-90" : ""}`,
|
|
@@ -2583,81 +2704,42 @@ function DataGrid({
|
|
|
2583
2704
|
// containers (that one scrolling vertically, this component's own
|
|
2584
2705
|
// `overflow-x-auto` div scrolling horizontally) fighting over which one
|
|
2585
2706
|
// a sticky header/pinned column is actually stuck relative to.
|
|
2586
|
-
/* @__PURE__ */
|
|
2587
|
-
editing && pendingEdits.size > 0 && /* @__PURE__ */
|
|
2588
|
-
|
|
2707
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
|
|
2708
|
+
editing && pendingEdits.size > 0 && /* @__PURE__ */ jsx28(
|
|
2709
|
+
EditingBar,
|
|
2589
2710
|
{
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
" row",
|
|
2596
|
-
pendingEdits.size === 1 ? "" : "s",
|
|
2597
|
-
" changed",
|
|
2598
|
-
hasEditErrors && /* @__PURE__ */ jsxs17("span", { className: "ml-2 text-destructive", children: [
|
|
2599
|
-
"Fix the highlighted errors before saving (",
|
|
2600
|
-
describeErrorRows(editErrors),
|
|
2601
|
-
")."
|
|
2602
|
-
] })
|
|
2603
|
-
] }),
|
|
2604
|
-
/* @__PURE__ */ jsxs17("div", { className: "flex gap-2", children: [
|
|
2605
|
-
/* @__PURE__ */ jsx26(
|
|
2606
|
-
Button,
|
|
2607
|
-
{
|
|
2608
|
-
type: "button",
|
|
2609
|
-
variant: "outline",
|
|
2610
|
-
size: "sm",
|
|
2611
|
-
"data-testid": "datagrid-discard-edits",
|
|
2612
|
-
disabled: editing.saving,
|
|
2613
|
-
onClick: handleDiscardEdits,
|
|
2614
|
-
children: resolveEditLabel(editing.discardLabel, pendingEdits.size, () => "Discard")
|
|
2615
|
-
}
|
|
2616
|
-
),
|
|
2617
|
-
/* @__PURE__ */ jsx26(
|
|
2618
|
-
Button,
|
|
2619
|
-
{
|
|
2620
|
-
type: "button",
|
|
2621
|
-
size: "sm",
|
|
2622
|
-
"data-testid": "datagrid-save-edits",
|
|
2623
|
-
disabled: editing.saving || hasEditErrors,
|
|
2624
|
-
onClick: () => void handleSaveEdits(),
|
|
2625
|
-
children: resolveEditLabel(
|
|
2626
|
-
editing.saveLabel,
|
|
2627
|
-
pendingEdits.size,
|
|
2628
|
-
(count) => `Save ${count} change${count === 1 ? "" : "s"}`
|
|
2629
|
-
)
|
|
2630
|
-
}
|
|
2631
|
-
)
|
|
2632
|
-
] })
|
|
2633
|
-
]
|
|
2711
|
+
editing,
|
|
2712
|
+
pendingRowCount: pendingEdits.size,
|
|
2713
|
+
editErrors,
|
|
2714
|
+
onSave: () => void handleSaveEdits(),
|
|
2715
|
+
onDiscard: handleDiscardEdits
|
|
2634
2716
|
}
|
|
2635
2717
|
),
|
|
2636
|
-
showSelectionColumn && headerActions && /* @__PURE__ */
|
|
2637
|
-
/* @__PURE__ */
|
|
2718
|
+
showSelectionColumn && headerActions && /* @__PURE__ */ jsxs18("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
|
|
2719
|
+
/* @__PURE__ */ jsxs18("span", { children: [
|
|
2638
2720
|
selectedRows.length,
|
|
2639
2721
|
" selected"
|
|
2640
2722
|
] }),
|
|
2641
|
-
/* @__PURE__ */
|
|
2723
|
+
/* @__PURE__ */ jsx28(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
|
|
2642
2724
|
] }),
|
|
2643
|
-
/* @__PURE__ */
|
|
2644
|
-
loading && /* @__PURE__ */
|
|
2725
|
+
/* @__PURE__ */ jsxs18("div", { className: "relative min-h-0 flex-1", children: [
|
|
2726
|
+
loading && /* @__PURE__ */ jsx28(
|
|
2645
2727
|
"div",
|
|
2646
2728
|
{
|
|
2647
2729
|
"data-testid": "datagrid-loading-overlay",
|
|
2648
2730
|
className: "absolute inset-0 z-30 flex items-center justify-center bg-background/60",
|
|
2649
|
-
children: /* @__PURE__ */
|
|
2731
|
+
children: /* @__PURE__ */ jsx28(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
|
|
2650
2732
|
}
|
|
2651
2733
|
),
|
|
2652
|
-
/* @__PURE__ */
|
|
2734
|
+
/* @__PURE__ */ jsx28(
|
|
2653
2735
|
"div",
|
|
2654
2736
|
{
|
|
2655
2737
|
ref: scrollRef,
|
|
2656
2738
|
"data-testid": testId,
|
|
2657
2739
|
className: "h-full overflow-auto rounded-md border",
|
|
2658
2740
|
style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : void 0,
|
|
2659
|
-
children: /* @__PURE__ */
|
|
2660
|
-
/* @__PURE__ */
|
|
2741
|
+
children: /* @__PURE__ */ jsxs18("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
|
|
2742
|
+
/* @__PURE__ */ jsxs18("thead", { ref: theadRef, className: "bg-muted", children: [
|
|
2661
2743
|
table.getHeaderGroups().map((headerGroup) => {
|
|
2662
2744
|
const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));
|
|
2663
2745
|
function renderLeafHeaderCell(column, rowSpan) {
|
|
@@ -2668,9 +2750,9 @@ function DataGrid({
|
|
|
2668
2750
|
const filterable = isFilterable(column) && column.filterDisplay !== "row";
|
|
2669
2751
|
const cellProps = leafHeaderCellPropsByColumn.get(column.id);
|
|
2670
2752
|
const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;
|
|
2671
|
-
return /* @__PURE__ */
|
|
2672
|
-
/* @__PURE__ */
|
|
2673
|
-
sortable ? /* @__PURE__ */
|
|
2753
|
+
return /* @__PURE__ */ jsxs18("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
|
|
2754
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1", children: [
|
|
2755
|
+
sortable ? /* @__PURE__ */ jsxs18(
|
|
2674
2756
|
"button",
|
|
2675
2757
|
{
|
|
2676
2758
|
type: "button",
|
|
@@ -2678,8 +2760,8 @@ function DataGrid({
|
|
|
2678
2760
|
onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
|
|
2679
2761
|
children: [
|
|
2680
2762
|
headerContent,
|
|
2681
|
-
sortEntry?.dir === "asc" && /* @__PURE__ */
|
|
2682
|
-
sortEntry?.dir === "desc" && /* @__PURE__ */
|
|
2763
|
+
sortEntry?.dir === "asc" && /* @__PURE__ */ jsx28(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
|
|
2764
|
+
sortEntry?.dir === "desc" && /* @__PURE__ */ jsx28(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
|
|
2683
2765
|
]
|
|
2684
2766
|
}
|
|
2685
2767
|
) : (
|
|
@@ -2688,10 +2770,10 @@ function DataGrid({
|
|
|
2688
2770
|
// silently breaking any interactive content a column's own `renderHeader`
|
|
2689
2771
|
// embeds directly (bare filter inputs, "select/reject all" buttons) —
|
|
2690
2772
|
// clicks never reach them at all, even though nothing looks wrong visually.
|
|
2691
|
-
/* @__PURE__ */
|
|
2773
|
+
/* @__PURE__ */ jsx28("div", { className: "flex items-center gap-1 px-1", children: headerContent })
|
|
2692
2774
|
),
|
|
2693
|
-
filterable && /* @__PURE__ */
|
|
2694
|
-
/* @__PURE__ */
|
|
2775
|
+
filterable && /* @__PURE__ */ jsxs18(Popover, { children: [
|
|
2776
|
+
/* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx28(
|
|
2695
2777
|
FunnelIcon7,
|
|
2696
2778
|
{
|
|
2697
2779
|
className: cn(
|
|
@@ -2701,10 +2783,10 @@ function DataGrid({
|
|
|
2701
2783
|
"aria-hidden": true
|
|
2702
2784
|
}
|
|
2703
2785
|
) }) }),
|
|
2704
|
-
/* @__PURE__ */
|
|
2786
|
+
/* @__PURE__ */ jsx28(PopoverContent, { children: renderFilterWidget(column) })
|
|
2705
2787
|
] })
|
|
2706
2788
|
] }),
|
|
2707
|
-
enableColumnResizing && /* @__PURE__ */
|
|
2789
|
+
enableColumnResizing && /* @__PURE__ */ jsx28(
|
|
2708
2790
|
"div",
|
|
2709
2791
|
{
|
|
2710
2792
|
"data-testid": `resize-handle-${column.id}`,
|
|
@@ -2715,9 +2797,9 @@ function DataGrid({
|
|
|
2715
2797
|
)
|
|
2716
2798
|
] }, header.id);
|
|
2717
2799
|
}
|
|
2718
|
-
return /* @__PURE__ */
|
|
2719
|
-
hasHeaderGroups && /* @__PURE__ */
|
|
2720
|
-
showDetailColumn && /* @__PURE__ */
|
|
2800
|
+
return /* @__PURE__ */ jsxs18(Fragment2, { children: [
|
|
2801
|
+
hasHeaderGroups && /* @__PURE__ */ jsxs18("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
|
|
2802
|
+
showDetailColumn && /* @__PURE__ */ jsx28(
|
|
2721
2803
|
"th",
|
|
2722
2804
|
{
|
|
2723
2805
|
rowSpan: 2,
|
|
@@ -2726,13 +2808,13 @@ function DataGrid({
|
|
|
2726
2808
|
"aria-hidden": true
|
|
2727
2809
|
}
|
|
2728
2810
|
),
|
|
2729
|
-
showSelectionColumn && /* @__PURE__ */
|
|
2811
|
+
showSelectionColumn && /* @__PURE__ */ jsx28(
|
|
2730
2812
|
"th",
|
|
2731
2813
|
{
|
|
2732
2814
|
rowSpan: 2,
|
|
2733
2815
|
className: selectionHeaderCellProps.className,
|
|
2734
2816
|
style: selectionHeaderCellProps.style,
|
|
2735
|
-
children: /* @__PURE__ */
|
|
2817
|
+
children: /* @__PURE__ */ jsx28(
|
|
2736
2818
|
"input",
|
|
2737
2819
|
{
|
|
2738
2820
|
type: "checkbox",
|
|
@@ -2744,7 +2826,7 @@ function DataGrid({
|
|
|
2744
2826
|
}
|
|
2745
2827
|
),
|
|
2746
2828
|
headerRuns.map(
|
|
2747
|
-
(run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */
|
|
2829
|
+
(run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx28(
|
|
2748
2830
|
"th",
|
|
2749
2831
|
{
|
|
2750
2832
|
colSpan: run.columns.length,
|
|
@@ -2754,7 +2836,7 @@ function DataGrid({
|
|
|
2754
2836
|
`group-${run.label}-${run.columns[0].id}`
|
|
2755
2837
|
)
|
|
2756
2838
|
),
|
|
2757
|
-
showRowActionsColumn && /* @__PURE__ */
|
|
2839
|
+
showRowActionsColumn && /* @__PURE__ */ jsx28(
|
|
2758
2840
|
"th",
|
|
2759
2841
|
{
|
|
2760
2842
|
rowSpan: 2,
|
|
@@ -2764,8 +2846,8 @@ function DataGrid({
|
|
|
2764
2846
|
}
|
|
2765
2847
|
)
|
|
2766
2848
|
] }, `${headerGroup.id}-groups`),
|
|
2767
|
-
/* @__PURE__ */
|
|
2768
|
-
!hasHeaderGroups && showDetailColumn && /* @__PURE__ */
|
|
2849
|
+
/* @__PURE__ */ jsxs18("tr", { className: "divide-x divide-border", children: [
|
|
2850
|
+
!hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx28(
|
|
2769
2851
|
"th",
|
|
2770
2852
|
{
|
|
2771
2853
|
className: detailHeaderCellProps.className,
|
|
@@ -2773,12 +2855,12 @@ function DataGrid({
|
|
|
2773
2855
|
"aria-hidden": true
|
|
2774
2856
|
}
|
|
2775
2857
|
),
|
|
2776
|
-
!hasHeaderGroups && showSelectionColumn && /* @__PURE__ */
|
|
2858
|
+
!hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx28(
|
|
2777
2859
|
"th",
|
|
2778
2860
|
{
|
|
2779
2861
|
className: selectionHeaderCellProps.className,
|
|
2780
2862
|
style: selectionHeaderCellProps.style,
|
|
2781
|
-
children: /* @__PURE__ */
|
|
2863
|
+
children: /* @__PURE__ */ jsx28(
|
|
2782
2864
|
"input",
|
|
2783
2865
|
{
|
|
2784
2866
|
type: "checkbox",
|
|
@@ -2794,7 +2876,7 @@ function DataGrid({
|
|
|
2794
2876
|
if (!column || hasHeaderGroups && !column.headerGroup) return null;
|
|
2795
2877
|
return renderLeafHeaderCell(column);
|
|
2796
2878
|
}),
|
|
2797
|
-
!hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */
|
|
2879
|
+
!hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx28(
|
|
2798
2880
|
"th",
|
|
2799
2881
|
{
|
|
2800
2882
|
className: rowActionsHeaderCellProps.className,
|
|
@@ -2805,9 +2887,9 @@ function DataGrid({
|
|
|
2805
2887
|
] }, headerGroup.id)
|
|
2806
2888
|
] }, headerGroup.id);
|
|
2807
2889
|
}),
|
|
2808
|
-
hasFilterRow && /* @__PURE__ */
|
|
2809
|
-
showDetailColumn && /* @__PURE__ */
|
|
2810
|
-
showSelectionColumn && /* @__PURE__ */
|
|
2890
|
+
hasFilterRow && /* @__PURE__ */ jsxs18("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
|
|
2891
|
+
showDetailColumn && /* @__PURE__ */ jsx28("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
|
|
2892
|
+
showSelectionColumn && /* @__PURE__ */ jsx28(
|
|
2811
2893
|
"th",
|
|
2812
2894
|
{
|
|
2813
2895
|
className: selectionHeaderCellProps.className,
|
|
@@ -2816,9 +2898,9 @@ function DataGrid({
|
|
|
2816
2898
|
),
|
|
2817
2899
|
visibleColumns.map((column) => {
|
|
2818
2900
|
const cellProps = filterHeaderCellPropsByColumn.get(column.id);
|
|
2819
|
-
return /* @__PURE__ */
|
|
2901
|
+
return /* @__PURE__ */ jsx28("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
|
|
2820
2902
|
}),
|
|
2821
|
-
showRowActionsColumn && /* @__PURE__ */
|
|
2903
|
+
showRowActionsColumn && /* @__PURE__ */ jsx28(
|
|
2822
2904
|
"th",
|
|
2823
2905
|
{
|
|
2824
2906
|
className: rowActionsHeaderCellProps.className,
|
|
@@ -2827,19 +2909,19 @@ function DataGrid({
|
|
|
2827
2909
|
)
|
|
2828
2910
|
] })
|
|
2829
2911
|
] }),
|
|
2830
|
-
rows.length === 0 ? /* @__PURE__ */
|
|
2831
|
-
paddingTop > 0 && /* @__PURE__ */
|
|
2912
|
+
rows.length === 0 ? /* @__PURE__ */ jsx28("tbody", { children: /* @__PURE__ */ jsx28("tr", { children: /* @__PURE__ */ jsx28("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? "Loading..." : "No results." }) }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs18(Fragment3, { children: [
|
|
2913
|
+
paddingTop > 0 && /* @__PURE__ */ jsx28("tbody", { children: /* @__PURE__ */ jsx28("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx28("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
|
|
2832
2914
|
virtualItems.map(
|
|
2833
2915
|
(virtualItem) => renderRow(tableRows[virtualItem.index], (el) => virtualizer.measureElement(el))
|
|
2834
2916
|
),
|
|
2835
|
-
paddingBottom > 0 && /* @__PURE__ */
|
|
2917
|
+
paddingBottom > 0 && /* @__PURE__ */ jsx28("tbody", { children: /* @__PURE__ */ jsx28("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx28("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingBottom } }) }) })
|
|
2836
2918
|
] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
|
|
2837
2919
|
] })
|
|
2838
2920
|
}
|
|
2839
2921
|
)
|
|
2840
2922
|
] }),
|
|
2841
|
-
showPagination && /* @__PURE__ */
|
|
2842
|
-
/* @__PURE__ */
|
|
2923
|
+
showPagination && /* @__PURE__ */ jsxs18("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
|
|
2924
|
+
/* @__PURE__ */ jsxs18("span", { children: [
|
|
2843
2925
|
"Page ",
|
|
2844
2926
|
state.page + 1,
|
|
2845
2927
|
" of ",
|
|
@@ -2850,8 +2932,8 @@ function DataGrid({
|
|
|
2850
2932
|
rowCount === 1 ? "" : "s",
|
|
2851
2933
|
")"
|
|
2852
2934
|
] }),
|
|
2853
|
-
/* @__PURE__ */
|
|
2854
|
-
/* @__PURE__ */
|
|
2935
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex gap-2", children: [
|
|
2936
|
+
/* @__PURE__ */ jsx28(
|
|
2855
2937
|
Button,
|
|
2856
2938
|
{
|
|
2857
2939
|
variant: "outline",
|
|
@@ -2861,7 +2943,7 @@ function DataGrid({
|
|
|
2861
2943
|
children: "Previous"
|
|
2862
2944
|
}
|
|
2863
2945
|
),
|
|
2864
|
-
/* @__PURE__ */
|
|
2946
|
+
/* @__PURE__ */ jsx28(
|
|
2865
2947
|
Button,
|
|
2866
2948
|
{
|
|
2867
2949
|
variant: "outline",
|
|
@@ -2880,7 +2962,7 @@ function DataGrid({
|
|
|
2880
2962
|
// src/tree/TreeDataGrid.tsx
|
|
2881
2963
|
import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
|
|
2882
2964
|
import { AlertCircle, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2, Loader2 as Loader22 } from "lucide-react";
|
|
2883
|
-
import { useEffect as
|
|
2965
|
+
import { Fragment as Fragment4, useEffect as useEffect10, useMemo as useMemo9, useRef as useRef9 } from "react";
|
|
2884
2966
|
|
|
2885
2967
|
// src/tree/flattenTree.ts
|
|
2886
2968
|
function flattenTree(roots, accessors, expanded, childrenOverride) {
|
|
@@ -2925,7 +3007,7 @@ function computeSelectionStates(roots, accessors, childrenOverride, selectedIds)
|
|
|
2925
3007
|
}
|
|
2926
3008
|
|
|
2927
3009
|
// src/tree/useTreeState.ts
|
|
2928
|
-
import { useCallback as
|
|
3010
|
+
import { useCallback as useCallback4, useMemo as useMemo8, useState as useState11 } from "react";
|
|
2929
3011
|
function useTreeState({
|
|
2930
3012
|
data,
|
|
2931
3013
|
getRowId,
|
|
@@ -2937,9 +3019,9 @@ function useTreeState({
|
|
|
2937
3019
|
childrenMap: controlledChildrenMap,
|
|
2938
3020
|
onChildrenMapChange
|
|
2939
3021
|
}) {
|
|
2940
|
-
const [internalExpanded, setInternalExpanded] =
|
|
3022
|
+
const [internalExpanded, setInternalExpanded] = useState11({});
|
|
2941
3023
|
const expanded = controlledExpanded ?? internalExpanded;
|
|
2942
|
-
const setExpanded =
|
|
3024
|
+
const setExpanded = useCallback4(
|
|
2943
3025
|
(updater) => {
|
|
2944
3026
|
if (controlledExpanded) {
|
|
2945
3027
|
onExpandedChange?.(updater(controlledExpanded));
|
|
@@ -2949,9 +3031,9 @@ function useTreeState({
|
|
|
2949
3031
|
},
|
|
2950
3032
|
[controlledExpanded, onExpandedChange]
|
|
2951
3033
|
);
|
|
2952
|
-
const [internalChildrenMap, setInternalChildrenMap] =
|
|
3034
|
+
const [internalChildrenMap, setInternalChildrenMap] = useState11(/* @__PURE__ */ new Map());
|
|
2953
3035
|
const childrenMap = controlledChildrenMap ?? internalChildrenMap;
|
|
2954
|
-
const setChildrenMap =
|
|
3036
|
+
const setChildrenMap = useCallback4(
|
|
2955
3037
|
(updater) => {
|
|
2956
3038
|
if (controlledChildrenMap) {
|
|
2957
3039
|
onChildrenMapChange?.(updater(controlledChildrenMap));
|
|
@@ -2961,13 +3043,13 @@ function useTreeState({
|
|
|
2961
3043
|
},
|
|
2962
3044
|
[controlledChildrenMap, onChildrenMapChange]
|
|
2963
3045
|
);
|
|
2964
|
-
const [loadingIds, setLoadingIds] =
|
|
2965
|
-
const [errorIds, setErrorIds] =
|
|
2966
|
-
const resolveHasChildren =
|
|
3046
|
+
const [loadingIds, setLoadingIds] = useState11(/* @__PURE__ */ new Set());
|
|
3047
|
+
const [errorIds, setErrorIds] = useState11(/* @__PURE__ */ new Map());
|
|
3048
|
+
const resolveHasChildren = useMemo8(
|
|
2967
3049
|
() => hasChildren ?? ((row) => (getChildren(row)?.length ?? 0) > 0),
|
|
2968
3050
|
[hasChildren, getChildren]
|
|
2969
3051
|
);
|
|
2970
|
-
const loadChildrenFor =
|
|
3052
|
+
const loadChildrenFor = useCallback4(
|
|
2971
3053
|
async (row) => {
|
|
2972
3054
|
const id = getRowId(row);
|
|
2973
3055
|
const cached = childrenMap.get(id);
|
|
@@ -3000,7 +3082,7 @@ function useTreeState({
|
|
|
3000
3082
|
},
|
|
3001
3083
|
[childrenMap, getChildren, getRowId, onLoadChildren, setChildrenMap]
|
|
3002
3084
|
);
|
|
3003
|
-
const toggleExpand =
|
|
3085
|
+
const toggleExpand = useCallback4(
|
|
3004
3086
|
(row) => {
|
|
3005
3087
|
const id = getRowId(row);
|
|
3006
3088
|
const willExpand = !expanded[id];
|
|
@@ -3012,14 +3094,14 @@ function useTreeState({
|
|
|
3012
3094
|
},
|
|
3013
3095
|
[expanded, getRowId, loadChildrenFor, resolveHasChildren, setExpanded]
|
|
3014
3096
|
);
|
|
3015
|
-
const retry =
|
|
3097
|
+
const retry = useCallback4(
|
|
3016
3098
|
(row) => {
|
|
3017
3099
|
loadChildrenFor(row).catch(() => {
|
|
3018
3100
|
});
|
|
3019
3101
|
},
|
|
3020
3102
|
[loadChildrenFor]
|
|
3021
3103
|
);
|
|
3022
|
-
const expandToLevel =
|
|
3104
|
+
const expandToLevel = useCallback4(
|
|
3023
3105
|
async (level) => {
|
|
3024
3106
|
const nextExpanded = { ...expanded };
|
|
3025
3107
|
async function visit(row, depth) {
|
|
@@ -3042,12 +3124,11 @@ function useTreeState({
|
|
|
3042
3124
|
}
|
|
3043
3125
|
|
|
3044
3126
|
// src/tree/TreeDataGrid.tsx
|
|
3045
|
-
import { Fragment as
|
|
3127
|
+
import { Fragment as Fragment5, jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
3046
3128
|
var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
|
|
3047
3129
|
var UNCHECKED_STATE = { checked: false, indeterminate: false };
|
|
3048
|
-
function renderCell(column, row) {
|
|
3049
|
-
|
|
3050
|
-
return column.cell ? column.cell(value, row) : defaultFormat(column, value);
|
|
3130
|
+
function renderCell(column, row, editingCtx) {
|
|
3131
|
+
return renderEditableCell(column, row, getColumnValue(column, row), editingCtx);
|
|
3051
3132
|
}
|
|
3052
3133
|
function TreeCell({
|
|
3053
3134
|
flatRow,
|
|
@@ -3056,10 +3137,11 @@ function TreeCell({
|
|
|
3056
3137
|
isLoading,
|
|
3057
3138
|
error,
|
|
3058
3139
|
onToggle,
|
|
3059
|
-
onRetry
|
|
3140
|
+
onRetry,
|
|
3141
|
+
editingCtx
|
|
3060
3142
|
}) {
|
|
3061
|
-
return /* @__PURE__ */
|
|
3062
|
-
flatRow.hasChildren ? /* @__PURE__ */
|
|
3143
|
+
return /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
|
|
3144
|
+
flatRow.hasChildren ? /* @__PURE__ */ jsx29(
|
|
3063
3145
|
"button",
|
|
3064
3146
|
{
|
|
3065
3147
|
type: "button",
|
|
@@ -3071,14 +3153,14 @@ function TreeCell({
|
|
|
3071
3153
|
"aria-label": flatRow.isExpanded ? "Collapse" : "Expand",
|
|
3072
3154
|
"aria-expanded": flatRow.isExpanded,
|
|
3073
3155
|
className: "flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50",
|
|
3074
|
-
children: isLoading ? /* @__PURE__ */
|
|
3156
|
+
children: isLoading ? /* @__PURE__ */ jsx29(Loader22, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx29(ChevronDown2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx29(ChevronRight2, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
|
|
3075
3157
|
}
|
|
3076
|
-
) : /* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
3078
|
-
flatRow.isExpanded && error ? /* @__PURE__ */
|
|
3079
|
-
/* @__PURE__ */
|
|
3158
|
+
) : /* @__PURE__ */ jsx29("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
3159
|
+
/* @__PURE__ */ jsx29("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
|
|
3160
|
+
flatRow.isExpanded && error ? /* @__PURE__ */ jsxs19("span", { className: "ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive", children: [
|
|
3161
|
+
/* @__PURE__ */ jsx29(AlertCircle, { className: "h-3 w-3", "aria-hidden": "true" }),
|
|
3080
3162
|
"Failed to load.",
|
|
3081
|
-
/* @__PURE__ */
|
|
3163
|
+
/* @__PURE__ */ jsx29("button", { type: "button", className: "underline underline-offset-2", onClick: onRetry, children: "Retry" })
|
|
3082
3164
|
] }) : null
|
|
3083
3165
|
] });
|
|
3084
3166
|
}
|
|
@@ -3086,6 +3168,7 @@ function TreeDataGrid({
|
|
|
3086
3168
|
columns,
|
|
3087
3169
|
data,
|
|
3088
3170
|
treeColumnId,
|
|
3171
|
+
columnVisibility,
|
|
3089
3172
|
getRowId,
|
|
3090
3173
|
getChildren,
|
|
3091
3174
|
hasChildren,
|
|
@@ -3106,12 +3189,19 @@ function TreeDataGrid({
|
|
|
3106
3189
|
onSelectedIdsChange,
|
|
3107
3190
|
getRowSelectionState,
|
|
3108
3191
|
isRowSelectionDisabled,
|
|
3109
|
-
|
|
3192
|
+
groupBy,
|
|
3193
|
+
renderGroupHeader,
|
|
3194
|
+
defaultGroupsExpanded = true,
|
|
3195
|
+
expandedGroups: controlledExpandedGroups,
|
|
3196
|
+
onExpandedGroupsChange,
|
|
3197
|
+
zebra = true,
|
|
3198
|
+
editing
|
|
3110
3199
|
}) {
|
|
3111
|
-
const accessors =
|
|
3200
|
+
const accessors = useMemo9(
|
|
3112
3201
|
() => ({ getRowId, getChildren, hasChildren }),
|
|
3113
3202
|
[getRowId, getChildren, hasChildren]
|
|
3114
3203
|
);
|
|
3204
|
+
const editingState = useEditingState(editing, getRowId);
|
|
3115
3205
|
const { expanded, childrenMap, loadingIds, errorIds, toggleExpand, retry, expandToLevel } = useTreeState({
|
|
3116
3206
|
data,
|
|
3117
3207
|
getRowId,
|
|
@@ -3123,21 +3213,48 @@ function TreeDataGrid({
|
|
|
3123
3213
|
childrenMap: controlledChildrenMap,
|
|
3124
3214
|
onChildrenMapChange
|
|
3125
3215
|
});
|
|
3126
|
-
const didAutoExpand =
|
|
3127
|
-
|
|
3216
|
+
const didAutoExpand = useRef9(false);
|
|
3217
|
+
useEffect10(() => {
|
|
3128
3218
|
if (didAutoExpand.current) return;
|
|
3129
3219
|
didAutoExpand.current = true;
|
|
3130
3220
|
if (initialExpandedLevel > 0) void expandToLevel(initialExpandedLevel);
|
|
3131
3221
|
}, []);
|
|
3132
|
-
const
|
|
3133
|
-
|
|
3134
|
-
|
|
3222
|
+
const visibleColumns = useVisibleColumns(columns, columnVisibility);
|
|
3223
|
+
const { isGroupExpanded, toggleGroupExpanded } = useGroupExpansion(
|
|
3224
|
+
defaultGroupsExpanded,
|
|
3225
|
+
controlledExpandedGroups,
|
|
3226
|
+
onExpandedGroupsChange
|
|
3135
3227
|
);
|
|
3228
|
+
const flatRows = useMemo9(
|
|
3229
|
+
() => groupBy ? [] : flattenTree(data, accessors, expanded, childrenMap),
|
|
3230
|
+
[groupBy, data, accessors, expanded, childrenMap]
|
|
3231
|
+
);
|
|
3232
|
+
const groupedBuckets = useMemo9(
|
|
3233
|
+
() => groupBy ? groupRows(data, groupBy).map((bucket) => ({
|
|
3234
|
+
key: bucket.key,
|
|
3235
|
+
roots: bucket.items,
|
|
3236
|
+
flatRows: flattenTree(bucket.items, accessors, expanded, childrenMap)
|
|
3237
|
+
})) : void 0,
|
|
3238
|
+
[groupBy, data, accessors, expanded, childrenMap]
|
|
3239
|
+
);
|
|
3240
|
+
const groupedRender = useMemo9(() => {
|
|
3241
|
+
if (!groupedBuckets) return void 0;
|
|
3242
|
+
const startIndexByBucket = /* @__PURE__ */ new Map();
|
|
3243
|
+
const rows = [];
|
|
3244
|
+
for (const bucket of groupedBuckets) {
|
|
3245
|
+
if (!isGroupExpanded(bucket.key)) continue;
|
|
3246
|
+
startIndexByBucket.set(bucket.key, rows.length);
|
|
3247
|
+
rows.push(...bucket.flatRows);
|
|
3248
|
+
}
|
|
3249
|
+
return { startIndexByBucket, rows };
|
|
3250
|
+
}, [groupedBuckets, isGroupExpanded]);
|
|
3251
|
+
const allFlatRows = groupedRender ? groupedRender.rows : flatRows;
|
|
3136
3252
|
const treeColId = treeColumnId ?? columns[0]?.id;
|
|
3137
3253
|
const showRowActionsColumn = Boolean(rowActions?.length);
|
|
3138
3254
|
const showSelectionColumn = controlledSelectedIds !== void 0;
|
|
3139
|
-
const totalColumnCount =
|
|
3140
|
-
const
|
|
3255
|
+
const totalColumnCount = visibleColumns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);
|
|
3256
|
+
const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));
|
|
3257
|
+
const selectionStates = useMemo9(
|
|
3141
3258
|
() => showSelectionColumn ? computeSelectionStates(data, accessors, childrenMap, controlledSelectedIds ?? EMPTY_SELECTED_IDS) : void 0,
|
|
3142
3259
|
[showSelectionColumn, data, accessors, childrenMap, controlledSelectedIds]
|
|
3143
3260
|
);
|
|
@@ -3153,9 +3270,9 @@ function TreeDataGrid({
|
|
|
3153
3270
|
else next.add(id);
|
|
3154
3271
|
onSelectedIdsChange?.(next);
|
|
3155
3272
|
}
|
|
3156
|
-
const selectableFlatRows =
|
|
3157
|
-
() =>
|
|
3158
|
-
[
|
|
3273
|
+
const selectableFlatRows = useMemo9(
|
|
3274
|
+
() => allFlatRows.filter((flatRow) => !(isRowSelectionDisabled?.(flatRow.row) ?? false)),
|
|
3275
|
+
[allFlatRows, isRowSelectionDisabled]
|
|
3159
3276
|
);
|
|
3160
3277
|
const allVisibleSelected = showSelectionColumn && selectableFlatRows.length > 0 && selectableFlatRows.every((flatRow) => controlledSelectedIds.has(flatRow.id));
|
|
3161
3278
|
function toggleAllVisibleSelected() {
|
|
@@ -3167,13 +3284,13 @@ function TreeDataGrid({
|
|
|
3167
3284
|
}
|
|
3168
3285
|
onSelectedIdsChange?.(next);
|
|
3169
3286
|
}
|
|
3170
|
-
const bodyTdClassByColumn =
|
|
3287
|
+
const bodyTdClassByColumn = useMemo9(() => {
|
|
3171
3288
|
const map = /* @__PURE__ */ new Map();
|
|
3172
|
-
for (const column of
|
|
3289
|
+
for (const column of visibleColumns) map.set(column.id, `p-2 ${alignClassName(column)}`);
|
|
3173
3290
|
return map;
|
|
3174
|
-
}, [
|
|
3175
|
-
const scrollRef =
|
|
3176
|
-
const shouldVirtualize = flatRows.length > virtualizeThreshold;
|
|
3291
|
+
}, [visibleColumns]);
|
|
3292
|
+
const scrollRef = useRef9(null);
|
|
3293
|
+
const shouldVirtualize = !groupBy && flatRows.length > virtualizeThreshold;
|
|
3177
3294
|
const virtualizer = useVirtualizer2({
|
|
3178
3295
|
count: flatRows.length,
|
|
3179
3296
|
getScrollElement: () => scrollRef.current,
|
|
@@ -3185,7 +3302,7 @@ function TreeDataGrid({
|
|
|
3185
3302
|
const rowProps = getRowProps?.(flatRow.row, flatRow.depth, index);
|
|
3186
3303
|
const rowTestId = getRowTestId?.(flatRow.row) ?? `tree-row-${flatRow.id}`;
|
|
3187
3304
|
const isOddRow = zebra && index % 2 === 1;
|
|
3188
|
-
return /* @__PURE__ */
|
|
3305
|
+
return /* @__PURE__ */ jsxs19(
|
|
3189
3306
|
"tr",
|
|
3190
3307
|
{
|
|
3191
3308
|
ref: measureRef,
|
|
@@ -3198,9 +3315,9 @@ function TreeDataGrid({
|
|
|
3198
3315
|
isOddRow && "bg-foreground/5"
|
|
3199
3316
|
) : `border-b border-border divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
|
|
3200
3317
|
children: [
|
|
3201
|
-
showSelectionColumn && /* @__PURE__ */
|
|
3318
|
+
showSelectionColumn && /* @__PURE__ */ jsx29("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
|
|
3202
3319
|
const state = resolveSelectionState(flatRow.row, flatRow.id);
|
|
3203
|
-
return /* @__PURE__ */
|
|
3320
|
+
return /* @__PURE__ */ jsx29(
|
|
3204
3321
|
Checkbox,
|
|
3205
3322
|
{
|
|
3206
3323
|
"aria-label": `Select row ${flatRow.id}`,
|
|
@@ -3211,7 +3328,7 @@ function TreeDataGrid({
|
|
|
3211
3328
|
}
|
|
3212
3329
|
);
|
|
3213
3330
|
})() }),
|
|
3214
|
-
|
|
3331
|
+
visibleColumns.map((column) => /* @__PURE__ */ jsx29("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx29(
|
|
3215
3332
|
TreeCell,
|
|
3216
3333
|
{
|
|
3217
3334
|
flatRow,
|
|
@@ -3220,10 +3337,11 @@ function TreeDataGrid({
|
|
|
3220
3337
|
isLoading: loadingIds.has(flatRow.id),
|
|
3221
3338
|
error: errorIds.get(flatRow.id),
|
|
3222
3339
|
onToggle: () => toggleExpand(flatRow.row),
|
|
3223
|
-
onRetry: () => retry(flatRow.row)
|
|
3340
|
+
onRetry: () => retry(flatRow.row),
|
|
3341
|
+
editingCtx: editingState.ctx
|
|
3224
3342
|
}
|
|
3225
|
-
) : renderCell(column, flatRow.row) }, column.id)),
|
|
3226
|
-
showRowActionsColumn && rowActions && /* @__PURE__ */
|
|
3343
|
+
) : renderCell(column, flatRow.row, editingState.ctx) }, column.id)),
|
|
3344
|
+
showRowActionsColumn && rowActions && /* @__PURE__ */ jsx29("td", { className: "p-2", children: /* @__PURE__ */ jsx29(
|
|
3227
3345
|
ActionsMenu,
|
|
3228
3346
|
{
|
|
3229
3347
|
items: rowActions,
|
|
@@ -3239,49 +3357,100 @@ function TreeDataGrid({
|
|
|
3239
3357
|
const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];
|
|
3240
3358
|
const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
|
|
3241
3359
|
const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
|
|
3242
|
-
return /* @__PURE__ */
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
}
|
|
3280
|
-
) })
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3360
|
+
return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: [
|
|
3361
|
+
editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx29(
|
|
3362
|
+
EditingBar,
|
|
3363
|
+
{
|
|
3364
|
+
editing,
|
|
3365
|
+
pendingRowCount: editingState.pendingEdits.size,
|
|
3366
|
+
editErrors: editingState.editErrors,
|
|
3367
|
+
onSave: () => void editingState.handleSaveEdits(),
|
|
3368
|
+
onDiscard: editingState.handleDiscardEdits,
|
|
3369
|
+
testIdPrefix: "tree-datagrid"
|
|
3370
|
+
}
|
|
3371
|
+
),
|
|
3372
|
+
/* @__PURE__ */ jsx29(
|
|
3373
|
+
"div",
|
|
3374
|
+
{
|
|
3375
|
+
ref: scrollRef,
|
|
3376
|
+
className: shouldVirtualize ? "overflow-y-auto" : void 0,
|
|
3377
|
+
style: shouldVirtualize ? { maxHeight: maxBodyHeight } : void 0,
|
|
3378
|
+
children: /* @__PURE__ */ jsxs19("table", { className: "w-full border-collapse text-sm", children: [
|
|
3379
|
+
/* @__PURE__ */ jsx29("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs19("tr", { className: "divide-x divide-border", children: [
|
|
3380
|
+
showSelectionColumn && /* @__PURE__ */ jsx29("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx29(
|
|
3381
|
+
Checkbox,
|
|
3382
|
+
{
|
|
3383
|
+
"aria-label": "Select all visible rows",
|
|
3384
|
+
checked: allVisibleSelected,
|
|
3385
|
+
onCheckedChange: toggleAllVisibleSelected
|
|
3386
|
+
}
|
|
3387
|
+
) }),
|
|
3388
|
+
visibleColumns.map((column) => /* @__PURE__ */ jsx29(
|
|
3389
|
+
"th",
|
|
3390
|
+
{
|
|
3391
|
+
style: column.width ? { width: column.width } : void 0,
|
|
3392
|
+
className: `border-b border-border p-2 font-medium ${alignClassName(column)}`,
|
|
3393
|
+
children: column.header
|
|
3394
|
+
},
|
|
3395
|
+
column.id
|
|
3396
|
+
)),
|
|
3397
|
+
showRowActionsColumn && /* @__PURE__ */ jsx29("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
|
|
3398
|
+
] }) }),
|
|
3399
|
+
/* @__PURE__ */ jsx29("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx29("tr", { children: /* @__PURE__ */ jsx29("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: "No results." }) }) : groupedBuckets ? groupedBuckets.map((bucket) => {
|
|
3400
|
+
const groupExpanded = isGroupExpanded(bucket.key);
|
|
3401
|
+
return /* @__PURE__ */ jsxs19(Fragment4, { children: [
|
|
3402
|
+
/* @__PURE__ */ jsx29("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx29(
|
|
3403
|
+
"td",
|
|
3404
|
+
{
|
|
3405
|
+
colSpan: totalColumnCount,
|
|
3406
|
+
className: cn(
|
|
3407
|
+
"sticky z-20 border-b p-2 font-medium",
|
|
3408
|
+
zebra ? "bg-muted" : "bg-background"
|
|
3409
|
+
),
|
|
3410
|
+
style: { top: groupHeaderTop },
|
|
3411
|
+
children: /* @__PURE__ */ jsxs19(
|
|
3412
|
+
"button",
|
|
3413
|
+
{
|
|
3414
|
+
type: "button",
|
|
3415
|
+
className: "flex w-full items-center gap-1 text-left",
|
|
3416
|
+
onClick: () => toggleGroupExpanded(bucket.key),
|
|
3417
|
+
"aria-expanded": groupExpanded,
|
|
3418
|
+
children: [
|
|
3419
|
+
/* @__PURE__ */ jsx29(
|
|
3420
|
+
ChevronRight2,
|
|
3421
|
+
{
|
|
3422
|
+
className: `h-4 w-4 shrink-0 transition-transform${groupExpanded ? " rotate-90" : ""}`,
|
|
3423
|
+
"aria-hidden": true
|
|
3424
|
+
}
|
|
3425
|
+
),
|
|
3426
|
+
renderGroupHeader ? renderGroupHeader(bucket.key, bucket.roots, groupExpanded) : `${bucket.key} (${bucket.roots.length})`
|
|
3427
|
+
]
|
|
3428
|
+
}
|
|
3429
|
+
)
|
|
3430
|
+
}
|
|
3431
|
+
) }),
|
|
3432
|
+
groupExpanded && (() => {
|
|
3433
|
+
const startIndex = groupedRender?.startIndexByBucket.get(bucket.key) ?? 0;
|
|
3434
|
+
return bucket.flatRows.map((flatRow, i) => renderRow(flatRow, startIndex + i));
|
|
3435
|
+
})()
|
|
3436
|
+
] }, `group-${bucket.key}`);
|
|
3437
|
+
}) : shouldVirtualize ? /* @__PURE__ */ jsxs19(Fragment5, { children: [
|
|
3438
|
+
paddingTop > 0 && /* @__PURE__ */ jsx29("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx29("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
|
|
3439
|
+
virtualItems.map(
|
|
3440
|
+
(virtualItem) => renderRow(flatRows[virtualItem.index], virtualItem.index, (el) => virtualizer.measureElement(el))
|
|
3441
|
+
),
|
|
3442
|
+
paddingBottom > 0 && /* @__PURE__ */ jsx29("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx29(
|
|
3443
|
+
"td",
|
|
3444
|
+
{
|
|
3445
|
+
colSpan: totalColumnCount,
|
|
3446
|
+
style: { padding: 0, border: "none", height: paddingBottom }
|
|
3447
|
+
}
|
|
3448
|
+
) })
|
|
3449
|
+
] }) : flatRows.map((flatRow, index) => renderRow(flatRow, index)) })
|
|
3450
|
+
] })
|
|
3451
|
+
}
|
|
3452
|
+
)
|
|
3453
|
+
] });
|
|
3285
3454
|
}
|
|
3286
3455
|
export {
|
|
3287
3456
|
ActionsMenu,
|
|
@@ -3292,6 +3461,7 @@ export {
|
|
|
3292
3461
|
DateEditor,
|
|
3293
3462
|
DateRangeFilter,
|
|
3294
3463
|
EditFieldError,
|
|
3464
|
+
EditingBar,
|
|
3295
3465
|
EnumEditor,
|
|
3296
3466
|
EnumFilter,
|
|
3297
3467
|
NumberComparisonFilter,
|
|
@@ -3324,11 +3494,16 @@ export {
|
|
|
3324
3494
|
processClientData,
|
|
3325
3495
|
renderDefaultEditWidget,
|
|
3326
3496
|
renderDefaultFilterWidget,
|
|
3497
|
+
renderEditableCell,
|
|
3327
3498
|
resolveMenuItems,
|
|
3328
3499
|
storageKeyFor,
|
|
3329
3500
|
toDate,
|
|
3330
3501
|
useDebouncedCallback,
|
|
3502
|
+
useEditingState,
|
|
3331
3503
|
useGridState,
|
|
3332
|
-
|
|
3504
|
+
useGroupExpansion,
|
|
3505
|
+
useStickyGroupHeaderTop,
|
|
3506
|
+
useTreeState,
|
|
3507
|
+
useVisibleColumns
|
|
3333
3508
|
};
|
|
3334
3509
|
//# sourceMappingURL=index.js.map
|