@bmsuisse/datagrid 0.26.0 → 0.27.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 +189 -59
- package/dist/index.js +459 -389
- 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,7 @@ 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, useLayoutEffect, useMemo as useMemo6, useRef as useRef7, useState as useState8 } from "react";
|
|
1836
2080
|
|
|
1837
2081
|
// src/lib/structuralColumns.ts
|
|
1838
2082
|
var SELECTION_COLUMN_WIDTH = 44;
|
|
@@ -1875,15 +2119,15 @@ function processClientData(data, state) {
|
|
|
1875
2119
|
}
|
|
1876
2120
|
|
|
1877
2121
|
// src/grid/useGridState.ts
|
|
1878
|
-
import { useCallback as useCallback2, useEffect as
|
|
2122
|
+
import { useCallback as useCallback2, useEffect as useEffect8, useMemo as useMemo5, useState as useState7 } from "react";
|
|
1879
2123
|
|
|
1880
2124
|
// src/hooks/useDebouncedCallback.ts
|
|
1881
|
-
import { useEffect as
|
|
2125
|
+
import { useEffect as useEffect7, useMemo as useMemo4, useRef as useRef6 } from "react";
|
|
1882
2126
|
function useDebouncedCallback(callback, delayMs) {
|
|
1883
|
-
const callbackRef =
|
|
2127
|
+
const callbackRef = useRef6(callback);
|
|
1884
2128
|
callbackRef.current = callback;
|
|
1885
|
-
const timeoutRef =
|
|
1886
|
-
|
|
2129
|
+
const timeoutRef = useRef6(void 0);
|
|
2130
|
+
useEffect7(() => {
|
|
1887
2131
|
return () => {
|
|
1888
2132
|
clearTimeout(timeoutRef.current);
|
|
1889
2133
|
};
|
|
@@ -1913,7 +2157,7 @@ function nextSortDir(current, descFirst) {
|
|
|
1913
2157
|
return void 0;
|
|
1914
2158
|
}
|
|
1915
2159
|
function useGridState(dataSource, initialState, externalState) {
|
|
1916
|
-
const [state, setState] =
|
|
2160
|
+
const [state, setState] = useState7(
|
|
1917
2161
|
() => externalState ?? { ...DEFAULT_STATE, ...initialState }
|
|
1918
2162
|
);
|
|
1919
2163
|
const notifyRaw = useCallback2(
|
|
@@ -1933,7 +2177,7 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
1933
2177
|
},
|
|
1934
2178
|
[notifyRaw, cancelDebouncedNotify]
|
|
1935
2179
|
);
|
|
1936
|
-
|
|
2180
|
+
useEffect8(() => {
|
|
1937
2181
|
if (!externalState) return;
|
|
1938
2182
|
cancelDebouncedNotify();
|
|
1939
2183
|
setState(externalState);
|
|
@@ -2002,7 +2246,7 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
2002
2246
|
}
|
|
2003
2247
|
|
|
2004
2248
|
// src/grid/DataGrid.tsx
|
|
2005
|
-
import { Fragment as Fragment3, jsx as
|
|
2249
|
+
import { Fragment as Fragment3, jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2006
2250
|
var gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });
|
|
2007
2251
|
var DEFAULT_COLUMN_SIZE = 150;
|
|
2008
2252
|
var DETAIL_COLUMN_WIDTH = 44;
|
|
@@ -2039,71 +2283,9 @@ function toTanstackColumns(columns, editingCtxRef) {
|
|
|
2039
2283
|
header: column.header,
|
|
2040
2284
|
size: column.width,
|
|
2041
2285
|
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
|
-
}
|
|
2286
|
+
cell: (info) => renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current)
|
|
2091
2287
|
}));
|
|
2092
2288
|
}
|
|
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
2289
|
function DataGrid({
|
|
2108
2290
|
columns,
|
|
2109
2291
|
dataSource,
|
|
@@ -2140,19 +2322,19 @@ function DataGrid({
|
|
|
2140
2322
|
initialState,
|
|
2141
2323
|
gridState
|
|
2142
2324
|
);
|
|
2143
|
-
const [internalSelectedIds, setInternalSelectedIds] =
|
|
2325
|
+
const [internalSelectedIds, setInternalSelectedIds] = useState8(/* @__PURE__ */ new Set());
|
|
2144
2326
|
const selectedIds = controlledSelectedIds ?? internalSelectedIds;
|
|
2145
2327
|
function updateSelectedIds(next) {
|
|
2146
2328
|
setInternalSelectedIds(next);
|
|
2147
2329
|
onSelectedIdsChange?.(next);
|
|
2148
2330
|
}
|
|
2149
|
-
const [internalColumnSizing, setInternalColumnSizing] =
|
|
2331
|
+
const [internalColumnSizing, setInternalColumnSizing] = useState8({});
|
|
2150
2332
|
const columnSizing = controlledColumnSizing ?? internalColumnSizing;
|
|
2151
2333
|
function updateColumnSizing(next) {
|
|
2152
2334
|
setInternalColumnSizing(next);
|
|
2153
2335
|
onColumnSizingChange?.(next);
|
|
2154
2336
|
}
|
|
2155
|
-
const [expandedIds, setExpandedIds] =
|
|
2337
|
+
const [expandedIds, setExpandedIds] = useState8(/* @__PURE__ */ new Set());
|
|
2156
2338
|
function toggleRowExpanded(id) {
|
|
2157
2339
|
setExpandedIds((prev) => {
|
|
2158
2340
|
const next = new Set(prev);
|
|
@@ -2161,7 +2343,7 @@ function DataGrid({
|
|
|
2161
2343
|
return next;
|
|
2162
2344
|
});
|
|
2163
2345
|
}
|
|
2164
|
-
const [internalExpandedGroups, setInternalExpandedGroups] =
|
|
2346
|
+
const [internalExpandedGroups, setInternalExpandedGroups] = useState8({});
|
|
2165
2347
|
const expandedGroupsState = controlledExpandedGroups ?? internalExpandedGroups;
|
|
2166
2348
|
function updateExpandedGroups(next) {
|
|
2167
2349
|
setInternalExpandedGroups(next);
|
|
@@ -2173,102 +2355,10 @@ function DataGrid({
|
|
|
2173
2355
|
function toggleGroupExpanded(key) {
|
|
2174
2356
|
updateExpandedGroups({ ...expandedGroupsState, [key]: !isGroupExpanded(key) });
|
|
2175
2357
|
}
|
|
2176
|
-
const
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
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;
|
|
2358
|
+
const { pendingEdits, editErrors, ctxRef: editingCtxRef, handleSaveEdits, handleDiscardEdits } = useEditingState(
|
|
2359
|
+
editing,
|
|
2360
|
+
getRowId
|
|
2361
|
+
);
|
|
2272
2362
|
const visibleColumns = useMemo6(
|
|
2273
2363
|
() => columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,
|
|
2274
2364
|
[columns, columnVisibility]
|
|
@@ -2424,8 +2514,8 @@ function DataGrid({
|
|
|
2424
2514
|
() => groupBy ? groupRows(tableRows, (row) => groupBy(row.original)) : void 0,
|
|
2425
2515
|
[tableRows, groupBy]
|
|
2426
2516
|
);
|
|
2427
|
-
const theadRef =
|
|
2428
|
-
const [groupHeaderTop, setGroupHeaderTop] =
|
|
2517
|
+
const theadRef = useRef7(null);
|
|
2518
|
+
const [groupHeaderTop, setGroupHeaderTop] = useState8(0);
|
|
2429
2519
|
useLayoutEffect(() => {
|
|
2430
2520
|
if (!groupBy) return;
|
|
2431
2521
|
const theadEl = theadRef.current;
|
|
@@ -2436,7 +2526,7 @@ function DataGrid({
|
|
|
2436
2526
|
observer.observe(theadEl);
|
|
2437
2527
|
return () => observer.disconnect();
|
|
2438
2528
|
}, [groupBy]);
|
|
2439
|
-
const scrollRef =
|
|
2529
|
+
const scrollRef = useRef7(null);
|
|
2440
2530
|
const virtualizer = useVirtualizer({
|
|
2441
2531
|
count: tableRows.length,
|
|
2442
2532
|
getScrollElement: () => scrollRef.current,
|
|
@@ -2448,8 +2538,8 @@ function DataGrid({
|
|
|
2448
2538
|
const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
|
|
2449
2539
|
const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
|
|
2450
2540
|
const lastVisibleIndex = virtualItems.length > 0 ? virtualItems[virtualItems.length - 1].index : -1;
|
|
2451
|
-
const notifiedForLengthRef =
|
|
2452
|
-
|
|
2541
|
+
const notifiedForLengthRef = useRef7(-1);
|
|
2542
|
+
useEffect9(() => {
|
|
2453
2543
|
if (!virtualize?.onEndReached) return;
|
|
2454
2544
|
if (lastVisibleIndex < 0 || lastVisibleIndex < tableRows.length - 1) return;
|
|
2455
2545
|
if (virtualize.hasMore === false) return;
|
|
@@ -2462,20 +2552,20 @@ function DataGrid({
|
|
|
2462
2552
|
const rowProps = getRowProps?.(row.original);
|
|
2463
2553
|
const rowTestId = getRowTestId?.(row.original) ?? `row-${row.id}`;
|
|
2464
2554
|
const isOddRow = zebra && row.index % 2 === 1;
|
|
2465
|
-
return /* @__PURE__ */
|
|
2466
|
-
/* @__PURE__ */
|
|
2555
|
+
return /* @__PURE__ */ jsxs18("tbody", { ref: measureRef, "data-index": row.index, children: [
|
|
2556
|
+
/* @__PURE__ */ jsxs18(
|
|
2467
2557
|
"tr",
|
|
2468
2558
|
{
|
|
2469
2559
|
...rowProps,
|
|
2470
2560
|
"data-testid": rowTestId,
|
|
2471
2561
|
className: rowProps?.className ? cn("divide-x divide-border", rowProps.className, isOddRow && "bg-foreground/5") : `divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
|
|
2472
2562
|
children: [
|
|
2473
|
-
showDetailColumn && /* @__PURE__ */
|
|
2563
|
+
showDetailColumn && /* @__PURE__ */ jsx28(
|
|
2474
2564
|
"td",
|
|
2475
2565
|
{
|
|
2476
2566
|
className: isOddRow ? detailCellProps.classNameOdd : detailCellProps.className,
|
|
2477
2567
|
style: detailCellProps.style,
|
|
2478
|
-
children: /* @__PURE__ */
|
|
2568
|
+
children: /* @__PURE__ */ jsx28(
|
|
2479
2569
|
Button,
|
|
2480
2570
|
{
|
|
2481
2571
|
type: "button",
|
|
@@ -2487,7 +2577,7 @@ function DataGrid({
|
|
|
2487
2577
|
stopRowClick(event);
|
|
2488
2578
|
toggleRowExpanded(row.id);
|
|
2489
2579
|
},
|
|
2490
|
-
children: /* @__PURE__ */
|
|
2580
|
+
children: /* @__PURE__ */ jsx28(
|
|
2491
2581
|
ChevronRight,
|
|
2492
2582
|
{
|
|
2493
2583
|
className: `h-4 w-4 transition-transform${isExpanded ? " rotate-90" : ""}`,
|
|
@@ -2498,12 +2588,12 @@ function DataGrid({
|
|
|
2498
2588
|
)
|
|
2499
2589
|
}
|
|
2500
2590
|
),
|
|
2501
|
-
showSelectionColumn && /* @__PURE__ */
|
|
2591
|
+
showSelectionColumn && /* @__PURE__ */ jsx28(
|
|
2502
2592
|
"td",
|
|
2503
2593
|
{
|
|
2504
2594
|
className: isOddRow ? selectionCellProps.classNameOdd : selectionCellProps.className,
|
|
2505
2595
|
style: selectionCellProps.style,
|
|
2506
|
-
children: /* @__PURE__ */
|
|
2596
|
+
children: /* @__PURE__ */ jsx28(
|
|
2507
2597
|
"input",
|
|
2508
2598
|
{
|
|
2509
2599
|
type: "checkbox",
|
|
@@ -2517,27 +2607,27 @@ function DataGrid({
|
|
|
2517
2607
|
),
|
|
2518
2608
|
row.getAllCells().map((cell) => {
|
|
2519
2609
|
const cellProps = bodyCellPropsByColumn.get(cell.column.id);
|
|
2520
|
-
return /* @__PURE__ */
|
|
2610
|
+
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
2611
|
}),
|
|
2522
|
-
showRowActionsColumn && rowActions && /* @__PURE__ */
|
|
2612
|
+
showRowActionsColumn && rowActions && /* @__PURE__ */ jsx28(
|
|
2523
2613
|
"td",
|
|
2524
2614
|
{
|
|
2525
2615
|
className: isOddRow ? rowActionsCellProps.classNameOdd : rowActionsCellProps.className,
|
|
2526
2616
|
style: rowActionsCellProps.style,
|
|
2527
|
-
children: /* @__PURE__ */
|
|
2617
|
+
children: /* @__PURE__ */ jsx28(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
|
|
2528
2618
|
}
|
|
2529
2619
|
)
|
|
2530
2620
|
]
|
|
2531
2621
|
}
|
|
2532
2622
|
),
|
|
2533
|
-
showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */
|
|
2623
|
+
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
2624
|
] }, row.id);
|
|
2535
2625
|
}
|
|
2536
2626
|
function renderGroupedBucket(bucket) {
|
|
2537
2627
|
const expanded = isGroupExpanded(bucket.key);
|
|
2538
2628
|
const originalRows = bucket.items.map((row) => row.original);
|
|
2539
|
-
return /* @__PURE__ */
|
|
2540
|
-
/* @__PURE__ */
|
|
2629
|
+
return /* @__PURE__ */ jsxs18(Fragment2, { children: [
|
|
2630
|
+
/* @__PURE__ */ jsx28("tbody", { children: /* @__PURE__ */ jsx28("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx28(
|
|
2541
2631
|
"td",
|
|
2542
2632
|
{
|
|
2543
2633
|
colSpan: totalColumnCount,
|
|
@@ -2546,7 +2636,7 @@ function DataGrid({
|
|
|
2546
2636
|
zebra ? "bg-muted" : "bg-background"
|
|
2547
2637
|
),
|
|
2548
2638
|
style: { top: groupHeaderTop },
|
|
2549
|
-
children: /* @__PURE__ */
|
|
2639
|
+
children: /* @__PURE__ */ jsxs18(
|
|
2550
2640
|
"button",
|
|
2551
2641
|
{
|
|
2552
2642
|
type: "button",
|
|
@@ -2554,7 +2644,7 @@ function DataGrid({
|
|
|
2554
2644
|
onClick: () => toggleGroupExpanded(bucket.key),
|
|
2555
2645
|
"aria-expanded": expanded,
|
|
2556
2646
|
children: [
|
|
2557
|
-
/* @__PURE__ */
|
|
2647
|
+
/* @__PURE__ */ jsx28(
|
|
2558
2648
|
ChevronRight,
|
|
2559
2649
|
{
|
|
2560
2650
|
className: `h-4 w-4 shrink-0 transition-transform${expanded ? " rotate-90" : ""}`,
|
|
@@ -2583,81 +2673,42 @@ function DataGrid({
|
|
|
2583
2673
|
// containers (that one scrolling vertically, this component's own
|
|
2584
2674
|
// `overflow-x-auto` div scrolling horizontally) fighting over which one
|
|
2585
2675
|
// a sticky header/pinned column is actually stuck relative to.
|
|
2586
|
-
/* @__PURE__ */
|
|
2587
|
-
editing && pendingEdits.size > 0 && /* @__PURE__ */
|
|
2588
|
-
|
|
2676
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
|
|
2677
|
+
editing && pendingEdits.size > 0 && /* @__PURE__ */ jsx28(
|
|
2678
|
+
EditingBar,
|
|
2589
2679
|
{
|
|
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
|
-
]
|
|
2680
|
+
editing,
|
|
2681
|
+
pendingRowCount: pendingEdits.size,
|
|
2682
|
+
editErrors,
|
|
2683
|
+
onSave: () => void handleSaveEdits(),
|
|
2684
|
+
onDiscard: handleDiscardEdits
|
|
2634
2685
|
}
|
|
2635
2686
|
),
|
|
2636
|
-
showSelectionColumn && headerActions && /* @__PURE__ */
|
|
2637
|
-
/* @__PURE__ */
|
|
2687
|
+
showSelectionColumn && headerActions && /* @__PURE__ */ jsxs18("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
|
|
2688
|
+
/* @__PURE__ */ jsxs18("span", { children: [
|
|
2638
2689
|
selectedRows.length,
|
|
2639
2690
|
" selected"
|
|
2640
2691
|
] }),
|
|
2641
|
-
/* @__PURE__ */
|
|
2692
|
+
/* @__PURE__ */ jsx28(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
|
|
2642
2693
|
] }),
|
|
2643
|
-
/* @__PURE__ */
|
|
2644
|
-
loading && /* @__PURE__ */
|
|
2694
|
+
/* @__PURE__ */ jsxs18("div", { className: "relative min-h-0 flex-1", children: [
|
|
2695
|
+
loading && /* @__PURE__ */ jsx28(
|
|
2645
2696
|
"div",
|
|
2646
2697
|
{
|
|
2647
2698
|
"data-testid": "datagrid-loading-overlay",
|
|
2648
2699
|
className: "absolute inset-0 z-30 flex items-center justify-center bg-background/60",
|
|
2649
|
-
children: /* @__PURE__ */
|
|
2700
|
+
children: /* @__PURE__ */ jsx28(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
|
|
2650
2701
|
}
|
|
2651
2702
|
),
|
|
2652
|
-
/* @__PURE__ */
|
|
2703
|
+
/* @__PURE__ */ jsx28(
|
|
2653
2704
|
"div",
|
|
2654
2705
|
{
|
|
2655
2706
|
ref: scrollRef,
|
|
2656
2707
|
"data-testid": testId,
|
|
2657
2708
|
className: "h-full overflow-auto rounded-md border",
|
|
2658
2709
|
style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : void 0,
|
|
2659
|
-
children: /* @__PURE__ */
|
|
2660
|
-
/* @__PURE__ */
|
|
2710
|
+
children: /* @__PURE__ */ jsxs18("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
|
|
2711
|
+
/* @__PURE__ */ jsxs18("thead", { ref: theadRef, className: "bg-muted", children: [
|
|
2661
2712
|
table.getHeaderGroups().map((headerGroup) => {
|
|
2662
2713
|
const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));
|
|
2663
2714
|
function renderLeafHeaderCell(column, rowSpan) {
|
|
@@ -2668,9 +2719,9 @@ function DataGrid({
|
|
|
2668
2719
|
const filterable = isFilterable(column) && column.filterDisplay !== "row";
|
|
2669
2720
|
const cellProps = leafHeaderCellPropsByColumn.get(column.id);
|
|
2670
2721
|
const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;
|
|
2671
|
-
return /* @__PURE__ */
|
|
2672
|
-
/* @__PURE__ */
|
|
2673
|
-
sortable ? /* @__PURE__ */
|
|
2722
|
+
return /* @__PURE__ */ jsxs18("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
|
|
2723
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1", children: [
|
|
2724
|
+
sortable ? /* @__PURE__ */ jsxs18(
|
|
2674
2725
|
"button",
|
|
2675
2726
|
{
|
|
2676
2727
|
type: "button",
|
|
@@ -2678,8 +2729,8 @@ function DataGrid({
|
|
|
2678
2729
|
onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
|
|
2679
2730
|
children: [
|
|
2680
2731
|
headerContent,
|
|
2681
|
-
sortEntry?.dir === "asc" && /* @__PURE__ */
|
|
2682
|
-
sortEntry?.dir === "desc" && /* @__PURE__ */
|
|
2732
|
+
sortEntry?.dir === "asc" && /* @__PURE__ */ jsx28(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
|
|
2733
|
+
sortEntry?.dir === "desc" && /* @__PURE__ */ jsx28(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
|
|
2683
2734
|
]
|
|
2684
2735
|
}
|
|
2685
2736
|
) : (
|
|
@@ -2688,10 +2739,10 @@ function DataGrid({
|
|
|
2688
2739
|
// silently breaking any interactive content a column's own `renderHeader`
|
|
2689
2740
|
// embeds directly (bare filter inputs, "select/reject all" buttons) —
|
|
2690
2741
|
// clicks never reach them at all, even though nothing looks wrong visually.
|
|
2691
|
-
/* @__PURE__ */
|
|
2742
|
+
/* @__PURE__ */ jsx28("div", { className: "flex items-center gap-1 px-1", children: headerContent })
|
|
2692
2743
|
),
|
|
2693
|
-
filterable && /* @__PURE__ */
|
|
2694
|
-
/* @__PURE__ */
|
|
2744
|
+
filterable && /* @__PURE__ */ jsxs18(Popover, { children: [
|
|
2745
|
+
/* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx28(
|
|
2695
2746
|
FunnelIcon7,
|
|
2696
2747
|
{
|
|
2697
2748
|
className: cn(
|
|
@@ -2701,10 +2752,10 @@ function DataGrid({
|
|
|
2701
2752
|
"aria-hidden": true
|
|
2702
2753
|
}
|
|
2703
2754
|
) }) }),
|
|
2704
|
-
/* @__PURE__ */
|
|
2755
|
+
/* @__PURE__ */ jsx28(PopoverContent, { children: renderFilterWidget(column) })
|
|
2705
2756
|
] })
|
|
2706
2757
|
] }),
|
|
2707
|
-
enableColumnResizing && /* @__PURE__ */
|
|
2758
|
+
enableColumnResizing && /* @__PURE__ */ jsx28(
|
|
2708
2759
|
"div",
|
|
2709
2760
|
{
|
|
2710
2761
|
"data-testid": `resize-handle-${column.id}`,
|
|
@@ -2715,9 +2766,9 @@ function DataGrid({
|
|
|
2715
2766
|
)
|
|
2716
2767
|
] }, header.id);
|
|
2717
2768
|
}
|
|
2718
|
-
return /* @__PURE__ */
|
|
2719
|
-
hasHeaderGroups && /* @__PURE__ */
|
|
2720
|
-
showDetailColumn && /* @__PURE__ */
|
|
2769
|
+
return /* @__PURE__ */ jsxs18(Fragment2, { children: [
|
|
2770
|
+
hasHeaderGroups && /* @__PURE__ */ jsxs18("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
|
|
2771
|
+
showDetailColumn && /* @__PURE__ */ jsx28(
|
|
2721
2772
|
"th",
|
|
2722
2773
|
{
|
|
2723
2774
|
rowSpan: 2,
|
|
@@ -2726,13 +2777,13 @@ function DataGrid({
|
|
|
2726
2777
|
"aria-hidden": true
|
|
2727
2778
|
}
|
|
2728
2779
|
),
|
|
2729
|
-
showSelectionColumn && /* @__PURE__ */
|
|
2780
|
+
showSelectionColumn && /* @__PURE__ */ jsx28(
|
|
2730
2781
|
"th",
|
|
2731
2782
|
{
|
|
2732
2783
|
rowSpan: 2,
|
|
2733
2784
|
className: selectionHeaderCellProps.className,
|
|
2734
2785
|
style: selectionHeaderCellProps.style,
|
|
2735
|
-
children: /* @__PURE__ */
|
|
2786
|
+
children: /* @__PURE__ */ jsx28(
|
|
2736
2787
|
"input",
|
|
2737
2788
|
{
|
|
2738
2789
|
type: "checkbox",
|
|
@@ -2744,7 +2795,7 @@ function DataGrid({
|
|
|
2744
2795
|
}
|
|
2745
2796
|
),
|
|
2746
2797
|
headerRuns.map(
|
|
2747
|
-
(run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */
|
|
2798
|
+
(run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx28(
|
|
2748
2799
|
"th",
|
|
2749
2800
|
{
|
|
2750
2801
|
colSpan: run.columns.length,
|
|
@@ -2754,7 +2805,7 @@ function DataGrid({
|
|
|
2754
2805
|
`group-${run.label}-${run.columns[0].id}`
|
|
2755
2806
|
)
|
|
2756
2807
|
),
|
|
2757
|
-
showRowActionsColumn && /* @__PURE__ */
|
|
2808
|
+
showRowActionsColumn && /* @__PURE__ */ jsx28(
|
|
2758
2809
|
"th",
|
|
2759
2810
|
{
|
|
2760
2811
|
rowSpan: 2,
|
|
@@ -2764,8 +2815,8 @@ function DataGrid({
|
|
|
2764
2815
|
}
|
|
2765
2816
|
)
|
|
2766
2817
|
] }, `${headerGroup.id}-groups`),
|
|
2767
|
-
/* @__PURE__ */
|
|
2768
|
-
!hasHeaderGroups && showDetailColumn && /* @__PURE__ */
|
|
2818
|
+
/* @__PURE__ */ jsxs18("tr", { className: "divide-x divide-border", children: [
|
|
2819
|
+
!hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx28(
|
|
2769
2820
|
"th",
|
|
2770
2821
|
{
|
|
2771
2822
|
className: detailHeaderCellProps.className,
|
|
@@ -2773,12 +2824,12 @@ function DataGrid({
|
|
|
2773
2824
|
"aria-hidden": true
|
|
2774
2825
|
}
|
|
2775
2826
|
),
|
|
2776
|
-
!hasHeaderGroups && showSelectionColumn && /* @__PURE__ */
|
|
2827
|
+
!hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx28(
|
|
2777
2828
|
"th",
|
|
2778
2829
|
{
|
|
2779
2830
|
className: selectionHeaderCellProps.className,
|
|
2780
2831
|
style: selectionHeaderCellProps.style,
|
|
2781
|
-
children: /* @__PURE__ */
|
|
2832
|
+
children: /* @__PURE__ */ jsx28(
|
|
2782
2833
|
"input",
|
|
2783
2834
|
{
|
|
2784
2835
|
type: "checkbox",
|
|
@@ -2794,7 +2845,7 @@ function DataGrid({
|
|
|
2794
2845
|
if (!column || hasHeaderGroups && !column.headerGroup) return null;
|
|
2795
2846
|
return renderLeafHeaderCell(column);
|
|
2796
2847
|
}),
|
|
2797
|
-
!hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */
|
|
2848
|
+
!hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx28(
|
|
2798
2849
|
"th",
|
|
2799
2850
|
{
|
|
2800
2851
|
className: rowActionsHeaderCellProps.className,
|
|
@@ -2805,9 +2856,9 @@ function DataGrid({
|
|
|
2805
2856
|
] }, headerGroup.id)
|
|
2806
2857
|
] }, headerGroup.id);
|
|
2807
2858
|
}),
|
|
2808
|
-
hasFilterRow && /* @__PURE__ */
|
|
2809
|
-
showDetailColumn && /* @__PURE__ */
|
|
2810
|
-
showSelectionColumn && /* @__PURE__ */
|
|
2859
|
+
hasFilterRow && /* @__PURE__ */ jsxs18("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
|
|
2860
|
+
showDetailColumn && /* @__PURE__ */ jsx28("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
|
|
2861
|
+
showSelectionColumn && /* @__PURE__ */ jsx28(
|
|
2811
2862
|
"th",
|
|
2812
2863
|
{
|
|
2813
2864
|
className: selectionHeaderCellProps.className,
|
|
@@ -2816,9 +2867,9 @@ function DataGrid({
|
|
|
2816
2867
|
),
|
|
2817
2868
|
visibleColumns.map((column) => {
|
|
2818
2869
|
const cellProps = filterHeaderCellPropsByColumn.get(column.id);
|
|
2819
|
-
return /* @__PURE__ */
|
|
2870
|
+
return /* @__PURE__ */ jsx28("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
|
|
2820
2871
|
}),
|
|
2821
|
-
showRowActionsColumn && /* @__PURE__ */
|
|
2872
|
+
showRowActionsColumn && /* @__PURE__ */ jsx28(
|
|
2822
2873
|
"th",
|
|
2823
2874
|
{
|
|
2824
2875
|
className: rowActionsHeaderCellProps.className,
|
|
@@ -2827,19 +2878,19 @@ function DataGrid({
|
|
|
2827
2878
|
)
|
|
2828
2879
|
] })
|
|
2829
2880
|
] }),
|
|
2830
|
-
rows.length === 0 ? /* @__PURE__ */
|
|
2831
|
-
paddingTop > 0 && /* @__PURE__ */
|
|
2881
|
+
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: [
|
|
2882
|
+
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
2883
|
virtualItems.map(
|
|
2833
2884
|
(virtualItem) => renderRow(tableRows[virtualItem.index], (el) => virtualizer.measureElement(el))
|
|
2834
2885
|
),
|
|
2835
|
-
paddingBottom > 0 && /* @__PURE__ */
|
|
2886
|
+
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
2887
|
] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
|
|
2837
2888
|
] })
|
|
2838
2889
|
}
|
|
2839
2890
|
)
|
|
2840
2891
|
] }),
|
|
2841
|
-
showPagination && /* @__PURE__ */
|
|
2842
|
-
/* @__PURE__ */
|
|
2892
|
+
showPagination && /* @__PURE__ */ jsxs18("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
|
|
2893
|
+
/* @__PURE__ */ jsxs18("span", { children: [
|
|
2843
2894
|
"Page ",
|
|
2844
2895
|
state.page + 1,
|
|
2845
2896
|
" of ",
|
|
@@ -2850,8 +2901,8 @@ function DataGrid({
|
|
|
2850
2901
|
rowCount === 1 ? "" : "s",
|
|
2851
2902
|
")"
|
|
2852
2903
|
] }),
|
|
2853
|
-
/* @__PURE__ */
|
|
2854
|
-
/* @__PURE__ */
|
|
2904
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex gap-2", children: [
|
|
2905
|
+
/* @__PURE__ */ jsx28(
|
|
2855
2906
|
Button,
|
|
2856
2907
|
{
|
|
2857
2908
|
variant: "outline",
|
|
@@ -2861,7 +2912,7 @@ function DataGrid({
|
|
|
2861
2912
|
children: "Previous"
|
|
2862
2913
|
}
|
|
2863
2914
|
),
|
|
2864
|
-
/* @__PURE__ */
|
|
2915
|
+
/* @__PURE__ */ jsx28(
|
|
2865
2916
|
Button,
|
|
2866
2917
|
{
|
|
2867
2918
|
variant: "outline",
|
|
@@ -2880,7 +2931,7 @@ function DataGrid({
|
|
|
2880
2931
|
// src/tree/TreeDataGrid.tsx
|
|
2881
2932
|
import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
|
|
2882
2933
|
import { AlertCircle, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2, Loader2 as Loader22 } from "lucide-react";
|
|
2883
|
-
import { useEffect as
|
|
2934
|
+
import { useEffect as useEffect10, useMemo as useMemo8, useRef as useRef8 } from "react";
|
|
2884
2935
|
|
|
2885
2936
|
// src/tree/flattenTree.ts
|
|
2886
2937
|
function flattenTree(roots, accessors, expanded, childrenOverride) {
|
|
@@ -2925,7 +2976,7 @@ function computeSelectionStates(roots, accessors, childrenOverride, selectedIds)
|
|
|
2925
2976
|
}
|
|
2926
2977
|
|
|
2927
2978
|
// src/tree/useTreeState.ts
|
|
2928
|
-
import { useCallback as useCallback3, useMemo as useMemo7, useState as
|
|
2979
|
+
import { useCallback as useCallback3, useMemo as useMemo7, useState as useState9 } from "react";
|
|
2929
2980
|
function useTreeState({
|
|
2930
2981
|
data,
|
|
2931
2982
|
getRowId,
|
|
@@ -2937,7 +2988,7 @@ function useTreeState({
|
|
|
2937
2988
|
childrenMap: controlledChildrenMap,
|
|
2938
2989
|
onChildrenMapChange
|
|
2939
2990
|
}) {
|
|
2940
|
-
const [internalExpanded, setInternalExpanded] =
|
|
2991
|
+
const [internalExpanded, setInternalExpanded] = useState9({});
|
|
2941
2992
|
const expanded = controlledExpanded ?? internalExpanded;
|
|
2942
2993
|
const setExpanded = useCallback3(
|
|
2943
2994
|
(updater) => {
|
|
@@ -2949,7 +3000,7 @@ function useTreeState({
|
|
|
2949
3000
|
},
|
|
2950
3001
|
[controlledExpanded, onExpandedChange]
|
|
2951
3002
|
);
|
|
2952
|
-
const [internalChildrenMap, setInternalChildrenMap] =
|
|
3003
|
+
const [internalChildrenMap, setInternalChildrenMap] = useState9(/* @__PURE__ */ new Map());
|
|
2953
3004
|
const childrenMap = controlledChildrenMap ?? internalChildrenMap;
|
|
2954
3005
|
const setChildrenMap = useCallback3(
|
|
2955
3006
|
(updater) => {
|
|
@@ -2961,8 +3012,8 @@ function useTreeState({
|
|
|
2961
3012
|
},
|
|
2962
3013
|
[controlledChildrenMap, onChildrenMapChange]
|
|
2963
3014
|
);
|
|
2964
|
-
const [loadingIds, setLoadingIds] =
|
|
2965
|
-
const [errorIds, setErrorIds] =
|
|
3015
|
+
const [loadingIds, setLoadingIds] = useState9(/* @__PURE__ */ new Set());
|
|
3016
|
+
const [errorIds, setErrorIds] = useState9(/* @__PURE__ */ new Map());
|
|
2966
3017
|
const resolveHasChildren = useMemo7(
|
|
2967
3018
|
() => hasChildren ?? ((row) => (getChildren(row)?.length ?? 0) > 0),
|
|
2968
3019
|
[hasChildren, getChildren]
|
|
@@ -3042,12 +3093,11 @@ function useTreeState({
|
|
|
3042
3093
|
}
|
|
3043
3094
|
|
|
3044
3095
|
// src/tree/TreeDataGrid.tsx
|
|
3045
|
-
import { Fragment as Fragment4, jsx as
|
|
3096
|
+
import { Fragment as Fragment4, jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
3046
3097
|
var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
|
|
3047
3098
|
var UNCHECKED_STATE = { checked: false, indeterminate: false };
|
|
3048
|
-
function renderCell(column, row) {
|
|
3049
|
-
|
|
3050
|
-
return column.cell ? column.cell(value, row) : defaultFormat(column, value);
|
|
3099
|
+
function renderCell(column, row, editingCtx) {
|
|
3100
|
+
return renderEditableCell(column, row, getColumnValue(column, row), editingCtx);
|
|
3051
3101
|
}
|
|
3052
3102
|
function TreeCell({
|
|
3053
3103
|
flatRow,
|
|
@@ -3056,10 +3106,11 @@ function TreeCell({
|
|
|
3056
3106
|
isLoading,
|
|
3057
3107
|
error,
|
|
3058
3108
|
onToggle,
|
|
3059
|
-
onRetry
|
|
3109
|
+
onRetry,
|
|
3110
|
+
editingCtx
|
|
3060
3111
|
}) {
|
|
3061
|
-
return /* @__PURE__ */
|
|
3062
|
-
flatRow.hasChildren ? /* @__PURE__ */
|
|
3112
|
+
return /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
|
|
3113
|
+
flatRow.hasChildren ? /* @__PURE__ */ jsx29(
|
|
3063
3114
|
"button",
|
|
3064
3115
|
{
|
|
3065
3116
|
type: "button",
|
|
@@ -3071,14 +3122,14 @@ function TreeCell({
|
|
|
3071
3122
|
"aria-label": flatRow.isExpanded ? "Collapse" : "Expand",
|
|
3072
3123
|
"aria-expanded": flatRow.isExpanded,
|
|
3073
3124
|
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__ */
|
|
3125
|
+
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
3126
|
}
|
|
3076
|
-
) : /* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
3078
|
-
flatRow.isExpanded && error ? /* @__PURE__ */
|
|
3079
|
-
/* @__PURE__ */
|
|
3127
|
+
) : /* @__PURE__ */ jsx29("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
3128
|
+
/* @__PURE__ */ jsx29("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
|
|
3129
|
+
flatRow.isExpanded && error ? /* @__PURE__ */ jsxs19("span", { className: "ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive", children: [
|
|
3130
|
+
/* @__PURE__ */ jsx29(AlertCircle, { className: "h-3 w-3", "aria-hidden": "true" }),
|
|
3080
3131
|
"Failed to load.",
|
|
3081
|
-
/* @__PURE__ */
|
|
3132
|
+
/* @__PURE__ */ jsx29("button", { type: "button", className: "underline underline-offset-2", onClick: onRetry, children: "Retry" })
|
|
3082
3133
|
] }) : null
|
|
3083
3134
|
] });
|
|
3084
3135
|
}
|
|
@@ -3106,12 +3157,14 @@ function TreeDataGrid({
|
|
|
3106
3157
|
onSelectedIdsChange,
|
|
3107
3158
|
getRowSelectionState,
|
|
3108
3159
|
isRowSelectionDisabled,
|
|
3109
|
-
zebra = true
|
|
3160
|
+
zebra = true,
|
|
3161
|
+
editing
|
|
3110
3162
|
}) {
|
|
3111
3163
|
const accessors = useMemo8(
|
|
3112
3164
|
() => ({ getRowId, getChildren, hasChildren }),
|
|
3113
3165
|
[getRowId, getChildren, hasChildren]
|
|
3114
3166
|
);
|
|
3167
|
+
const editingState = useEditingState(editing, getRowId);
|
|
3115
3168
|
const { expanded, childrenMap, loadingIds, errorIds, toggleExpand, retry, expandToLevel } = useTreeState({
|
|
3116
3169
|
data,
|
|
3117
3170
|
getRowId,
|
|
@@ -3123,8 +3176,8 @@ function TreeDataGrid({
|
|
|
3123
3176
|
childrenMap: controlledChildrenMap,
|
|
3124
3177
|
onChildrenMapChange
|
|
3125
3178
|
});
|
|
3126
|
-
const didAutoExpand =
|
|
3127
|
-
|
|
3179
|
+
const didAutoExpand = useRef8(false);
|
|
3180
|
+
useEffect10(() => {
|
|
3128
3181
|
if (didAutoExpand.current) return;
|
|
3129
3182
|
didAutoExpand.current = true;
|
|
3130
3183
|
if (initialExpandedLevel > 0) void expandToLevel(initialExpandedLevel);
|
|
@@ -3172,7 +3225,7 @@ function TreeDataGrid({
|
|
|
3172
3225
|
for (const column of columns) map.set(column.id, `p-2 ${alignClassName(column)}`);
|
|
3173
3226
|
return map;
|
|
3174
3227
|
}, [columns]);
|
|
3175
|
-
const scrollRef =
|
|
3228
|
+
const scrollRef = useRef8(null);
|
|
3176
3229
|
const shouldVirtualize = flatRows.length > virtualizeThreshold;
|
|
3177
3230
|
const virtualizer = useVirtualizer2({
|
|
3178
3231
|
count: flatRows.length,
|
|
@@ -3185,7 +3238,7 @@ function TreeDataGrid({
|
|
|
3185
3238
|
const rowProps = getRowProps?.(flatRow.row, flatRow.depth, index);
|
|
3186
3239
|
const rowTestId = getRowTestId?.(flatRow.row) ?? `tree-row-${flatRow.id}`;
|
|
3187
3240
|
const isOddRow = zebra && index % 2 === 1;
|
|
3188
|
-
return /* @__PURE__ */
|
|
3241
|
+
return /* @__PURE__ */ jsxs19(
|
|
3189
3242
|
"tr",
|
|
3190
3243
|
{
|
|
3191
3244
|
ref: measureRef,
|
|
@@ -3198,9 +3251,9 @@ function TreeDataGrid({
|
|
|
3198
3251
|
isOddRow && "bg-foreground/5"
|
|
3199
3252
|
) : `border-b border-border divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
|
|
3200
3253
|
children: [
|
|
3201
|
-
showSelectionColumn && /* @__PURE__ */
|
|
3254
|
+
showSelectionColumn && /* @__PURE__ */ jsx29("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
|
|
3202
3255
|
const state = resolveSelectionState(flatRow.row, flatRow.id);
|
|
3203
|
-
return /* @__PURE__ */
|
|
3256
|
+
return /* @__PURE__ */ jsx29(
|
|
3204
3257
|
Checkbox,
|
|
3205
3258
|
{
|
|
3206
3259
|
"aria-label": `Select row ${flatRow.id}`,
|
|
@@ -3211,7 +3264,7 @@ function TreeDataGrid({
|
|
|
3211
3264
|
}
|
|
3212
3265
|
);
|
|
3213
3266
|
})() }),
|
|
3214
|
-
columns.map((column) => /* @__PURE__ */
|
|
3267
|
+
columns.map((column) => /* @__PURE__ */ jsx29("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx29(
|
|
3215
3268
|
TreeCell,
|
|
3216
3269
|
{
|
|
3217
3270
|
flatRow,
|
|
@@ -3220,10 +3273,11 @@ function TreeDataGrid({
|
|
|
3220
3273
|
isLoading: loadingIds.has(flatRow.id),
|
|
3221
3274
|
error: errorIds.get(flatRow.id),
|
|
3222
3275
|
onToggle: () => toggleExpand(flatRow.row),
|
|
3223
|
-
onRetry: () => retry(flatRow.row)
|
|
3276
|
+
onRetry: () => retry(flatRow.row),
|
|
3277
|
+
editingCtx: editingState.ctx
|
|
3224
3278
|
}
|
|
3225
|
-
) : renderCell(column, flatRow.row) }, column.id)),
|
|
3226
|
-
showRowActionsColumn && rowActions && /* @__PURE__ */
|
|
3279
|
+
) : renderCell(column, flatRow.row, editingState.ctx) }, column.id)),
|
|
3280
|
+
showRowActionsColumn && rowActions && /* @__PURE__ */ jsx29("td", { className: "p-2", children: /* @__PURE__ */ jsx29(
|
|
3227
3281
|
ActionsMenu,
|
|
3228
3282
|
{
|
|
3229
3283
|
items: rowActions,
|
|
@@ -3239,49 +3293,62 @@ function TreeDataGrid({
|
|
|
3239
3293
|
const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];
|
|
3240
3294
|
const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
|
|
3241
3295
|
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
|
-
|
|
3296
|
+
return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: [
|
|
3297
|
+
editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx29(
|
|
3298
|
+
EditingBar,
|
|
3299
|
+
{
|
|
3300
|
+
editing,
|
|
3301
|
+
pendingRowCount: editingState.pendingEdits.size,
|
|
3302
|
+
editErrors: editingState.editErrors,
|
|
3303
|
+
onSave: () => void editingState.handleSaveEdits(),
|
|
3304
|
+
onDiscard: editingState.handleDiscardEdits,
|
|
3305
|
+
testIdPrefix: "tree-datagrid"
|
|
3306
|
+
}
|
|
3307
|
+
),
|
|
3308
|
+
/* @__PURE__ */ jsx29(
|
|
3309
|
+
"div",
|
|
3310
|
+
{
|
|
3311
|
+
ref: scrollRef,
|
|
3312
|
+
className: shouldVirtualize ? "overflow-y-auto" : void 0,
|
|
3313
|
+
style: shouldVirtualize ? { maxHeight: maxBodyHeight } : void 0,
|
|
3314
|
+
children: /* @__PURE__ */ jsxs19("table", { className: "w-full border-collapse text-sm", children: [
|
|
3315
|
+
/* @__PURE__ */ jsx29("thead", { children: /* @__PURE__ */ jsxs19("tr", { className: "divide-x divide-border", children: [
|
|
3316
|
+
showSelectionColumn && /* @__PURE__ */ jsx29("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx29(
|
|
3317
|
+
Checkbox,
|
|
3318
|
+
{
|
|
3319
|
+
"aria-label": "Select all visible rows",
|
|
3320
|
+
checked: allVisibleSelected,
|
|
3321
|
+
onCheckedChange: toggleAllVisibleSelected
|
|
3322
|
+
}
|
|
3323
|
+
) }),
|
|
3324
|
+
columns.map((column) => /* @__PURE__ */ jsx29(
|
|
3325
|
+
"th",
|
|
3326
|
+
{
|
|
3327
|
+
style: column.width ? { width: column.width } : void 0,
|
|
3328
|
+
className: `border-b border-border p-2 font-medium ${alignClassName(column)}`,
|
|
3329
|
+
children: column.header
|
|
3330
|
+
},
|
|
3331
|
+
column.id
|
|
3332
|
+
)),
|
|
3333
|
+
showRowActionsColumn && /* @__PURE__ */ jsx29("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
|
|
3334
|
+
] }) }),
|
|
3335
|
+
/* @__PURE__ */ jsx29("tbody", { children: flatRows.length === 0 ? /* @__PURE__ */ jsx29("tr", { children: /* @__PURE__ */ jsx29("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: "No results." }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs19(Fragment4, { children: [
|
|
3336
|
+
paddingTop > 0 && /* @__PURE__ */ jsx29("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx29("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
|
|
3337
|
+
virtualItems.map(
|
|
3338
|
+
(virtualItem) => renderRow(flatRows[virtualItem.index], virtualItem.index, (el) => virtualizer.measureElement(el))
|
|
3339
|
+
),
|
|
3340
|
+
paddingBottom > 0 && /* @__PURE__ */ jsx29("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx29(
|
|
3341
|
+
"td",
|
|
3342
|
+
{
|
|
3343
|
+
colSpan: totalColumnCount,
|
|
3344
|
+
style: { padding: 0, border: "none", height: paddingBottom }
|
|
3345
|
+
}
|
|
3346
|
+
) })
|
|
3347
|
+
] }) : flatRows.map((flatRow, index) => renderRow(flatRow, index)) })
|
|
3348
|
+
] })
|
|
3349
|
+
}
|
|
3350
|
+
)
|
|
3351
|
+
] });
|
|
3285
3352
|
}
|
|
3286
3353
|
export {
|
|
3287
3354
|
ActionsMenu,
|
|
@@ -3292,6 +3359,7 @@ export {
|
|
|
3292
3359
|
DateEditor,
|
|
3293
3360
|
DateRangeFilter,
|
|
3294
3361
|
EditFieldError,
|
|
3362
|
+
EditingBar,
|
|
3295
3363
|
EnumEditor,
|
|
3296
3364
|
EnumFilter,
|
|
3297
3365
|
NumberComparisonFilter,
|
|
@@ -3324,10 +3392,12 @@ export {
|
|
|
3324
3392
|
processClientData,
|
|
3325
3393
|
renderDefaultEditWidget,
|
|
3326
3394
|
renderDefaultFilterWidget,
|
|
3395
|
+
renderEditableCell,
|
|
3327
3396
|
resolveMenuItems,
|
|
3328
3397
|
storageKeyFor,
|
|
3329
3398
|
toDate,
|
|
3330
3399
|
useDebouncedCallback,
|
|
3400
|
+
useEditingState,
|
|
3331
3401
|
useGridState,
|
|
3332
3402
|
useTreeState
|
|
3333
3403
|
};
|