@bmsuisse/datagrid 0.25.3 → 0.26.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.js CHANGED
@@ -1235,6 +1235,10 @@ function isSortable(column) {
1235
1235
  function isFilterable(column) {
1236
1236
  return column.filterable === true;
1237
1237
  }
1238
+ function isEditable(column, row) {
1239
+ if (!column.editable) return false;
1240
+ return typeof column.editable === "function" ? column.editable(row) : true;
1241
+ }
1238
1242
 
1239
1243
  // src/filter/facetedValues.ts
1240
1244
  function withoutColumn(filter, columnId) {
@@ -1365,6 +1369,202 @@ function renderDefaultFilterWidget(column, value, onChange) {
1365
1369
  }
1366
1370
  }
1367
1371
 
1372
+ // src/edit/widget-types.ts
1373
+ function editErrorId(rowId, columnId) {
1374
+ return `edit-error-${rowId}-${columnId}`;
1375
+ }
1376
+
1377
+ // src/edit/EditFieldError.tsx
1378
+ import { jsx as jsx15 } from "react/jsx-runtime";
1379
+ function EditFieldError({ id, message }) {
1380
+ return /* @__PURE__ */ jsx15("span", { id, className: "mt-0.5 block text-xs text-destructive", children: message });
1381
+ }
1382
+
1383
+ // src/edit/BooleanEditor.tsx
1384
+ import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
1385
+ function toBoolean(value) {
1386
+ if (typeof value === "boolean") return value;
1387
+ if (typeof value === "string") return value.trim().toLowerCase() === "true";
1388
+ if (typeof value === "number") return value !== 0;
1389
+ return false;
1390
+ }
1391
+ function BooleanEditor({
1392
+ column,
1393
+ rowId,
1394
+ value,
1395
+ onChange,
1396
+ error,
1397
+ autoFocus
1398
+ }) {
1399
+ const errorId = editErrorId(rowId, column.id);
1400
+ return (
1401
+ // No `gap-*` here (unlike a plain flex stack) — `EditFieldError`'s own
1402
+ // `mt-0.5` already provides the same spacing every other editor gets;
1403
+ // adding both would double it for this one editor only.
1404
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center", children: [
1405
+ /* @__PURE__ */ jsx16(
1406
+ Checkbox,
1407
+ {
1408
+ "aria-label": `Edit ${column.header}`,
1409
+ "aria-invalid": error ? true : void 0,
1410
+ "aria-describedby": error ? errorId : void 0,
1411
+ "data-testid": `edit-${rowId}-${column.id}`,
1412
+ autoFocus,
1413
+ checked: toBoolean(value),
1414
+ onCheckedChange: (next) => onChange(next === true)
1415
+ }
1416
+ ),
1417
+ error && /* @__PURE__ */ jsx16(EditFieldError, { id: errorId, message: error })
1418
+ ] })
1419
+ );
1420
+ }
1421
+
1422
+ // src/edit/DateEditor.tsx
1423
+ import { format as formatDate, parseISO as parseISO4 } from "date-fns";
1424
+ import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
1425
+ function inputValueOf(value, columnType) {
1426
+ const date = toDate(value);
1427
+ if (!date) return "";
1428
+ return columnType === "datetime" ? formatDate(date, "yyyy-MM-dd'T'HH:mm:ss") : formatDate(date, "yyyy-MM-dd");
1429
+ }
1430
+ function DateEditor({
1431
+ column,
1432
+ rowId,
1433
+ value,
1434
+ onChange,
1435
+ error,
1436
+ autoFocus
1437
+ }) {
1438
+ const errorId = editErrorId(rowId, column.id);
1439
+ return /* @__PURE__ */ jsxs10("div", { children: [
1440
+ /* @__PURE__ */ jsx17(
1441
+ Input,
1442
+ {
1443
+ type: column.type === "datetime" ? "datetime-local" : "date",
1444
+ step: column.type === "datetime" ? 1 : void 0,
1445
+ className: "h-8",
1446
+ "aria-label": `Edit ${column.header}`,
1447
+ "aria-invalid": error ? true : void 0,
1448
+ "aria-describedby": error ? errorId : void 0,
1449
+ "data-testid": `edit-${rowId}-${column.id}`,
1450
+ autoFocus,
1451
+ value: inputValueOf(value, column.type),
1452
+ onChange: (event) => onChange(event.target.value === "" ? null : parseISO4(event.target.value))
1453
+ }
1454
+ ),
1455
+ error && /* @__PURE__ */ jsx17(EditFieldError, { id: errorId, message: error })
1456
+ ] });
1457
+ }
1458
+
1459
+ // src/edit/EnumEditor.tsx
1460
+ import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
1461
+ function EnumEditor({
1462
+ column,
1463
+ rowId,
1464
+ value,
1465
+ onChange,
1466
+ error,
1467
+ autoFocus
1468
+ }) {
1469
+ const errorId = editErrorId(rowId, column.id);
1470
+ return /* @__PURE__ */ jsxs11("div", { children: [
1471
+ /* @__PURE__ */ jsxs11(Select, { value: value === null || value === void 0 ? "" : String(value), onValueChange: onChange, children: [
1472
+ /* @__PURE__ */ jsx18(
1473
+ SelectTrigger,
1474
+ {
1475
+ className: "h-8",
1476
+ "aria-label": `Edit ${column.header}`,
1477
+ "aria-invalid": error ? true : void 0,
1478
+ "aria-describedby": error ? errorId : void 0,
1479
+ "data-testid": `edit-${rowId}-${column.id}`,
1480
+ autoFocus,
1481
+ children: /* @__PURE__ */ jsx18(SelectValue, {})
1482
+ }
1483
+ ),
1484
+ /* @__PURE__ */ jsx18(SelectContent, { children: column.options.map((option) => /* @__PURE__ */ jsx18(SelectItem, { value: option.value, children: option.label }, option.value)) })
1485
+ ] }),
1486
+ error && /* @__PURE__ */ jsx18(EditFieldError, { id: errorId, message: error })
1487
+ ] });
1488
+ }
1489
+
1490
+ // src/edit/NumberEditor.tsx
1491
+ import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
1492
+ function NumberEditor({
1493
+ column,
1494
+ rowId,
1495
+ value,
1496
+ onChange,
1497
+ error,
1498
+ autoFocus
1499
+ }) {
1500
+ const errorId = editErrorId(rowId, column.id);
1501
+ return /* @__PURE__ */ jsxs12("div", { children: [
1502
+ /* @__PURE__ */ jsx19(
1503
+ Input,
1504
+ {
1505
+ type: "number",
1506
+ className: "h-8",
1507
+ "aria-label": `Edit ${column.header}`,
1508
+ "aria-invalid": error ? true : void 0,
1509
+ "aria-describedby": error ? errorId : void 0,
1510
+ "data-testid": `edit-${rowId}-${column.id}`,
1511
+ autoFocus,
1512
+ value: value === null || value === void 0 ? "" : String(value),
1513
+ onChange: (event) => onChange(event.target.value === "" ? null : Number(event.target.value))
1514
+ }
1515
+ ),
1516
+ error && /* @__PURE__ */ jsx19(EditFieldError, { id: errorId, message: error })
1517
+ ] });
1518
+ }
1519
+
1520
+ // src/edit/StringEditor.tsx
1521
+ import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
1522
+ function StringEditor({
1523
+ column,
1524
+ rowId,
1525
+ value,
1526
+ onChange,
1527
+ error,
1528
+ autoFocus
1529
+ }) {
1530
+ const errorId = editErrorId(rowId, column.id);
1531
+ return /* @__PURE__ */ jsxs13("div", { children: [
1532
+ /* @__PURE__ */ jsx20(
1533
+ Input,
1534
+ {
1535
+ className: "h-8",
1536
+ "aria-label": `Edit ${column.header}`,
1537
+ "aria-invalid": error ? true : void 0,
1538
+ "aria-describedby": error ? errorId : void 0,
1539
+ "data-testid": `edit-${rowId}-${column.id}`,
1540
+ autoFocus,
1541
+ value: value === null || value === void 0 ? "" : String(value),
1542
+ onChange: (event) => onChange(event.target.value)
1543
+ }
1544
+ ),
1545
+ error && /* @__PURE__ */ jsx20(EditFieldError, { id: errorId, message: error })
1546
+ ] });
1547
+ }
1548
+
1549
+ // src/edit/registry.tsx
1550
+ import { jsx as jsx21 } from "react/jsx-runtime";
1551
+ function renderDefaultEditWidget(column, rowId, value, onChange, error, autoFocus) {
1552
+ switch (column.type) {
1553
+ case "string":
1554
+ return /* @__PURE__ */ jsx21(StringEditor, { column, rowId, value, onChange, error, autoFocus });
1555
+ case "enum":
1556
+ return /* @__PURE__ */ jsx21(EnumEditor, { column, rowId, value, onChange, error, autoFocus });
1557
+ case "boolean":
1558
+ return /* @__PURE__ */ jsx21(BooleanEditor, { column, rowId, value, onChange, error, autoFocus });
1559
+ case "number":
1560
+ case "currency":
1561
+ return /* @__PURE__ */ jsx21(NumberEditor, { column, rowId, value, onChange, error, autoFocus });
1562
+ case "date":
1563
+ case "datetime":
1564
+ return /* @__PURE__ */ jsx21(DateEditor, { column, rowId, value, onChange, error, autoFocus });
1565
+ }
1566
+ }
1567
+
1368
1568
  // src/column-selector/ColumnSelector.tsx
1369
1569
  import { Columns3 } from "lucide-react";
1370
1570
  import { useEffect as useEffect4 } from "react";
@@ -1373,10 +1573,10 @@ import { useEffect as useEffect4 } from "react";
1373
1573
  import * as DialogPrimitive from "@radix-ui/react-dialog";
1374
1574
  import { X } from "lucide-react";
1375
1575
  import { forwardRef as forwardRef6 } from "react";
1376
- import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
1576
+ import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
1377
1577
  var Dialog = DialogPrimitive.Root;
1378
1578
  var DialogTrigger = DialogPrimitive.Trigger;
1379
- var DialogOverlay = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx15(
1579
+ var DialogOverlay = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx22(
1380
1580
  DialogPrimitive.Overlay,
1381
1581
  {
1382
1582
  ref,
@@ -1385,9 +1585,9 @@ var DialogOverlay = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ *
1385
1585
  }
1386
1586
  ));
1387
1587
  DialogOverlay.displayName = "DialogOverlay";
1388
- var DialogContent = forwardRef6(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs9(DialogPrimitive.Portal, { children: [
1389
- /* @__PURE__ */ jsx15(DialogOverlay, {}),
1390
- /* @__PURE__ */ jsxs9(
1588
+ var DialogContent = forwardRef6(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs14(DialogPrimitive.Portal, { children: [
1589
+ /* @__PURE__ */ jsx22(DialogOverlay, {}),
1590
+ /* @__PURE__ */ jsxs14(
1391
1591
  DialogPrimitive.Content,
1392
1592
  {
1393
1593
  ref,
@@ -1398,9 +1598,9 @@ var DialogContent = forwardRef6(({ className, children, ...props }, ref) => /* @
1398
1598
  ...props,
1399
1599
  children: [
1400
1600
  children,
1401
- /* @__PURE__ */ jsxs9(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100", children: [
1402
- /* @__PURE__ */ jsx15(X, { className: "h-4 w-4" }),
1403
- /* @__PURE__ */ jsx15("span", { className: "sr-only", children: "Close" })
1601
+ /* @__PURE__ */ jsxs14(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100", children: [
1602
+ /* @__PURE__ */ jsx22(X, { className: "h-4 w-4" }),
1603
+ /* @__PURE__ */ jsx22("span", { className: "sr-only", children: "Close" })
1404
1604
  ] })
1405
1605
  ]
1406
1606
  }
@@ -1410,10 +1610,10 @@ DialogContent.displayName = "DialogContent";
1410
1610
  var DialogHeader = ({
1411
1611
  className,
1412
1612
  ...props
1413
- }) => /* @__PURE__ */ jsx15("div", { className: cn("flex flex-col gap-1.5", className), ...props });
1414
- var DialogTitle = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx15(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
1613
+ }) => /* @__PURE__ */ jsx22("div", { className: cn("flex flex-col gap-1.5", className), ...props });
1614
+ var DialogTitle = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx22(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
1415
1615
  DialogTitle.displayName = "DialogTitle";
1416
- var DialogDescription = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx15(
1616
+ var DialogDescription = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx22(
1417
1617
  DialogPrimitive.Description,
1418
1618
  {
1419
1619
  ref,
@@ -1481,7 +1681,7 @@ function canHideGroup(columns, visibility, groupColumnIds) {
1481
1681
  }
1482
1682
 
1483
1683
  // src/column-selector/ColumnSelector.tsx
1484
- import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
1684
+ import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
1485
1685
  function ColumnSelector({
1486
1686
  columns,
1487
1687
  visibility,
@@ -1506,20 +1706,20 @@ function ColumnSelector({
1506
1706
  }
1507
1707
  const totalVisible = countVisible(columns, visibility);
1508
1708
  const groups = groupColumns(columns);
1509
- return /* @__PURE__ */ jsxs10(Dialog, { open, onOpenChange, children: [
1510
- /* @__PURE__ */ jsx16(DialogTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx16(Button, { variant: "outline", size: "icon", "aria-label": "Choose columns", children: /* @__PURE__ */ jsx16(Columns3, { className: "h-4 w-4", "aria-hidden": true }) }) }),
1511
- /* @__PURE__ */ jsxs10(DialogContent, { className: "w-fit max-w-[calc(100vw-2rem)] sm:max-w-2xl", children: [
1512
- /* @__PURE__ */ jsx16(DialogHeader, { children: /* @__PURE__ */ jsx16(DialogTitle, { children: "Columns" }) }),
1513
- /* @__PURE__ */ jsx16("div", { className: "flex flex-wrap gap-x-8 gap-y-5", children: groups.map(({ group, columns: groupCols }) => /* @__PURE__ */ jsxs10("div", { className: "flex min-w-32 flex-col gap-1.5", children: [
1514
- group && /* @__PURE__ */ jsx16("span", { className: "border-b pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: group }),
1709
+ return /* @__PURE__ */ jsxs15(Dialog, { open, onOpenChange, children: [
1710
+ /* @__PURE__ */ jsx23(DialogTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx23(Button, { variant: "outline", size: "icon", "aria-label": "Choose columns", children: /* @__PURE__ */ jsx23(Columns3, { className: "h-4 w-4", "aria-hidden": true }) }) }),
1711
+ /* @__PURE__ */ jsxs15(DialogContent, { className: "w-fit max-w-[calc(100vw-2rem)] sm:max-w-2xl", children: [
1712
+ /* @__PURE__ */ jsx23(DialogHeader, { children: /* @__PURE__ */ jsx23(DialogTitle, { children: "Columns" }) }),
1713
+ /* @__PURE__ */ jsx23("div", { className: "flex flex-wrap gap-x-8 gap-y-5", children: groups.map(({ group, columns: groupCols }) => /* @__PURE__ */ jsxs15("div", { className: "flex min-w-32 flex-col gap-1.5", children: [
1714
+ group && /* @__PURE__ */ jsx23("span", { className: "border-b pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: group }),
1515
1715
  groupCols.map((column) => {
1516
1716
  const visible = isColumnVisible(visibility, column.id);
1517
- return /* @__PURE__ */ jsxs10(
1717
+ return /* @__PURE__ */ jsxs15(
1518
1718
  "label",
1519
1719
  {
1520
1720
  className: "flex items-center gap-2 rounded-md px-1.5 py-1 text-sm hover:bg-accent/50 focus-within:bg-accent/50",
1521
1721
  children: [
1522
- /* @__PURE__ */ jsx16(
1722
+ /* @__PURE__ */ jsx23(
1523
1723
  Checkbox,
1524
1724
  {
1525
1725
  checked: visible,
@@ -1556,10 +1756,10 @@ import { useMemo as useMemo3 } from "react";
1556
1756
  // src/components/ui/dropdown-menu.tsx
1557
1757
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
1558
1758
  import { forwardRef as forwardRef7 } from "react";
1559
- import { jsx as jsx17 } from "react/jsx-runtime";
1759
+ import { jsx as jsx24 } from "react/jsx-runtime";
1560
1760
  var DropdownMenu = DropdownMenuPrimitive.Root;
1561
1761
  var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
1562
- var DropdownMenuContent = forwardRef7(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx17(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx17(
1762
+ var DropdownMenuContent = forwardRef7(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx24(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx24(
1563
1763
  DropdownMenuPrimitive.Content,
1564
1764
  {
1565
1765
  ref,
@@ -1572,7 +1772,7 @@ var DropdownMenuContent = forwardRef7(({ className, sideOffset = 4, ...props },
1572
1772
  }
1573
1773
  ) }));
1574
1774
  DropdownMenuContent.displayName = "DropdownMenuContent";
1575
- var DropdownMenuItem = forwardRef7(({ className, danger, ...props }, ref) => /* @__PURE__ */ jsx17(
1775
+ var DropdownMenuItem = forwardRef7(({ className, danger, ...props }, ref) => /* @__PURE__ */ jsx24(
1576
1776
  DropdownMenuPrimitive.Item,
1577
1777
  {
1578
1778
  ref,
@@ -1585,7 +1785,7 @@ var DropdownMenuItem = forwardRef7(({ className, danger, ...props }, ref) => /*
1585
1785
  }
1586
1786
  ));
1587
1787
  DropdownMenuItem.displayName = "DropdownMenuItem";
1588
- var DropdownMenuSeparator = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
1788
+ var DropdownMenuSeparator = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
1589
1789
  DropdownMenuPrimitive.Separator,
1590
1790
  {
1591
1791
  ref,
@@ -1594,7 +1794,7 @@ var DropdownMenuSeparator = forwardRef7(({ className, ...props }, ref) => /* @__
1594
1794
  }
1595
1795
  ));
1596
1796
  DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
1597
- var DropdownMenuLabel = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx17(
1797
+ var DropdownMenuLabel = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
1598
1798
  DropdownMenuPrimitive.Label,
1599
1799
  {
1600
1800
  ref,
@@ -1605,16 +1805,16 @@ var DropdownMenuLabel = forwardRef7(({ className, ...props }, ref) => /* @__PURE
1605
1805
  DropdownMenuLabel.displayName = "DropdownMenuLabel";
1606
1806
 
1607
1807
  // src/menu/ActionsMenu.tsx
1608
- import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
1808
+ import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1609
1809
  function ActionsMenu({ items, ctx, triggerLabel }) {
1610
1810
  const resolved = useMemo3(
1611
1811
  () => resolveMenuItems(items, ctx),
1612
1812
  [items, ctx.row, ctx.selectedRows]
1613
1813
  );
1614
1814
  if (resolved.length === 0) return null;
1615
- return /* @__PURE__ */ jsxs11(DropdownMenu, { children: [
1616
- /* @__PURE__ */ jsx18(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx18(Button, { variant: "ghost", size: "icon", "aria-label": triggerLabel, onClick: stopRowClick, children: /* @__PURE__ */ jsx18(MoreVertical, { className: "h-4 w-4", "aria-hidden": true }) }) }),
1617
- /* @__PURE__ */ jsx18(DropdownMenuContent, { align: "end", children: resolved.map((item) => /* @__PURE__ */ jsx18(
1815
+ return /* @__PURE__ */ jsxs16(DropdownMenu, { children: [
1816
+ /* @__PURE__ */ jsx25(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx25(Button, { variant: "ghost", size: "icon", "aria-label": triggerLabel, onClick: stopRowClick, children: /* @__PURE__ */ jsx25(MoreVertical, { className: "h-4 w-4", "aria-hidden": true }) }) }),
1817
+ /* @__PURE__ */ jsx25(DropdownMenuContent, { align: "end", children: resolved.map((item) => /* @__PURE__ */ jsx25(
1618
1818
  DropdownMenuItem,
1619
1819
  {
1620
1820
  danger: item.danger,
@@ -1802,7 +2002,7 @@ function useGridState(dataSource, initialState, externalState) {
1802
2002
  }
1803
2003
 
1804
2004
  // src/grid/DataGrid.tsx
1805
- import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
2005
+ import { Fragment as Fragment3, jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
1806
2006
  var gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });
1807
2007
  var DEFAULT_COLUMN_SIZE = 150;
1808
2008
  var DETAIL_COLUMN_WIDTH = 44;
@@ -1833,15 +2033,77 @@ function computeHeaderRuns(columns) {
1833
2033
  }
1834
2034
  return runs;
1835
2035
  }
1836
- function toTanstackColumns(columns) {
2036
+ function toTanstackColumns(columns, editingCtxRef) {
1837
2037
  return columns.map((column) => ({
1838
2038
  id: column.id,
1839
2039
  header: column.header,
1840
2040
  size: column.width,
1841
2041
  accessorFn: (row) => getColumnValue(column, row),
1842
- cell: (info) => column.cell ? column.cell(info.getValue(), info.row.original) : defaultFormat(column, info.getValue())
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
+ }
1843
2091
  }));
1844
2092
  }
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
+ }
1845
2107
  function DataGrid({
1846
2108
  columns,
1847
2109
  dataSource,
@@ -1870,7 +2132,8 @@ function DataGrid({
1870
2132
  defaultGroupsExpanded = true,
1871
2133
  expandedGroups: controlledExpandedGroups,
1872
2134
  onExpandedGroupsChange,
1873
- zebra = true
2135
+ zebra = true,
2136
+ editing
1874
2137
  }) {
1875
2138
  const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(
1876
2139
  dataSource,
@@ -1910,6 +2173,102 @@ function DataGrid({
1910
2173
  function toggleGroupExpanded(key) {
1911
2174
  updateExpandedGroups({ ...expandedGroupsState, [key]: !isGroupExpanded(key) });
1912
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;
1913
2272
  const visibleColumns = useMemo6(
1914
2273
  () => columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,
1915
2274
  [columns, columnVisibility]
@@ -1938,7 +2297,7 @@ function DataGrid({
1938
2297
  }
1939
2298
  updateSelectedIds(next);
1940
2299
  }
1941
- const tanstackColumns = useMemo6(() => toTanstackColumns(visibleColumns), [visibleColumns]);
2300
+ const tanstackColumns = useMemo6(() => toTanstackColumns(visibleColumns, editingCtxRef), [visibleColumns]);
1942
2301
  const columnById = useMemo6(() => {
1943
2302
  const map = /* @__PURE__ */ new Map();
1944
2303
  for (const column of visibleColumns) map.set(column.id, column);
@@ -2103,20 +2462,20 @@ function DataGrid({
2103
2462
  const rowProps = getRowProps?.(row.original);
2104
2463
  const rowTestId = getRowTestId?.(row.original) ?? `row-${row.id}`;
2105
2464
  const isOddRow = zebra && row.index % 2 === 1;
2106
- return /* @__PURE__ */ jsxs12("tbody", { ref: measureRef, "data-index": row.index, children: [
2107
- /* @__PURE__ */ jsxs12(
2465
+ return /* @__PURE__ */ jsxs17("tbody", { ref: measureRef, "data-index": row.index, children: [
2466
+ /* @__PURE__ */ jsxs17(
2108
2467
  "tr",
2109
2468
  {
2110
2469
  ...rowProps,
2111
2470
  "data-testid": rowTestId,
2112
2471
  className: rowProps?.className ? cn("divide-x divide-border", rowProps.className, isOddRow && "bg-foreground/5") : `divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
2113
2472
  children: [
2114
- showDetailColumn && /* @__PURE__ */ jsx19(
2473
+ showDetailColumn && /* @__PURE__ */ jsx26(
2115
2474
  "td",
2116
2475
  {
2117
2476
  className: isOddRow ? detailCellProps.classNameOdd : detailCellProps.className,
2118
2477
  style: detailCellProps.style,
2119
- children: /* @__PURE__ */ jsx19(
2478
+ children: /* @__PURE__ */ jsx26(
2120
2479
  Button,
2121
2480
  {
2122
2481
  type: "button",
@@ -2128,7 +2487,7 @@ function DataGrid({
2128
2487
  stopRowClick(event);
2129
2488
  toggleRowExpanded(row.id);
2130
2489
  },
2131
- children: /* @__PURE__ */ jsx19(
2490
+ children: /* @__PURE__ */ jsx26(
2132
2491
  ChevronRight,
2133
2492
  {
2134
2493
  className: `h-4 w-4 transition-transform${isExpanded ? " rotate-90" : ""}`,
@@ -2139,12 +2498,12 @@ function DataGrid({
2139
2498
  )
2140
2499
  }
2141
2500
  ),
2142
- showSelectionColumn && /* @__PURE__ */ jsx19(
2501
+ showSelectionColumn && /* @__PURE__ */ jsx26(
2143
2502
  "td",
2144
2503
  {
2145
2504
  className: isOddRow ? selectionCellProps.classNameOdd : selectionCellProps.className,
2146
2505
  style: selectionCellProps.style,
2147
- children: /* @__PURE__ */ jsx19(
2506
+ children: /* @__PURE__ */ jsx26(
2148
2507
  "input",
2149
2508
  {
2150
2509
  type: "checkbox",
@@ -2158,27 +2517,27 @@ function DataGrid({
2158
2517
  ),
2159
2518
  row.getAllCells().map((cell) => {
2160
2519
  const cellProps = bodyCellPropsByColumn.get(cell.column.id);
2161
- return /* @__PURE__ */ jsx19("td", { style: cellProps?.style, className: cellProps?.className ?? BODY_TD_BASE_CLASS, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id);
2520
+ return /* @__PURE__ */ jsx26("td", { style: cellProps?.style, className: cellProps?.className ?? BODY_TD_BASE_CLASS, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id);
2162
2521
  }),
2163
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx19(
2522
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx26(
2164
2523
  "td",
2165
2524
  {
2166
2525
  className: isOddRow ? rowActionsCellProps.classNameOdd : rowActionsCellProps.className,
2167
2526
  style: rowActionsCellProps.style,
2168
- children: /* @__PURE__ */ jsx19(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
2527
+ children: /* @__PURE__ */ jsx26(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
2169
2528
  }
2170
2529
  )
2171
2530
  ]
2172
2531
  }
2173
2532
  ),
2174
- showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx19("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, className: "border-b border-border p-2", children: renderDetail(row.original) }) })
2533
+ showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx26("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx26("td", { colSpan: totalColumnCount, className: "border-b border-border p-2", children: renderDetail(row.original) }) })
2175
2534
  ] }, row.id);
2176
2535
  }
2177
2536
  function renderGroupedBucket(bucket) {
2178
2537
  const expanded = isGroupExpanded(bucket.key);
2179
2538
  const originalRows = bucket.items.map((row) => row.original);
2180
- return /* @__PURE__ */ jsxs12(Fragment2, { children: [
2181
- /* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx19(
2539
+ return /* @__PURE__ */ jsxs17(Fragment2, { children: [
2540
+ /* @__PURE__ */ jsx26("tbody", { children: /* @__PURE__ */ jsx26("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx26(
2182
2541
  "td",
2183
2542
  {
2184
2543
  colSpan: totalColumnCount,
@@ -2187,7 +2546,7 @@ function DataGrid({
2187
2546
  zebra ? "bg-muted" : "bg-background"
2188
2547
  ),
2189
2548
  style: { top: groupHeaderTop },
2190
- children: /* @__PURE__ */ jsxs12(
2549
+ children: /* @__PURE__ */ jsxs17(
2191
2550
  "button",
2192
2551
  {
2193
2552
  type: "button",
@@ -2195,7 +2554,7 @@ function DataGrid({
2195
2554
  onClick: () => toggleGroupExpanded(bucket.key),
2196
2555
  "aria-expanded": expanded,
2197
2556
  children: [
2198
- /* @__PURE__ */ jsx19(
2557
+ /* @__PURE__ */ jsx26(
2199
2558
  ChevronRight,
2200
2559
  {
2201
2560
  className: `h-4 w-4 shrink-0 transition-transform${expanded ? " rotate-90" : ""}`,
@@ -2224,32 +2583,81 @@ function DataGrid({
2224
2583
  // containers (that one scrolling vertically, this component's own
2225
2584
  // `overflow-x-auto` div scrolling horizontally) fighting over which one
2226
2585
  // a sticky header/pinned column is actually stuck relative to.
2227
- /* @__PURE__ */ jsxs12("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
2228
- showSelectionColumn && headerActions && /* @__PURE__ */ jsxs12("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
2229
- /* @__PURE__ */ jsxs12("span", { children: [
2586
+ /* @__PURE__ */ jsxs17("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
2587
+ editing && pendingEdits.size > 0 && /* @__PURE__ */ jsxs17(
2588
+ "div",
2589
+ {
2590
+ "data-testid": "datagrid-edit-bar",
2591
+ className: "flex shrink-0 flex-wrap items-center justify-between gap-2 rounded-md border bg-muted/50 px-3 py-2 text-sm",
2592
+ children: [
2593
+ /* @__PURE__ */ jsxs17("span", { "aria-live": "polite", children: [
2594
+ pendingEdits.size,
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
+ ]
2634
+ }
2635
+ ),
2636
+ showSelectionColumn && headerActions && /* @__PURE__ */ jsxs17("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
2637
+ /* @__PURE__ */ jsxs17("span", { children: [
2230
2638
  selectedRows.length,
2231
2639
  " selected"
2232
2640
  ] }),
2233
- /* @__PURE__ */ jsx19(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
2641
+ /* @__PURE__ */ jsx26(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
2234
2642
  ] }),
2235
- /* @__PURE__ */ jsxs12("div", { className: "relative min-h-0 flex-1", children: [
2236
- loading && /* @__PURE__ */ jsx19(
2643
+ /* @__PURE__ */ jsxs17("div", { className: "relative min-h-0 flex-1", children: [
2644
+ loading && /* @__PURE__ */ jsx26(
2237
2645
  "div",
2238
2646
  {
2239
2647
  "data-testid": "datagrid-loading-overlay",
2240
2648
  className: "absolute inset-0 z-30 flex items-center justify-center bg-background/60",
2241
- children: /* @__PURE__ */ jsx19(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
2649
+ children: /* @__PURE__ */ jsx26(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
2242
2650
  }
2243
2651
  ),
2244
- /* @__PURE__ */ jsx19(
2652
+ /* @__PURE__ */ jsx26(
2245
2653
  "div",
2246
2654
  {
2247
2655
  ref: scrollRef,
2248
2656
  "data-testid": testId,
2249
2657
  className: "h-full overflow-auto rounded-md border",
2250
2658
  style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : void 0,
2251
- children: /* @__PURE__ */ jsxs12("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
2252
- /* @__PURE__ */ jsxs12("thead", { ref: theadRef, className: "bg-muted", children: [
2659
+ children: /* @__PURE__ */ jsxs17("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
2660
+ /* @__PURE__ */ jsxs17("thead", { ref: theadRef, className: "bg-muted", children: [
2253
2661
  table.getHeaderGroups().map((headerGroup) => {
2254
2662
  const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));
2255
2663
  function renderLeafHeaderCell(column, rowSpan) {
@@ -2260,9 +2668,9 @@ function DataGrid({
2260
2668
  const filterable = isFilterable(column) && column.filterDisplay !== "row";
2261
2669
  const cellProps = leafHeaderCellPropsByColumn.get(column.id);
2262
2670
  const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;
2263
- return /* @__PURE__ */ jsxs12("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
2264
- /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
2265
- sortable ? /* @__PURE__ */ jsxs12(
2671
+ return /* @__PURE__ */ jsxs17("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
2672
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-1", children: [
2673
+ sortable ? /* @__PURE__ */ jsxs17(
2266
2674
  "button",
2267
2675
  {
2268
2676
  type: "button",
@@ -2270,8 +2678,8 @@ function DataGrid({
2270
2678
  onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
2271
2679
  children: [
2272
2680
  headerContent,
2273
- sortEntry?.dir === "asc" && /* @__PURE__ */ jsx19(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
2274
- sortEntry?.dir === "desc" && /* @__PURE__ */ jsx19(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
2681
+ sortEntry?.dir === "asc" && /* @__PURE__ */ jsx26(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
2682
+ sortEntry?.dir === "desc" && /* @__PURE__ */ jsx26(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
2275
2683
  ]
2276
2684
  }
2277
2685
  ) : (
@@ -2280,10 +2688,10 @@ function DataGrid({
2280
2688
  // silently breaking any interactive content a column's own `renderHeader`
2281
2689
  // embeds directly (bare filter inputs, "select/reject all" buttons) —
2282
2690
  // clicks never reach them at all, even though nothing looks wrong visually.
2283
- /* @__PURE__ */ jsx19("div", { className: "flex items-center gap-1 px-1", children: headerContent })
2691
+ /* @__PURE__ */ jsx26("div", { className: "flex items-center gap-1 px-1", children: headerContent })
2284
2692
  ),
2285
- filterable && /* @__PURE__ */ jsxs12(Popover, { children: [
2286
- /* @__PURE__ */ jsx19(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx19(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx19(
2693
+ filterable && /* @__PURE__ */ jsxs17(Popover, { children: [
2694
+ /* @__PURE__ */ jsx26(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx26(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx26(
2287
2695
  FunnelIcon7,
2288
2696
  {
2289
2697
  className: cn(
@@ -2293,10 +2701,10 @@ function DataGrid({
2293
2701
  "aria-hidden": true
2294
2702
  }
2295
2703
  ) }) }),
2296
- /* @__PURE__ */ jsx19(PopoverContent, { children: renderFilterWidget(column) })
2704
+ /* @__PURE__ */ jsx26(PopoverContent, { children: renderFilterWidget(column) })
2297
2705
  ] })
2298
2706
  ] }),
2299
- enableColumnResizing && /* @__PURE__ */ jsx19(
2707
+ enableColumnResizing && /* @__PURE__ */ jsx26(
2300
2708
  "div",
2301
2709
  {
2302
2710
  "data-testid": `resize-handle-${column.id}`,
@@ -2307,9 +2715,9 @@ function DataGrid({
2307
2715
  )
2308
2716
  ] }, header.id);
2309
2717
  }
2310
- return /* @__PURE__ */ jsxs12(Fragment2, { children: [
2311
- hasHeaderGroups && /* @__PURE__ */ jsxs12("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
2312
- showDetailColumn && /* @__PURE__ */ jsx19(
2718
+ return /* @__PURE__ */ jsxs17(Fragment2, { children: [
2719
+ hasHeaderGroups && /* @__PURE__ */ jsxs17("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
2720
+ showDetailColumn && /* @__PURE__ */ jsx26(
2313
2721
  "th",
2314
2722
  {
2315
2723
  rowSpan: 2,
@@ -2318,13 +2726,13 @@ function DataGrid({
2318
2726
  "aria-hidden": true
2319
2727
  }
2320
2728
  ),
2321
- showSelectionColumn && /* @__PURE__ */ jsx19(
2729
+ showSelectionColumn && /* @__PURE__ */ jsx26(
2322
2730
  "th",
2323
2731
  {
2324
2732
  rowSpan: 2,
2325
2733
  className: selectionHeaderCellProps.className,
2326
2734
  style: selectionHeaderCellProps.style,
2327
- children: /* @__PURE__ */ jsx19(
2735
+ children: /* @__PURE__ */ jsx26(
2328
2736
  "input",
2329
2737
  {
2330
2738
  type: "checkbox",
@@ -2336,7 +2744,7 @@ function DataGrid({
2336
2744
  }
2337
2745
  ),
2338
2746
  headerRuns.map(
2339
- (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx19(
2747
+ (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx26(
2340
2748
  "th",
2341
2749
  {
2342
2750
  colSpan: run.columns.length,
@@ -2346,7 +2754,7 @@ function DataGrid({
2346
2754
  `group-${run.label}-${run.columns[0].id}`
2347
2755
  )
2348
2756
  ),
2349
- showRowActionsColumn && /* @__PURE__ */ jsx19(
2757
+ showRowActionsColumn && /* @__PURE__ */ jsx26(
2350
2758
  "th",
2351
2759
  {
2352
2760
  rowSpan: 2,
@@ -2356,8 +2764,8 @@ function DataGrid({
2356
2764
  }
2357
2765
  )
2358
2766
  ] }, `${headerGroup.id}-groups`),
2359
- /* @__PURE__ */ jsxs12("tr", { className: "divide-x divide-border", children: [
2360
- !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx19(
2767
+ /* @__PURE__ */ jsxs17("tr", { className: "divide-x divide-border", children: [
2768
+ !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx26(
2361
2769
  "th",
2362
2770
  {
2363
2771
  className: detailHeaderCellProps.className,
@@ -2365,12 +2773,12 @@ function DataGrid({
2365
2773
  "aria-hidden": true
2366
2774
  }
2367
2775
  ),
2368
- !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx19(
2776
+ !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx26(
2369
2777
  "th",
2370
2778
  {
2371
2779
  className: selectionHeaderCellProps.className,
2372
2780
  style: selectionHeaderCellProps.style,
2373
- children: /* @__PURE__ */ jsx19(
2781
+ children: /* @__PURE__ */ jsx26(
2374
2782
  "input",
2375
2783
  {
2376
2784
  type: "checkbox",
@@ -2386,7 +2794,7 @@ function DataGrid({
2386
2794
  if (!column || hasHeaderGroups && !column.headerGroup) return null;
2387
2795
  return renderLeafHeaderCell(column);
2388
2796
  }),
2389
- !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx19(
2797
+ !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx26(
2390
2798
  "th",
2391
2799
  {
2392
2800
  className: rowActionsHeaderCellProps.className,
@@ -2397,9 +2805,9 @@ function DataGrid({
2397
2805
  ] }, headerGroup.id)
2398
2806
  ] }, headerGroup.id);
2399
2807
  }),
2400
- hasFilterRow && /* @__PURE__ */ jsxs12("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
2401
- showDetailColumn && /* @__PURE__ */ jsx19("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
2402
- showSelectionColumn && /* @__PURE__ */ jsx19(
2808
+ hasFilterRow && /* @__PURE__ */ jsxs17("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
2809
+ showDetailColumn && /* @__PURE__ */ jsx26("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
2810
+ showSelectionColumn && /* @__PURE__ */ jsx26(
2403
2811
  "th",
2404
2812
  {
2405
2813
  className: selectionHeaderCellProps.className,
@@ -2408,9 +2816,9 @@ function DataGrid({
2408
2816
  ),
2409
2817
  visibleColumns.map((column) => {
2410
2818
  const cellProps = filterHeaderCellPropsByColumn.get(column.id);
2411
- return /* @__PURE__ */ jsx19("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
2819
+ return /* @__PURE__ */ jsx26("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
2412
2820
  }),
2413
- showRowActionsColumn && /* @__PURE__ */ jsx19(
2821
+ showRowActionsColumn && /* @__PURE__ */ jsx26(
2414
2822
  "th",
2415
2823
  {
2416
2824
  className: rowActionsHeaderCellProps.className,
@@ -2419,19 +2827,19 @@ function DataGrid({
2419
2827
  )
2420
2828
  ] })
2421
2829
  ] }),
2422
- rows.length === 0 ? /* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? "Loading..." : "No results." }) }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs12(Fragment3, { children: [
2423
- paddingTop > 0 && /* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
2830
+ rows.length === 0 ? /* @__PURE__ */ jsx26("tbody", { children: /* @__PURE__ */ jsx26("tr", { children: /* @__PURE__ */ jsx26("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? "Loading..." : "No results." }) }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs17(Fragment3, { children: [
2831
+ paddingTop > 0 && /* @__PURE__ */ jsx26("tbody", { children: /* @__PURE__ */ jsx26("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx26("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
2424
2832
  virtualItems.map(
2425
2833
  (virtualItem) => renderRow(tableRows[virtualItem.index], (el) => virtualizer.measureElement(el))
2426
2834
  ),
2427
- paddingBottom > 0 && /* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingBottom } }) }) })
2835
+ paddingBottom > 0 && /* @__PURE__ */ jsx26("tbody", { children: /* @__PURE__ */ jsx26("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx26("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingBottom } }) }) })
2428
2836
  ] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
2429
2837
  ] })
2430
2838
  }
2431
2839
  )
2432
2840
  ] }),
2433
- showPagination && /* @__PURE__ */ jsxs12("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
2434
- /* @__PURE__ */ jsxs12("span", { children: [
2841
+ showPagination && /* @__PURE__ */ jsxs17("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
2842
+ /* @__PURE__ */ jsxs17("span", { children: [
2435
2843
  "Page ",
2436
2844
  state.page + 1,
2437
2845
  " of ",
@@ -2442,8 +2850,8 @@ function DataGrid({
2442
2850
  rowCount === 1 ? "" : "s",
2443
2851
  ")"
2444
2852
  ] }),
2445
- /* @__PURE__ */ jsxs12("div", { className: "flex gap-2", children: [
2446
- /* @__PURE__ */ jsx19(
2853
+ /* @__PURE__ */ jsxs17("div", { className: "flex gap-2", children: [
2854
+ /* @__PURE__ */ jsx26(
2447
2855
  Button,
2448
2856
  {
2449
2857
  variant: "outline",
@@ -2453,7 +2861,7 @@ function DataGrid({
2453
2861
  children: "Previous"
2454
2862
  }
2455
2863
  ),
2456
- /* @__PURE__ */ jsx19(
2864
+ /* @__PURE__ */ jsx26(
2457
2865
  Button,
2458
2866
  {
2459
2867
  variant: "outline",
@@ -2634,7 +3042,7 @@ function useTreeState({
2634
3042
  }
2635
3043
 
2636
3044
  // src/tree/TreeDataGrid.tsx
2637
- import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
3045
+ import { Fragment as Fragment4, jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
2638
3046
  var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
2639
3047
  var UNCHECKED_STATE = { checked: false, indeterminate: false };
2640
3048
  function renderCell(column, row) {
@@ -2650,8 +3058,8 @@ function TreeCell({
2650
3058
  onToggle,
2651
3059
  onRetry
2652
3060
  }) {
2653
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
2654
- flatRow.hasChildren ? /* @__PURE__ */ jsx20(
3061
+ return /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
3062
+ flatRow.hasChildren ? /* @__PURE__ */ jsx27(
2655
3063
  "button",
2656
3064
  {
2657
3065
  type: "button",
@@ -2663,14 +3071,14 @@ function TreeCell({
2663
3071
  "aria-label": flatRow.isExpanded ? "Collapse" : "Expand",
2664
3072
  "aria-expanded": flatRow.isExpanded,
2665
3073
  className: "flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50",
2666
- children: isLoading ? /* @__PURE__ */ jsx20(Loader22, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx20(ChevronDown2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx20(ChevronRight2, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
3074
+ children: isLoading ? /* @__PURE__ */ jsx27(Loader22, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx27(ChevronDown2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx27(ChevronRight2, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
2667
3075
  }
2668
- ) : /* @__PURE__ */ jsx20("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
2669
- /* @__PURE__ */ jsx20("span", { className: "truncate", children: renderCell(column, flatRow.row) }),
2670
- flatRow.isExpanded && error ? /* @__PURE__ */ jsxs13("span", { className: "ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive", children: [
2671
- /* @__PURE__ */ jsx20(AlertCircle, { className: "h-3 w-3", "aria-hidden": "true" }),
3076
+ ) : /* @__PURE__ */ jsx27("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
3077
+ /* @__PURE__ */ jsx27("span", { className: "truncate", children: renderCell(column, flatRow.row) }),
3078
+ flatRow.isExpanded && error ? /* @__PURE__ */ jsxs18("span", { className: "ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive", children: [
3079
+ /* @__PURE__ */ jsx27(AlertCircle, { className: "h-3 w-3", "aria-hidden": "true" }),
2672
3080
  "Failed to load.",
2673
- /* @__PURE__ */ jsx20("button", { type: "button", className: "underline underline-offset-2", onClick: onRetry, children: "Retry" })
3081
+ /* @__PURE__ */ jsx27("button", { type: "button", className: "underline underline-offset-2", onClick: onRetry, children: "Retry" })
2674
3082
  ] }) : null
2675
3083
  ] });
2676
3084
  }
@@ -2777,7 +3185,7 @@ function TreeDataGrid({
2777
3185
  const rowProps = getRowProps?.(flatRow.row, flatRow.depth, index);
2778
3186
  const rowTestId = getRowTestId?.(flatRow.row) ?? `tree-row-${flatRow.id}`;
2779
3187
  const isOddRow = zebra && index % 2 === 1;
2780
- return /* @__PURE__ */ jsxs13(
3188
+ return /* @__PURE__ */ jsxs18(
2781
3189
  "tr",
2782
3190
  {
2783
3191
  ref: measureRef,
@@ -2790,9 +3198,9 @@ function TreeDataGrid({
2790
3198
  isOddRow && "bg-foreground/5"
2791
3199
  ) : `border-b border-border divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
2792
3200
  children: [
2793
- showSelectionColumn && /* @__PURE__ */ jsx20("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
3201
+ showSelectionColumn && /* @__PURE__ */ jsx27("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
2794
3202
  const state = resolveSelectionState(flatRow.row, flatRow.id);
2795
- return /* @__PURE__ */ jsx20(
3203
+ return /* @__PURE__ */ jsx27(
2796
3204
  Checkbox,
2797
3205
  {
2798
3206
  "aria-label": `Select row ${flatRow.id}`,
@@ -2803,7 +3211,7 @@ function TreeDataGrid({
2803
3211
  }
2804
3212
  );
2805
3213
  })() }),
2806
- columns.map((column) => /* @__PURE__ */ jsx20("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx20(
3214
+ columns.map((column) => /* @__PURE__ */ jsx27("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx27(
2807
3215
  TreeCell,
2808
3216
  {
2809
3217
  flatRow,
@@ -2815,7 +3223,7 @@ function TreeDataGrid({
2815
3223
  onRetry: () => retry(flatRow.row)
2816
3224
  }
2817
3225
  ) : renderCell(column, flatRow.row) }, column.id)),
2818
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx20("td", { className: "p-2", children: /* @__PURE__ */ jsx20(
3226
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx27("td", { className: "p-2", children: /* @__PURE__ */ jsx27(
2819
3227
  ActionsMenu,
2820
3228
  {
2821
3229
  items: rowActions,
@@ -2831,15 +3239,15 @@ function TreeDataGrid({
2831
3239
  const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];
2832
3240
  const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
2833
3241
  const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
2834
- return /* @__PURE__ */ jsx20("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: /* @__PURE__ */ jsx20(
3242
+ return /* @__PURE__ */ jsx27("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: /* @__PURE__ */ jsx27(
2835
3243
  "div",
2836
3244
  {
2837
3245
  ref: scrollRef,
2838
3246
  className: shouldVirtualize ? "overflow-y-auto" : void 0,
2839
3247
  style: shouldVirtualize ? { maxHeight: maxBodyHeight } : void 0,
2840
- children: /* @__PURE__ */ jsxs13("table", { className: "w-full border-collapse text-sm", children: [
2841
- /* @__PURE__ */ jsx20("thead", { children: /* @__PURE__ */ jsxs13("tr", { className: "divide-x divide-border", children: [
2842
- showSelectionColumn && /* @__PURE__ */ jsx20("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx20(
3248
+ children: /* @__PURE__ */ jsxs18("table", { className: "w-full border-collapse text-sm", children: [
3249
+ /* @__PURE__ */ jsx27("thead", { children: /* @__PURE__ */ jsxs18("tr", { className: "divide-x divide-border", children: [
3250
+ showSelectionColumn && /* @__PURE__ */ jsx27("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx27(
2843
3251
  Checkbox,
2844
3252
  {
2845
3253
  "aria-label": "Select all visible rows",
@@ -2847,7 +3255,7 @@ function TreeDataGrid({
2847
3255
  onCheckedChange: toggleAllVisibleSelected
2848
3256
  }
2849
3257
  ) }),
2850
- columns.map((column) => /* @__PURE__ */ jsx20(
3258
+ columns.map((column) => /* @__PURE__ */ jsx27(
2851
3259
  "th",
2852
3260
  {
2853
3261
  style: column.width ? { width: column.width } : void 0,
@@ -2856,14 +3264,14 @@ function TreeDataGrid({
2856
3264
  },
2857
3265
  column.id
2858
3266
  )),
2859
- showRowActionsColumn && /* @__PURE__ */ jsx20("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
3267
+ showRowActionsColumn && /* @__PURE__ */ jsx27("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
2860
3268
  ] }) }),
2861
- /* @__PURE__ */ jsx20("tbody", { children: flatRows.length === 0 ? /* @__PURE__ */ jsx20("tr", { children: /* @__PURE__ */ jsx20("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: "No results." }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs13(Fragment4, { children: [
2862
- paddingTop > 0 && /* @__PURE__ */ jsx20("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx20("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
3269
+ /* @__PURE__ */ jsx27("tbody", { children: flatRows.length === 0 ? /* @__PURE__ */ jsx27("tr", { children: /* @__PURE__ */ jsx27("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: "No results." }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs18(Fragment4, { children: [
3270
+ paddingTop > 0 && /* @__PURE__ */ jsx27("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx27("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
2863
3271
  virtualItems.map(
2864
3272
  (virtualItem) => renderRow(flatRows[virtualItem.index], virtualItem.index, (el) => virtualizer.measureElement(el))
2865
3273
  ),
2866
- paddingBottom > 0 && /* @__PURE__ */ jsx20("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx20(
3274
+ paddingBottom > 0 && /* @__PURE__ */ jsx27("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx27(
2867
3275
  "td",
2868
3276
  {
2869
3277
  colSpan: totalColumnCount,
@@ -2877,14 +3285,20 @@ function TreeDataGrid({
2877
3285
  }
2878
3286
  export {
2879
3287
  ActionsMenu,
3288
+ BooleanEditor,
2880
3289
  BooleanFilter,
2881
3290
  ColumnSelector,
2882
3291
  DataGrid,
3292
+ DateEditor,
2883
3293
  DateRangeFilter,
3294
+ EditFieldError,
3295
+ EnumEditor,
2884
3296
  EnumFilter,
2885
3297
  NumberComparisonFilter,
3298
+ NumberEditor,
2886
3299
  NumberHistogramFilter,
2887
3300
  NumberRangeFilter,
3301
+ StringEditor,
2888
3302
  StringFilter,
2889
3303
  TreeDataGrid,
2890
3304
  UNARY_OPERATORS,
@@ -2894,6 +3308,7 @@ export {
2894
3308
  countVisible,
2895
3309
  defaultAlign,
2896
3310
  defaultFormat,
3311
+ editErrorId,
2897
3312
  evaluateFilter,
2898
3313
  facetedNumberValues,
2899
3314
  fieldKey,
@@ -2902,13 +3317,16 @@ export {
2902
3317
  groupColumns,
2903
3318
  isColumnVisible,
2904
3319
  isCompositeFilterDescriptor,
3320
+ isEditable,
2905
3321
  isFilterDescriptor,
2906
3322
  isFilterable,
2907
3323
  isSortable,
2908
3324
  processClientData,
3325
+ renderDefaultEditWidget,
2909
3326
  renderDefaultFilterWidget,
2910
3327
  resolveMenuItems,
2911
3328
  storageKeyFor,
3329
+ toDate,
2912
3330
  useDebouncedCallback,
2913
3331
  useGridState,
2914
3332
  useTreeState