@bmsuisse/datagrid 0.29.0 → 0.30.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
@@ -1487,9 +1487,76 @@ function EnumEditor({
1487
1487
  ] });
1488
1488
  }
1489
1489
 
1490
+ // src/edit/MultilineStringEditor.tsx
1491
+ import { useLayoutEffect, useRef as useRef4 } from "react";
1492
+
1493
+ // src/components/ui/textarea.tsx
1494
+ import { forwardRef as forwardRef6 } from "react";
1495
+ import { jsx as jsx19 } from "react/jsx-runtime";
1496
+ var Textarea = forwardRef6(({ className, ...props }, ref) => {
1497
+ return /* @__PURE__ */ jsx19(
1498
+ "textarea",
1499
+ {
1500
+ className: cn(
1501
+ "flex min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
1502
+ className
1503
+ ),
1504
+ ref,
1505
+ ...props
1506
+ }
1507
+ );
1508
+ });
1509
+ Textarea.displayName = "Textarea";
1510
+
1511
+ // src/edit/MultilineStringEditor.tsx
1512
+ import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
1513
+ function MultilineStringEditor({
1514
+ column,
1515
+ rowId,
1516
+ value,
1517
+ onChange,
1518
+ error,
1519
+ autoFocus
1520
+ }) {
1521
+ const errorId = editErrorId(rowId, column.id);
1522
+ const textareaRef = useRef4(null);
1523
+ const selectionRef = useRef4(null);
1524
+ const text = value === null || value === void 0 ? "" : String(value);
1525
+ useLayoutEffect(() => {
1526
+ const el = textareaRef.current;
1527
+ if (!el) return;
1528
+ el.style.height = "auto";
1529
+ el.style.height = `${el.scrollHeight}px`;
1530
+ if (selectionRef.current) {
1531
+ if (document.activeElement !== el) el.focus();
1532
+ el.setSelectionRange(selectionRef.current.start, selectionRef.current.end);
1533
+ }
1534
+ }, [text]);
1535
+ return /* @__PURE__ */ jsxs12("div", { children: [
1536
+ /* @__PURE__ */ jsx20(
1537
+ Textarea,
1538
+ {
1539
+ ref: textareaRef,
1540
+ className: "resize-none overflow-hidden",
1541
+ "aria-label": `Edit ${column.header}`,
1542
+ "aria-invalid": error ? true : void 0,
1543
+ "aria-describedby": error ? errorId : void 0,
1544
+ "data-testid": `edit-${rowId}-${column.id}`,
1545
+ autoFocus,
1546
+ value: text,
1547
+ onChange: (event) => {
1548
+ selectionRef.current = { start: event.target.selectionStart ?? 0, end: event.target.selectionEnd ?? 0 };
1549
+ onChange(event.target.value);
1550
+ }
1551
+ }
1552
+ ),
1553
+ error && /* @__PURE__ */ jsx20(EditFieldError, { id: errorId, message: error })
1554
+ ] });
1555
+ }
1556
+
1490
1557
  // src/edit/NumberEditor.tsx
1491
- import { useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
1492
- import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
1558
+ import { useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
1559
+ import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
1493
1560
  function textFromValue(value) {
1494
1561
  return value === null || value === void 0 ? "" : String(value);
1495
1562
  }
@@ -1503,14 +1570,14 @@ function NumberEditor({
1503
1570
  }) {
1504
1571
  const errorId = editErrorId(rowId, column.id);
1505
1572
  const [text, setText] = useState5(() => textFromValue(value));
1506
- const lastEmittedRef = useRef4(value);
1573
+ const lastEmittedRef = useRef5(value);
1507
1574
  useEffect4(() => {
1508
1575
  if (value === lastEmittedRef.current) return;
1509
1576
  lastEmittedRef.current = value;
1510
1577
  setText(textFromValue(value));
1511
1578
  }, [value]);
1512
- return /* @__PURE__ */ jsxs12("div", { children: [
1513
- /* @__PURE__ */ jsx19(
1579
+ return /* @__PURE__ */ jsxs13("div", { children: [
1580
+ /* @__PURE__ */ jsx21(
1514
1581
  Input,
1515
1582
  {
1516
1583
  type: "number",
@@ -1529,12 +1596,12 @@ function NumberEditor({
1529
1596
  }
1530
1597
  }
1531
1598
  ),
1532
- error && /* @__PURE__ */ jsx19(EditFieldError, { id: errorId, message: error })
1599
+ error && /* @__PURE__ */ jsx21(EditFieldError, { id: errorId, message: error })
1533
1600
  ] });
1534
1601
  }
1535
1602
 
1536
1603
  // src/edit/StringEditor.tsx
1537
- import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
1604
+ import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
1538
1605
  function StringEditor({
1539
1606
  column,
1540
1607
  rowId,
@@ -1544,8 +1611,8 @@ function StringEditor({
1544
1611
  autoFocus
1545
1612
  }) {
1546
1613
  const errorId = editErrorId(rowId, column.id);
1547
- return /* @__PURE__ */ jsxs13("div", { children: [
1548
- /* @__PURE__ */ jsx20(
1614
+ return /* @__PURE__ */ jsxs14("div", { children: [
1615
+ /* @__PURE__ */ jsx22(
1549
1616
  Input,
1550
1617
  {
1551
1618
  className: "h-8",
@@ -1558,31 +1625,31 @@ function StringEditor({
1558
1625
  onChange: (event) => onChange(event.target.value)
1559
1626
  }
1560
1627
  ),
1561
- error && /* @__PURE__ */ jsx20(EditFieldError, { id: errorId, message: error })
1628
+ error && /* @__PURE__ */ jsx22(EditFieldError, { id: errorId, message: error })
1562
1629
  ] });
1563
1630
  }
1564
1631
 
1565
1632
  // src/edit/registry.tsx
1566
- import { jsx as jsx21 } from "react/jsx-runtime";
1633
+ import { jsx as jsx23 } from "react/jsx-runtime";
1567
1634
  function renderDefaultEditWidget(column, rowId, value, onChange, error, autoFocus) {
1568
1635
  switch (column.type) {
1569
1636
  case "string":
1570
- return /* @__PURE__ */ jsx21(StringEditor, { column, rowId, value, onChange, error, autoFocus });
1637
+ return column.multiline ? /* @__PURE__ */ jsx23(MultilineStringEditor, { column, rowId, value, onChange, error, autoFocus }) : /* @__PURE__ */ jsx23(StringEditor, { column, rowId, value, onChange, error, autoFocus });
1571
1638
  case "enum":
1572
- return /* @__PURE__ */ jsx21(EnumEditor, { column, rowId, value, onChange, error, autoFocus });
1639
+ return /* @__PURE__ */ jsx23(EnumEditor, { column, rowId, value, onChange, error, autoFocus });
1573
1640
  case "boolean":
1574
- return /* @__PURE__ */ jsx21(BooleanEditor, { column, rowId, value, onChange, error, autoFocus });
1641
+ return /* @__PURE__ */ jsx23(BooleanEditor, { column, rowId, value, onChange, error, autoFocus });
1575
1642
  case "number":
1576
1643
  case "currency":
1577
- return /* @__PURE__ */ jsx21(NumberEditor, { column, rowId, value, onChange, error, autoFocus });
1644
+ return /* @__PURE__ */ jsx23(NumberEditor, { column, rowId, value, onChange, error, autoFocus });
1578
1645
  case "date":
1579
1646
  case "datetime":
1580
- return /* @__PURE__ */ jsx21(DateEditor, { column, rowId, value, onChange, error, autoFocus });
1647
+ return /* @__PURE__ */ jsx23(DateEditor, { column, rowId, value, onChange, error, autoFocus });
1581
1648
  }
1582
1649
  }
1583
1650
 
1584
1651
  // src/edit/editingState.ts
1585
- import { useEffect as useEffect5, useRef as useRef5, useState as useState6 } from "react";
1652
+ import { useEffect as useEffect5, useRef as useRef6, useState as useState6 } from "react";
1586
1653
  function editValuesEqual(a, b) {
1587
1654
  if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
1588
1655
  return a === b;
@@ -1684,13 +1751,13 @@ function useEditingState(editing, getRowId) {
1684
1751
  editing?.onDiscard?.();
1685
1752
  }
1686
1753
  const ctx = editing ? { pendingEdits, editErrors, activeRowId, autoFocusTarget, getRowId, onEdit: commitEdit, onActivateRow: activateRow } : void 0;
1687
- const ctxRef = useRef5(void 0);
1754
+ const ctxRef = useRef6(void 0);
1688
1755
  ctxRef.current = ctx;
1689
1756
  return { pendingEdits, editErrors, hasEditErrors, ctx, ctxRef, handleSaveEdits, handleDiscardEdits };
1690
1757
  }
1691
1758
 
1692
1759
  // src/edit/renderEditableCell.tsx
1693
- import { jsx as jsx22 } from "react/jsx-runtime";
1760
+ import { jsx as jsx24 } from "react/jsx-runtime";
1694
1761
  function renderEditableCell(column, row, rawValue, ctx) {
1695
1762
  const staticContent = (value2) => column.cell ? column.cell(value2, row) : defaultFormat(column, value2);
1696
1763
  if (!ctx || !isEditable(column, row)) return staticContent(rawValue);
@@ -1702,7 +1769,7 @@ function renderEditableCell(column, row, rawValue, ctx) {
1702
1769
  const autoFocus = ctx.autoFocusTarget?.rowId === rowId && ctx.autoFocusTarget.columnId === column.id;
1703
1770
  const onChange = (next) => ctx.onEdit(column, row, next);
1704
1771
  const editor = column.renderEditCell ? column.renderEditCell(value, row, onChange, error, autoFocus) : renderDefaultEditWidget(column, rowId, value, onChange, error, autoFocus);
1705
- return /* @__PURE__ */ jsx22(
1772
+ return /* @__PURE__ */ jsx24(
1706
1773
  "div",
1707
1774
  {
1708
1775
  onKeyDown: (event) => {
@@ -1715,7 +1782,7 @@ function renderEditableCell(column, row, rawValue, ctx) {
1715
1782
  }
1716
1783
  );
1717
1784
  }
1718
- return /* @__PURE__ */ jsx22(
1785
+ return /* @__PURE__ */ jsx24(
1719
1786
  "span",
1720
1787
  {
1721
1788
  role: "button",
@@ -1738,7 +1805,7 @@ function renderEditableCell(column, row, rawValue, ctx) {
1738
1805
  }
1739
1806
 
1740
1807
  // src/edit/EditingBar.tsx
1741
- import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
1808
+ import { jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
1742
1809
  function resolveEditLabel(label, changedRowCount, fallback) {
1743
1810
  if (label === void 0) return fallback(changedRowCount);
1744
1811
  return typeof label === "function" ? label(changedRowCount) : label;
@@ -1758,25 +1825,25 @@ function EditingBar({
1758
1825
  testIdPrefix = "datagrid"
1759
1826
  }) {
1760
1827
  const hasEditErrors = editErrors.size > 0;
1761
- return /* @__PURE__ */ jsxs14(
1828
+ return /* @__PURE__ */ jsxs15(
1762
1829
  "div",
1763
1830
  {
1764
1831
  "data-testid": `${testIdPrefix}-edit-bar`,
1765
1832
  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
1833
  children: [
1767
- /* @__PURE__ */ jsxs14("span", { "aria-live": "polite", children: [
1834
+ /* @__PURE__ */ jsxs15("span", { "aria-live": "polite", children: [
1768
1835
  pendingRowCount,
1769
1836
  " row",
1770
1837
  pendingRowCount === 1 ? "" : "s",
1771
1838
  " changed",
1772
- hasEditErrors && /* @__PURE__ */ jsxs14("span", { className: "ml-2 text-destructive", children: [
1839
+ hasEditErrors && /* @__PURE__ */ jsxs15("span", { className: "ml-2 text-destructive", children: [
1773
1840
  "Fix the highlighted errors before saving (",
1774
1841
  describeErrorRows(editErrors),
1775
1842
  ")."
1776
1843
  ] })
1777
1844
  ] }),
1778
- /* @__PURE__ */ jsxs14("div", { className: "flex gap-2", children: [
1779
- /* @__PURE__ */ jsx23(
1845
+ /* @__PURE__ */ jsxs15("div", { className: "flex gap-2", children: [
1846
+ /* @__PURE__ */ jsx25(
1780
1847
  Button,
1781
1848
  {
1782
1849
  type: "button",
@@ -1788,7 +1855,7 @@ function EditingBar({
1788
1855
  children: resolveEditLabel(editing.discardLabel, pendingRowCount, () => "Discard")
1789
1856
  }
1790
1857
  ),
1791
- /* @__PURE__ */ jsx23(
1858
+ /* @__PURE__ */ jsx25(
1792
1859
  Button,
1793
1860
  {
1794
1861
  type: "button",
@@ -1809,6 +1876,633 @@ function EditingBar({
1809
1876
  );
1810
1877
  }
1811
1878
 
1879
+ // src/cell-editing/rangeUtils.ts
1880
+ function buildIndexMap(ids) {
1881
+ const map = /* @__PURE__ */ new Map();
1882
+ ids.forEach((id, index) => map.set(id, index));
1883
+ return map;
1884
+ }
1885
+ function normalizeRange(range, rowIndex, columnIndex) {
1886
+ const anchorRow = rowIndex.get(range.anchor.rowId);
1887
+ const focusRow = rowIndex.get(range.focus.rowId);
1888
+ const anchorCol = columnIndex.get(range.anchor.columnId);
1889
+ const focusCol = columnIndex.get(range.focus.columnId);
1890
+ if (anchorRow === void 0 || focusRow === void 0 || anchorCol === void 0 || focusCol === void 0) {
1891
+ return void 0;
1892
+ }
1893
+ return {
1894
+ rowStart: Math.min(anchorRow, focusRow),
1895
+ rowEnd: Math.max(anchorRow, focusRow),
1896
+ colStart: Math.min(anchorCol, focusCol),
1897
+ colEnd: Math.max(anchorCol, focusCol)
1898
+ };
1899
+ }
1900
+ function isCellInRange(cell, range, rowIndex, columnIndex) {
1901
+ const normalized = normalizeRange(range, rowIndex, columnIndex);
1902
+ if (!normalized) return false;
1903
+ const row = rowIndex.get(cell.rowId);
1904
+ const col = columnIndex.get(cell.columnId);
1905
+ if (row === void 0 || col === void 0) return false;
1906
+ return row >= normalized.rowStart && row <= normalized.rowEnd && col >= normalized.colStart && col <= normalized.colEnd;
1907
+ }
1908
+ function cellsInRange(range, rowIds, columnIds, rowIndex, columnIndex) {
1909
+ const normalized = normalizeRange(range, rowIndex, columnIndex);
1910
+ if (!normalized) return [];
1911
+ const cells = [];
1912
+ for (let r = normalized.rowStart; r <= normalized.rowEnd; r++) {
1913
+ const rowId = rowIds[r];
1914
+ if (rowId === void 0) continue;
1915
+ for (let c = normalized.colStart; c <= normalized.colEnd; c++) {
1916
+ const columnId = columnIds[c];
1917
+ if (columnId === void 0) continue;
1918
+ cells.push({ rowId, columnId });
1919
+ }
1920
+ }
1921
+ return cells;
1922
+ }
1923
+ function extendRangeForFill(base, target) {
1924
+ const rowOvershoot = Math.max(0, base.rowStart - target.row, target.row - base.rowEnd);
1925
+ const colOvershoot = Math.max(0, base.colStart - target.col, target.col - base.colEnd);
1926
+ if (rowOvershoot === 0 && colOvershoot === 0) return base;
1927
+ if (rowOvershoot >= colOvershoot) {
1928
+ return {
1929
+ rowStart: Math.min(base.rowStart, target.row),
1930
+ rowEnd: Math.max(base.rowEnd, target.row),
1931
+ colStart: base.colStart,
1932
+ colEnd: base.colEnd
1933
+ };
1934
+ }
1935
+ return {
1936
+ rowStart: base.rowStart,
1937
+ rowEnd: base.rowEnd,
1938
+ colStart: Math.min(base.colStart, target.col),
1939
+ colEnd: Math.max(base.colEnd, target.col)
1940
+ };
1941
+ }
1942
+ function extendRangeForFillToCellRange(base, target, rowIds, columnIds, rowIndex, columnIndex) {
1943
+ const normalizedBase = normalizeRange(base, rowIndex, columnIndex);
1944
+ if (!normalizedBase) return void 0;
1945
+ const targetRow = rowIndex.get(target.rowId);
1946
+ const targetCol = columnIndex.get(target.columnId);
1947
+ if (targetRow === void 0 || targetCol === void 0) return void 0;
1948
+ const extended = extendRangeForFill(normalizedBase, { row: targetRow, col: targetCol });
1949
+ const startRowId = rowIds[extended.rowStart];
1950
+ const endRowId = rowIds[extended.rowEnd];
1951
+ const startColumnId = columnIds[extended.colStart];
1952
+ const endColumnId = columnIds[extended.colEnd];
1953
+ if (startRowId === void 0 || endRowId === void 0 || startColumnId === void 0 || endColumnId === void 0) {
1954
+ return void 0;
1955
+ }
1956
+ return { anchor: { rowId: startRowId, columnId: startColumnId }, focus: { rowId: endRowId, columnId: endColumnId } };
1957
+ }
1958
+
1959
+ // src/cell-editing/useCellSelection.ts
1960
+ import { useCallback as useCallback2, useMemo as useMemo3, useRef as useRef7, useState as useState7 } from "react";
1961
+ function useCellSelection({ rowIds, columnIds, onNavigateToRow }) {
1962
+ const [selection, setSelectionState] = useState7(void 0);
1963
+ const [isDragging, setIsDragging] = useState7(false);
1964
+ const [dragFocus, setDragFocus] = useState7(void 0);
1965
+ const draggingRef = useRef7(false);
1966
+ const dragFocusRef = useRef7(void 0);
1967
+ const rowIndex = useMemo3(() => buildIndexMap(rowIds), [rowIds]);
1968
+ const columnIndex = useMemo3(() => buildIndexMap(columnIds), [columnIds]);
1969
+ const startSelection = useCallback2((cell, options) => {
1970
+ setSelectionState((prev) => options?.extend && prev ? { anchor: prev.anchor, focus: cell } : { anchor: cell, focus: cell });
1971
+ draggingRef.current = true;
1972
+ setIsDragging(true);
1973
+ dragFocusRef.current = void 0;
1974
+ setDragFocus(void 0);
1975
+ }, []);
1976
+ const updateDrag = useCallback2((cell) => {
1977
+ if (!draggingRef.current) return;
1978
+ dragFocusRef.current = cell;
1979
+ setDragFocus(cell);
1980
+ }, []);
1981
+ const endDrag = useCallback2(() => {
1982
+ if (!draggingRef.current) return;
1983
+ draggingRef.current = false;
1984
+ setIsDragging(false);
1985
+ const finalFocus = dragFocusRef.current;
1986
+ dragFocusRef.current = void 0;
1987
+ setDragFocus(void 0);
1988
+ if (finalFocus) {
1989
+ setSelectionState((prev) => prev ? { anchor: prev.anchor, focus: finalFocus } : prev);
1990
+ }
1991
+ }, []);
1992
+ const moveSelection = useCallback2(
1993
+ (direction, options) => {
1994
+ const from = selection?.focus ?? { rowId: rowIds[0] ?? "", columnId: columnIds[0] ?? "" };
1995
+ const rowIdx = rowIndex.get(from.rowId);
1996
+ const colIdx = columnIndex.get(from.columnId);
1997
+ if (rowIdx === void 0 || colIdx === void 0 || rowIds.length === 0 || columnIds.length === 0) return;
1998
+ let nextRowIdx = rowIdx;
1999
+ let nextColIdx = colIdx;
2000
+ if (direction === "up") nextRowIdx = Math.max(0, rowIdx - 1);
2001
+ else if (direction === "down") nextRowIdx = Math.min(rowIds.length - 1, rowIdx + 1);
2002
+ else if (direction === "left") nextColIdx = Math.max(0, colIdx - 1);
2003
+ else nextColIdx = Math.min(columnIds.length - 1, colIdx + 1);
2004
+ const nextRowId = rowIds[nextRowIdx];
2005
+ const nextColumnId = columnIds[nextColIdx];
2006
+ if (nextRowId !== from.rowId) onNavigateToRow?.(nextRowId);
2007
+ const nextCell = { rowId: nextRowId, columnId: nextColumnId };
2008
+ const anchor = options?.extend ? selection?.anchor ?? nextCell : nextCell;
2009
+ setSelectionState({ anchor, focus: nextCell });
2010
+ },
2011
+ [selection, rowIds, columnIds, rowIndex, columnIndex, onNavigateToRow]
2012
+ );
2013
+ const setSelection = useCallback2((range) => {
2014
+ setSelectionState(range);
2015
+ }, []);
2016
+ const clearSelection = useCallback2(() => {
2017
+ draggingRef.current = false;
2018
+ setIsDragging(false);
2019
+ setDragFocus(void 0);
2020
+ setSelectionState(void 0);
2021
+ }, []);
2022
+ const effectiveRange = useMemo3(() => {
2023
+ if (!selection) return void 0;
2024
+ if (isDragging && dragFocus) return { anchor: selection.anchor, focus: dragFocus };
2025
+ return selection;
2026
+ }, [selection, isDragging, dragFocus]);
2027
+ const [isFillDragging, setIsFillDragging] = useState7(false);
2028
+ const [fillTarget, setFillTarget] = useState7(void 0);
2029
+ const fillDraggingRef = useRef7(false);
2030
+ const fillTargetRef = useRef7(void 0);
2031
+ const fillBaseRef = useRef7(void 0);
2032
+ const startFillDrag = useCallback2(() => {
2033
+ if (!selection) return;
2034
+ fillBaseRef.current = selection;
2035
+ fillDraggingRef.current = true;
2036
+ setIsFillDragging(true);
2037
+ }, [selection]);
2038
+ const updateFillDrag = useCallback2((cell) => {
2039
+ if (!fillDraggingRef.current) return;
2040
+ fillTargetRef.current = cell;
2041
+ setFillTarget(cell);
2042
+ }, []);
2043
+ const endFillDrag = useCallback2(() => {
2044
+ if (!fillDraggingRef.current) return void 0;
2045
+ fillDraggingRef.current = false;
2046
+ setIsFillDragging(false);
2047
+ const sourceRange = fillBaseRef.current;
2048
+ const target = fillTargetRef.current;
2049
+ fillBaseRef.current = void 0;
2050
+ fillTargetRef.current = void 0;
2051
+ setFillTarget(void 0);
2052
+ if (!sourceRange || !target) return void 0;
2053
+ const finalRange = extendRangeForFillToCellRange(sourceRange, target, rowIds, columnIds, rowIndex, columnIndex);
2054
+ if (!finalRange) return void 0;
2055
+ setSelectionState(finalRange);
2056
+ return { sourceRange, finalRange };
2057
+ }, [rowIds, columnIds, rowIndex, columnIndex]);
2058
+ const fillPreviewRange = useMemo3(() => {
2059
+ if (!isFillDragging || !selection || !fillTarget) return void 0;
2060
+ return extendRangeForFillToCellRange(selection, fillTarget, rowIds, columnIds, rowIndex, columnIndex);
2061
+ }, [isFillDragging, selection, fillTarget, rowIds, columnIds, rowIndex, columnIndex]);
2062
+ return {
2063
+ selection,
2064
+ isDragging,
2065
+ effectiveRange,
2066
+ startSelection,
2067
+ updateDrag,
2068
+ endDrag,
2069
+ moveSelection,
2070
+ setSelection,
2071
+ clearSelection,
2072
+ isFillDragging,
2073
+ fillPreviewRange,
2074
+ startFillDrag,
2075
+ updateFillDrag,
2076
+ endFillDrag
2077
+ };
2078
+ }
2079
+
2080
+ // src/cell-editing/useCellEditingState.ts
2081
+ import { useRef as useRef8, useState as useState8 } from "react";
2082
+ function cellKey(rowId, columnId) {
2083
+ return `${rowId}:${columnId}`;
2084
+ }
2085
+ function useCellEditingState(cellEditing, getRowId) {
2086
+ const [editingCell, setEditingCell] = useState8(void 0);
2087
+ const [initialText, setInitialText] = useState8(void 0);
2088
+ const [hasDraft, setHasDraft] = useState8(false);
2089
+ const [draftValue, setDraftValue] = useState8(void 0);
2090
+ const [overrides, setOverrides] = useState8(/* @__PURE__ */ new Map());
2091
+ const [errors, setErrors] = useState8(/* @__PURE__ */ new Map());
2092
+ const justCommittedRef = useRef8(false);
2093
+ function closeEditor() {
2094
+ setEditingCell(void 0);
2095
+ setInitialText(void 0);
2096
+ setHasDraft(false);
2097
+ setDraftValue(void 0);
2098
+ }
2099
+ function applyChanges(changes) {
2100
+ if (!cellEditing || cellEditing.disabled || changes.length === 0) return;
2101
+ setOverrides((prev) => {
2102
+ const next = new Map(prev);
2103
+ for (const change of changes) {
2104
+ next.set(cellKey(change.rowId, change.columnId), { value: change.value, previousValue: change.previousValue });
2105
+ }
2106
+ return next;
2107
+ });
2108
+ setErrors((prev) => {
2109
+ if (prev.size === 0) return prev;
2110
+ const next = new Map(prev);
2111
+ for (const change of changes) next.delete(cellKey(change.rowId, change.columnId));
2112
+ return next.size === prev.size ? prev : next;
2113
+ });
2114
+ void cellEditing.onCellsChange(changes);
2115
+ }
2116
+ function onBeginEdit(cell, text) {
2117
+ if (!cellEditing || cellEditing.disabled) return;
2118
+ setEditingCell(cell);
2119
+ setInitialText(text);
2120
+ setHasDraft(text !== void 0);
2121
+ setDraftValue(text);
2122
+ }
2123
+ function onChangeDraft(value) {
2124
+ setHasDraft(true);
2125
+ setDraftValue(value);
2126
+ }
2127
+ function onCommitEdit(row, previousValue) {
2128
+ const cell = editingCell;
2129
+ const shouldCommit = hasDraft;
2130
+ const value = draftValue;
2131
+ closeEditor();
2132
+ if (!cell || !shouldCommit) return;
2133
+ justCommittedRef.current = true;
2134
+ applyChanges([{ rowId: cell.rowId, row, columnId: cell.columnId, previousValue, value }]);
2135
+ }
2136
+ function onCommitValue(row, previousValue, value) {
2137
+ const cell = editingCell;
2138
+ closeEditor();
2139
+ if (!cell) return;
2140
+ justCommittedRef.current = true;
2141
+ applyChanges([{ rowId: cell.rowId, row, columnId: cell.columnId, previousValue, value }]);
2142
+ }
2143
+ function consumeJustCommitted() {
2144
+ const value = justCommittedRef.current;
2145
+ justCommittedRef.current = false;
2146
+ return value;
2147
+ }
2148
+ function onSetError(rowId, columnId, message) {
2149
+ setErrors((prev) => {
2150
+ const key = cellKey(rowId, columnId);
2151
+ if (message === void 0) {
2152
+ if (!prev.has(key)) return prev;
2153
+ const next2 = new Map(prev);
2154
+ next2.delete(key);
2155
+ return next2;
2156
+ }
2157
+ const next = new Map(prev);
2158
+ next.set(key, message);
2159
+ return next;
2160
+ });
2161
+ }
2162
+ function getError(rowId, columnId) {
2163
+ return errors.get(cellKey(rowId, columnId));
2164
+ }
2165
+ function resolveValue(rowId, columnId, rawValue) {
2166
+ const override = overrides.get(cellKey(rowId, columnId));
2167
+ if (!override) return rawValue;
2168
+ if (rawValue !== override.previousValue) return rawValue;
2169
+ return override.value;
2170
+ }
2171
+ const ctx = cellEditing ? {
2172
+ editingCell,
2173
+ initialText,
2174
+ hasDraft,
2175
+ draftValue,
2176
+ getRowId,
2177
+ getError,
2178
+ resolveValue,
2179
+ onBeginEdit,
2180
+ onCancelEdit: closeEditor,
2181
+ onChangeDraft,
2182
+ onCommitEdit,
2183
+ onCommitValue,
2184
+ onSetError,
2185
+ consumeJustCommitted
2186
+ } : void 0;
2187
+ const ctxRef = useRef8(void 0);
2188
+ ctxRef.current = ctx;
2189
+ return { ctx, ctxRef, applyChanges };
2190
+ }
2191
+
2192
+ // src/cell-editing/renderCellModeCell.tsx
2193
+ import { jsx as jsx26 } from "react/jsx-runtime";
2194
+ function isAtomicEditorType(type) {
2195
+ return type === "enum" || type === "boolean" || type === "date" || type === "datetime";
2196
+ }
2197
+ function renderCellModeCell(column, row, rawValue, ctx) {
2198
+ const staticContent = (value2) => column.cell ? column.cell(value2, row) : defaultFormat(column, value2);
2199
+ if (!ctx) return staticContent(rawValue);
2200
+ const rowId = ctx.getRowId(row);
2201
+ const value = ctx.resolveValue(rowId, column.id, rawValue);
2202
+ const isThisCellEditing = ctx.editingCell?.rowId === rowId && ctx.editingCell.columnId === column.id && isEditable(column, row);
2203
+ if (!isThisCellEditing) {
2204
+ return /* @__PURE__ */ jsx26("span", { "data-testid": `cell-${rowId}-${column.id}`, className: "block w-full", children: staticContent(value) });
2205
+ }
2206
+ const isAtomic = isAtomicEditorType(column.type);
2207
+ const error = ctx.getError(rowId, column.id);
2208
+ const startValue = ctx.initialText !== void 0 ? ctx.initialText : value;
2209
+ const currentValue = ctx.hasDraft ? ctx.draftValue : startValue;
2210
+ function validate(candidate) {
2211
+ return column.validateEdit?.(candidate, row);
2212
+ }
2213
+ const onChange = (next) => {
2214
+ const message = validate(next);
2215
+ ctx.onSetError(rowId, column.id, message);
2216
+ if (isAtomic && !message) {
2217
+ ctx.onCommitValue(row, value, next);
2218
+ return;
2219
+ }
2220
+ ctx.onChangeDraft(next);
2221
+ };
2222
+ const editor = column.renderEditCell ? column.renderEditCell(currentValue, row, onChange, error, true) : renderDefaultEditWidget(column, rowId, currentValue, onChange, error, true);
2223
+ function handleKeyDown(event) {
2224
+ if (event.key === "Escape") {
2225
+ event.stopPropagation();
2226
+ ctx.onCancelEdit();
2227
+ return;
2228
+ }
2229
+ const isMultilineNewline = column.type === "string" && column.multiline && event.key === "Enter" && event.shiftKey;
2230
+ if (isMultilineNewline) return;
2231
+ if (isAtomic || event.key !== "Enter" && event.key !== "Tab") return;
2232
+ const message = validate(currentValue);
2233
+ if (message) {
2234
+ event.stopPropagation();
2235
+ return;
2236
+ }
2237
+ ctx.onCommitEdit(row, value);
2238
+ }
2239
+ function handleBlur() {
2240
+ if (isAtomic) return;
2241
+ if (validate(currentValue)) ctx.onCancelEdit();
2242
+ else ctx.onCommitEdit(row, value);
2243
+ }
2244
+ return /* @__PURE__ */ jsx26("div", { onKeyDown: handleKeyDown, onBlur: handleBlur, children: editor });
2245
+ }
2246
+
2247
+ // src/cell-editing/SelectionOverlay.tsx
2248
+ import { useLayoutEffect as useLayoutEffect2, useState as useState9 } from "react";
2249
+ import { Fragment as Fragment2, jsx as jsx27, jsxs as jsxs16 } from "react/jsx-runtime";
2250
+ function cellSelector(rowId, columnId) {
2251
+ const escape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
2252
+ return `[data-cell-row="${escape(rowId)}"][data-cell-col="${escape(columnId)}"]`;
2253
+ }
2254
+ function computeRangeRect(container, range) {
2255
+ const anchorEl = container.querySelector(cellSelector(range.anchor.rowId, range.anchor.columnId));
2256
+ const focusEl = container.querySelector(cellSelector(range.focus.rowId, range.focus.columnId));
2257
+ if (!anchorEl || !focusEl) return void 0;
2258
+ const containerRect = container.getBoundingClientRect();
2259
+ const a = anchorEl.getBoundingClientRect();
2260
+ const f = focusEl.getBoundingClientRect();
2261
+ const left = Math.min(a.left, f.left) - containerRect.left + container.scrollLeft;
2262
+ const top = Math.min(a.top, f.top) - containerRect.top + container.scrollTop;
2263
+ const right = Math.max(a.right, f.right) - containerRect.left + container.scrollLeft;
2264
+ const bottom = Math.max(a.bottom, f.bottom) - containerRect.top + container.scrollTop;
2265
+ return { top, left, width: right - left, height: bottom - top };
2266
+ }
2267
+ function rectsEqual(a, b) {
2268
+ if (a === b) return true;
2269
+ if (!a || !b) return false;
2270
+ return a.top === b.top && a.left === b.left && a.width === b.width && a.height === b.height;
2271
+ }
2272
+ var HANDLE_SIZE = 8;
2273
+ function SelectionOverlay({
2274
+ containerRef,
2275
+ range,
2276
+ fillPreviewRange,
2277
+ onFillHandleMouseDown
2278
+ }) {
2279
+ const [rect, setRect] = useState9(void 0);
2280
+ const [previewRect, setPreviewRect] = useState9(void 0);
2281
+ useLayoutEffect2(() => {
2282
+ const container = containerRef.current;
2283
+ const nextRect = range && container ? computeRangeRect(container, range) : void 0;
2284
+ setRect((prev) => rectsEqual(prev, nextRect) ? prev : nextRect);
2285
+ const nextPreviewRect = fillPreviewRange && container ? computeRangeRect(container, fillPreviewRange) : void 0;
2286
+ setPreviewRect((prev) => rectsEqual(prev, nextPreviewRect) ? prev : nextPreviewRect);
2287
+ });
2288
+ if (!rect) return null;
2289
+ return /* @__PURE__ */ jsxs16(Fragment2, { children: [
2290
+ /* @__PURE__ */ jsx27(
2291
+ "div",
2292
+ {
2293
+ "data-testid": "cell-selection-overlay",
2294
+ "aria-hidden": true,
2295
+ className: "pointer-events-none absolute z-20 border-2 border-primary bg-primary/10",
2296
+ style: { top: rect.top, left: rect.left, width: rect.width, height: rect.height }
2297
+ }
2298
+ ),
2299
+ previewRect && /* @__PURE__ */ jsx27(
2300
+ "div",
2301
+ {
2302
+ "data-testid": "cell-fill-preview-overlay",
2303
+ "aria-hidden": true,
2304
+ className: "pointer-events-none absolute z-20 border-2 border-dashed border-primary",
2305
+ style: { top: previewRect.top, left: previewRect.left, width: previewRect.width, height: previewRect.height }
2306
+ }
2307
+ ),
2308
+ onFillHandleMouseDown && /* @__PURE__ */ jsx27(
2309
+ "div",
2310
+ {
2311
+ "data-testid": "cell-fill-handle",
2312
+ role: "button",
2313
+ "aria-label": "Fill handle: drag to fill adjacent cells",
2314
+ className: "absolute z-30 cursor-crosshair border border-background bg-primary",
2315
+ style: {
2316
+ top: rect.top + rect.height - HANDLE_SIZE / 2,
2317
+ left: rect.left + rect.width - HANDLE_SIZE / 2,
2318
+ width: HANDLE_SIZE,
2319
+ height: HANDLE_SIZE
2320
+ },
2321
+ onMouseDown: (event) => {
2322
+ event.stopPropagation();
2323
+ onFillHandleMouseDown(event);
2324
+ }
2325
+ }
2326
+ )
2327
+ ] });
2328
+ }
2329
+
2330
+ // src/cell-editing/coerce.ts
2331
+ function parseBoolean(raw) {
2332
+ const normalized = raw.trim().toLowerCase();
2333
+ if (normalized === "true" || normalized === "yes" || normalized === "1") return true;
2334
+ if (normalized === "false" || normalized === "no" || normalized === "0") return false;
2335
+ return void 0;
2336
+ }
2337
+ function matchEnumOption(options, raw) {
2338
+ const trimmed = raw.trim();
2339
+ const byValue = options.find((option) => option.value === trimmed);
2340
+ if (byValue) return byValue.value;
2341
+ const lowered = trimmed.toLowerCase();
2342
+ return options.find((option) => option.label.toLowerCase() === lowered)?.value;
2343
+ }
2344
+ function coerceValueForColumn(column, raw) {
2345
+ switch (column.type) {
2346
+ case "string":
2347
+ return { value: raw };
2348
+ case "number":
2349
+ case "currency": {
2350
+ if (raw.trim() === "") return { value: null };
2351
+ const num = toNumber(raw);
2352
+ return num === null ? void 0 : { value: num };
2353
+ }
2354
+ case "boolean": {
2355
+ const bool = parseBoolean(raw);
2356
+ return bool === void 0 ? void 0 : { value: bool };
2357
+ }
2358
+ case "date":
2359
+ case "datetime": {
2360
+ const date = toDate(raw);
2361
+ return date === null ? void 0 : { value: date };
2362
+ }
2363
+ case "enum": {
2364
+ const matched = matchEnumOption(column.options, raw);
2365
+ return matched === void 0 ? void 0 : { value: matched };
2366
+ }
2367
+ }
2368
+ }
2369
+
2370
+ // src/cell-editing/clipboard.ts
2371
+ function quoteTsvField(field) {
2372
+ if (!/[\t\n\r"]/.test(field)) return field;
2373
+ return `"${field.replace(/"/g, '""')}"`;
2374
+ }
2375
+ function rangeToTsv(range, rows, rowIds, columns, columnIds, getRowId) {
2376
+ const rowIndex = buildIndexMap(rowIds);
2377
+ const columnIndex = buildIndexMap(columnIds);
2378
+ const normalized = normalizeRange(range, rowIndex, columnIndex);
2379
+ if (!normalized) return "";
2380
+ const columnById = new Map(columns.map((column) => [column.id, column]));
2381
+ const rowById = new Map(rows.map((row) => [getRowId(row), row]));
2382
+ const lines = [];
2383
+ for (let r = normalized.rowStart; r <= normalized.rowEnd; r++) {
2384
+ const rowId = rowIds[r];
2385
+ const row = rowId !== void 0 ? rowById.get(rowId) : void 0;
2386
+ const fields = [];
2387
+ for (let c = normalized.colStart; c <= normalized.colEnd; c++) {
2388
+ const columnId = columnIds[c];
2389
+ const column = columnId !== void 0 ? columnById.get(columnId) : void 0;
2390
+ if (!column || row === void 0) {
2391
+ fields.push("");
2392
+ continue;
2393
+ }
2394
+ const value = getColumnValue(column, row);
2395
+ const text = column.formatForClipboard ? column.formatForClipboard(value, row) : defaultFormat(column, value);
2396
+ fields.push(quoteTsvField(text));
2397
+ }
2398
+ lines.push(fields.join(" "));
2399
+ }
2400
+ return lines.join("\n");
2401
+ }
2402
+ function parseTsv(text) {
2403
+ const normalized = text.replace(/\r\n/g, "\n");
2404
+ const rows = [];
2405
+ let row = [];
2406
+ let field = "";
2407
+ let inQuotes = false;
2408
+ let i = 0;
2409
+ while (i < normalized.length) {
2410
+ const ch = normalized[i];
2411
+ if (inQuotes) {
2412
+ if (ch === '"') {
2413
+ if (normalized[i + 1] === '"') {
2414
+ field += '"';
2415
+ i += 2;
2416
+ continue;
2417
+ }
2418
+ inQuotes = false;
2419
+ i++;
2420
+ continue;
2421
+ }
2422
+ field += ch;
2423
+ i++;
2424
+ continue;
2425
+ }
2426
+ if (ch === '"' && field === "") {
2427
+ inQuotes = true;
2428
+ i++;
2429
+ continue;
2430
+ }
2431
+ if (ch === " ") {
2432
+ row.push(field);
2433
+ field = "";
2434
+ i++;
2435
+ continue;
2436
+ }
2437
+ if (ch === "\n") {
2438
+ row.push(field);
2439
+ rows.push(row);
2440
+ row = [];
2441
+ field = "";
2442
+ i++;
2443
+ continue;
2444
+ }
2445
+ field += ch;
2446
+ i++;
2447
+ }
2448
+ row.push(field);
2449
+ rows.push(row);
2450
+ const last = rows[rows.length - 1];
2451
+ if (rows.length > 1 && normalized.endsWith("\n") && last?.length === 1 && last[0] === "") {
2452
+ rows.pop();
2453
+ }
2454
+ return rows;
2455
+ }
2456
+
2457
+ // src/cell-editing/fillHandle.ts
2458
+ function computeFillChanges(sourceRange, finalRange, rowIds, columnIds, columns, getRow) {
2459
+ const rowIndex = buildIndexMap(rowIds);
2460
+ const columnIndex = buildIndexMap(columnIds);
2461
+ const source = normalizeRange(sourceRange, rowIndex, columnIndex);
2462
+ const final = normalizeRange(finalRange, rowIndex, columnIndex);
2463
+ if (!source || !final) return [];
2464
+ const columnById = new Map(columns.map((column) => [column.id, column]));
2465
+ const sourceRowSpan = source.rowEnd - source.rowStart + 1;
2466
+ const sourceColSpan = source.colEnd - source.colStart + 1;
2467
+ function sourceRowFor(targetRow) {
2468
+ if (targetRow >= source.rowStart && targetRow <= source.rowEnd) return targetRow;
2469
+ if (targetRow > source.rowEnd) return source.rowStart + (targetRow - source.rowEnd - 1) % sourceRowSpan;
2470
+ return source.rowEnd - (source.rowStart - targetRow - 1) % sourceRowSpan;
2471
+ }
2472
+ function sourceColFor(targetCol) {
2473
+ if (targetCol >= source.colStart && targetCol <= source.colEnd) return targetCol;
2474
+ if (targetCol > source.colEnd) return source.colStart + (targetCol - source.colEnd - 1) % sourceColSpan;
2475
+ return source.colEnd - (source.colStart - targetCol - 1) % sourceColSpan;
2476
+ }
2477
+ const changes = [];
2478
+ for (let r = final.rowStart; r <= final.rowEnd; r++) {
2479
+ const isNewRow = r < source.rowStart || r > source.rowEnd;
2480
+ for (let c = final.colStart; c <= final.colEnd; c++) {
2481
+ const isNewCol = c < source.colStart || c > source.colEnd;
2482
+ if (!isNewRow && !isNewCol) continue;
2483
+ const targetRowId = rowIds[r];
2484
+ const targetColumnId = columnIds[c];
2485
+ if (targetRowId === void 0 || targetColumnId === void 0) continue;
2486
+ const column = columnById.get(targetColumnId);
2487
+ const targetRow = getRow(targetRowId);
2488
+ if (!column || targetRow === void 0 || !isEditable(column, targetRow)) continue;
2489
+ const sourceRowId = rowIds[sourceRowFor(r)];
2490
+ const sourceColumnId = columnIds[sourceColFor(c)];
2491
+ const sourceRow = sourceRowId !== void 0 ? getRow(sourceRowId) : void 0;
2492
+ const sourceColumn = sourceColumnId !== void 0 ? columnById.get(sourceColumnId) : void 0;
2493
+ if (sourceRow === void 0 || !sourceColumn) continue;
2494
+ changes.push({
2495
+ rowId: targetRowId,
2496
+ row: targetRow,
2497
+ columnId: targetColumnId,
2498
+ previousValue: getColumnValue(column, targetRow),
2499
+ value: getColumnValue(sourceColumn, sourceRow)
2500
+ });
2501
+ }
2502
+ }
2503
+ return changes;
2504
+ }
2505
+
1812
2506
  // src/column-selector/ColumnSelector.tsx
1813
2507
  import { Columns3 } from "lucide-react";
1814
2508
  import { useEffect as useEffect6 } from "react";
@@ -1816,11 +2510,11 @@ import { useEffect as useEffect6 } from "react";
1816
2510
  // src/components/ui/dialog.tsx
1817
2511
  import * as DialogPrimitive from "@radix-ui/react-dialog";
1818
2512
  import { X } from "lucide-react";
1819
- import { forwardRef as forwardRef6 } from "react";
1820
- import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
2513
+ import { forwardRef as forwardRef7 } from "react";
2514
+ import { jsx as jsx28, jsxs as jsxs17 } from "react/jsx-runtime";
1821
2515
  var Dialog = DialogPrimitive.Root;
1822
2516
  var DialogTrigger = DialogPrimitive.Trigger;
1823
- var DialogOverlay = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
2517
+ var DialogOverlay = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx28(
1824
2518
  DialogPrimitive.Overlay,
1825
2519
  {
1826
2520
  ref,
@@ -1829,9 +2523,9 @@ var DialogOverlay = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ *
1829
2523
  }
1830
2524
  ));
1831
2525
  DialogOverlay.displayName = "DialogOverlay";
1832
- var DialogContent = forwardRef6(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs15(DialogPrimitive.Portal, { children: [
1833
- /* @__PURE__ */ jsx24(DialogOverlay, {}),
1834
- /* @__PURE__ */ jsxs15(
2526
+ var DialogContent = forwardRef7(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs17(DialogPrimitive.Portal, { children: [
2527
+ /* @__PURE__ */ jsx28(DialogOverlay, {}),
2528
+ /* @__PURE__ */ jsxs17(
1835
2529
  DialogPrimitive.Content,
1836
2530
  {
1837
2531
  ref,
@@ -1842,9 +2536,9 @@ var DialogContent = forwardRef6(({ className, children, ...props }, ref) => /* @
1842
2536
  ...props,
1843
2537
  children: [
1844
2538
  children,
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" })
2539
+ /* @__PURE__ */ jsxs17(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100", children: [
2540
+ /* @__PURE__ */ jsx28(X, { className: "h-4 w-4" }),
2541
+ /* @__PURE__ */ jsx28("span", { className: "sr-only", children: "Close" })
1848
2542
  ] })
1849
2543
  ]
1850
2544
  }
@@ -1854,10 +2548,10 @@ DialogContent.displayName = "DialogContent";
1854
2548
  var DialogHeader = ({
1855
2549
  className,
1856
2550
  ...props
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 }));
2551
+ }) => /* @__PURE__ */ jsx28("div", { className: cn("flex flex-col gap-1.5", className), ...props });
2552
+ var DialogTitle = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx28(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
1859
2553
  DialogTitle.displayName = "DialogTitle";
1860
- var DialogDescription = forwardRef6(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
2554
+ var DialogDescription = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx28(
1861
2555
  DialogPrimitive.Description,
1862
2556
  {
1863
2557
  ref,
@@ -1925,7 +2619,7 @@ function canHideGroup(columns, visibility, groupColumnIds) {
1925
2619
  }
1926
2620
 
1927
2621
  // src/column-selector/ColumnSelector.tsx
1928
- import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
2622
+ import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
1929
2623
  function ColumnSelector({
1930
2624
  columns,
1931
2625
  visibility,
@@ -1950,20 +2644,20 @@ function ColumnSelector({
1950
2644
  }
1951
2645
  const totalVisible = countVisible(columns, visibility);
1952
2646
  const groups = groupColumns(columns);
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 }),
2647
+ return /* @__PURE__ */ jsxs18(Dialog, { open, onOpenChange, children: [
2648
+ /* @__PURE__ */ jsx29(DialogTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx29(Button, { variant: "outline", size: "icon", "aria-label": "Choose columns", children: /* @__PURE__ */ jsx29(Columns3, { className: "h-4 w-4", "aria-hidden": true }) }) }),
2649
+ /* @__PURE__ */ jsxs18(DialogContent, { className: "w-fit max-w-[calc(100vw-2rem)] sm:max-w-2xl", children: [
2650
+ /* @__PURE__ */ jsx29(DialogHeader, { children: /* @__PURE__ */ jsx29(DialogTitle, { children: "Columns" }) }),
2651
+ /* @__PURE__ */ jsx29("div", { className: "flex flex-wrap gap-x-8 gap-y-5", children: groups.map(({ group, columns: groupCols }) => /* @__PURE__ */ jsxs18("div", { className: "flex min-w-32 flex-col gap-1.5", children: [
2652
+ group && /* @__PURE__ */ jsx29("span", { className: "border-b pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: group }),
1959
2653
  groupCols.map((column) => {
1960
2654
  const visible = isColumnVisible(visibility, column.id);
1961
- return /* @__PURE__ */ jsxs16(
2655
+ return /* @__PURE__ */ jsxs18(
1962
2656
  "label",
1963
2657
  {
1964
2658
  className: "flex items-center gap-2 rounded-md px-1.5 py-1 text-sm hover:bg-accent/50 focus-within:bg-accent/50",
1965
2659
  children: [
1966
- /* @__PURE__ */ jsx25(
2660
+ /* @__PURE__ */ jsx29(
1967
2661
  Checkbox,
1968
2662
  {
1969
2663
  checked: visible,
@@ -1995,15 +2689,15 @@ function resolveMenuItems(items, ctx) {
1995
2689
 
1996
2690
  // src/menu/ActionsMenu.tsx
1997
2691
  import { MoreVertical } from "lucide-react";
1998
- import { useMemo as useMemo3 } from "react";
2692
+ import { useMemo as useMemo4 } from "react";
1999
2693
 
2000
2694
  // src/components/ui/dropdown-menu.tsx
2001
2695
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2002
- import { forwardRef as forwardRef7 } from "react";
2003
- import { jsx as jsx26 } from "react/jsx-runtime";
2696
+ import { forwardRef as forwardRef8 } from "react";
2697
+ import { jsx as jsx30 } from "react/jsx-runtime";
2004
2698
  var DropdownMenu = DropdownMenuPrimitive.Root;
2005
2699
  var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
2006
- var DropdownMenuContent = forwardRef7(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx26(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx26(
2700
+ var DropdownMenuContent = forwardRef8(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx30(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx30(
2007
2701
  DropdownMenuPrimitive.Content,
2008
2702
  {
2009
2703
  ref,
@@ -2016,7 +2710,7 @@ var DropdownMenuContent = forwardRef7(({ className, sideOffset = 4, ...props },
2016
2710
  }
2017
2711
  ) }));
2018
2712
  DropdownMenuContent.displayName = "DropdownMenuContent";
2019
- var DropdownMenuItem = forwardRef7(({ className, danger, ...props }, ref) => /* @__PURE__ */ jsx26(
2713
+ var DropdownMenuItem = forwardRef8(({ className, danger, ...props }, ref) => /* @__PURE__ */ jsx30(
2020
2714
  DropdownMenuPrimitive.Item,
2021
2715
  {
2022
2716
  ref,
@@ -2029,7 +2723,7 @@ var DropdownMenuItem = forwardRef7(({ className, danger, ...props }, ref) => /*
2029
2723
  }
2030
2724
  ));
2031
2725
  DropdownMenuItem.displayName = "DropdownMenuItem";
2032
- var DropdownMenuSeparator = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx26(
2726
+ var DropdownMenuSeparator = forwardRef8(({ className, ...props }, ref) => /* @__PURE__ */ jsx30(
2033
2727
  DropdownMenuPrimitive.Separator,
2034
2728
  {
2035
2729
  ref,
@@ -2038,7 +2732,7 @@ var DropdownMenuSeparator = forwardRef7(({ className, ...props }, ref) => /* @__
2038
2732
  }
2039
2733
  ));
2040
2734
  DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
2041
- var DropdownMenuLabel = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx26(
2735
+ var DropdownMenuLabel = forwardRef8(({ className, ...props }, ref) => /* @__PURE__ */ jsx30(
2042
2736
  DropdownMenuPrimitive.Label,
2043
2737
  {
2044
2738
  ref,
@@ -2049,16 +2743,16 @@ var DropdownMenuLabel = forwardRef7(({ className, ...props }, ref) => /* @__PURE
2049
2743
  DropdownMenuLabel.displayName = "DropdownMenuLabel";
2050
2744
 
2051
2745
  // src/menu/ActionsMenu.tsx
2052
- import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
2746
+ import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
2053
2747
  function ActionsMenu({ items, ctx, triggerLabel }) {
2054
- const resolved = useMemo3(
2748
+ const resolved = useMemo4(
2055
2749
  () => resolveMenuItems(items, ctx),
2056
2750
  [items, ctx.row, ctx.selectedRows]
2057
2751
  );
2058
2752
  if (resolved.length === 0) return null;
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(
2753
+ return /* @__PURE__ */ jsxs19(DropdownMenu, { children: [
2754
+ /* @__PURE__ */ jsx31(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx31(Button, { variant: "ghost", size: "icon", "aria-label": triggerLabel, onClick: stopRowClick, children: /* @__PURE__ */ jsx31(MoreVertical, { className: "h-4 w-4", "aria-hidden": true }) }) }),
2755
+ /* @__PURE__ */ jsx31(DropdownMenuContent, { align: "end", children: resolved.map((item) => /* @__PURE__ */ jsx31(
2062
2756
  DropdownMenuItem,
2063
2757
  {
2064
2758
  danger: item.danger,
@@ -2076,25 +2770,25 @@ import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon as FunnelIcon7 } from "@
2076
2770
  import { columnResizingFeature, columnSizingFeature, flexRender, tableFeatures, useTable } from "@tanstack/react-table";
2077
2771
  import { useVirtualizer } from "@tanstack/react-virtual";
2078
2772
  import { ChevronRight, Loader2 } from "lucide-react";
2079
- import { Fragment as Fragment2, useEffect as useEffect9, useMemo as useMemo7, useRef as useRef8, useState as useState10 } from "react";
2773
+ import { Fragment as Fragment3, useEffect as useEffect9, useMemo as useMemo8, useRef as useRef11, useState as useState13 } from "react";
2080
2774
 
2081
2775
  // src/hooks/useGroupExpansion.ts
2082
- import { useCallback as useCallback2, useState as useState7 } from "react";
2776
+ import { useCallback as useCallback3, useState as useState10 } from "react";
2083
2777
  function useGroupExpansion(defaultGroupsExpanded, controlledExpandedGroups, onExpandedGroupsChange) {
2084
- const [internalExpandedGroups, setInternalExpandedGroups] = useState7({});
2778
+ const [internalExpandedGroups, setInternalExpandedGroups] = useState10({});
2085
2779
  const expandedGroups = controlledExpandedGroups ?? internalExpandedGroups;
2086
- const updateExpandedGroups = useCallback2(
2780
+ const updateExpandedGroups = useCallback3(
2087
2781
  (next) => {
2088
2782
  setInternalExpandedGroups(next);
2089
2783
  onExpandedGroupsChange?.(next);
2090
2784
  },
2091
2785
  [onExpandedGroupsChange]
2092
2786
  );
2093
- const isGroupExpanded = useCallback2(
2787
+ const isGroupExpanded = useCallback3(
2094
2788
  (key) => expandedGroups[key] ?? defaultGroupsExpanded,
2095
2789
  [expandedGroups, defaultGroupsExpanded]
2096
2790
  );
2097
- const toggleGroupExpanded = useCallback2(
2791
+ const toggleGroupExpanded = useCallback3(
2098
2792
  (key) => {
2099
2793
  updateExpandedGroups({ ...expandedGroups, [key]: !isGroupExpanded(key) });
2100
2794
  },
@@ -2104,11 +2798,11 @@ function useGroupExpansion(defaultGroupsExpanded, controlledExpandedGroups, onEx
2104
2798
  }
2105
2799
 
2106
2800
  // src/hooks/useStickyGroupHeaderTop.ts
2107
- import { useLayoutEffect, useRef as useRef6, useState as useState8 } from "react";
2801
+ import { useLayoutEffect as useLayoutEffect3, useRef as useRef9, useState as useState11 } from "react";
2108
2802
  function useStickyGroupHeaderTop(enabled) {
2109
- const theadRef = useRef6(null);
2110
- const [groupHeaderTop, setGroupHeaderTop] = useState8(0);
2111
- useLayoutEffect(() => {
2803
+ const theadRef = useRef9(null);
2804
+ const [groupHeaderTop, setGroupHeaderTop] = useState11(0);
2805
+ useLayoutEffect3(() => {
2112
2806
  if (!enabled) return;
2113
2807
  const theadEl = theadRef.current;
2114
2808
  if (!theadEl) return;
@@ -2122,9 +2816,9 @@ function useStickyGroupHeaderTop(enabled) {
2122
2816
  }
2123
2817
 
2124
2818
  // src/hooks/useVisibleColumns.ts
2125
- import { useMemo as useMemo4 } from "react";
2819
+ import { useMemo as useMemo5 } from "react";
2126
2820
  function useVisibleColumns(columns, columnVisibility) {
2127
- return useMemo4(
2821
+ return useMemo5(
2128
2822
  () => columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,
2129
2823
  [columns, columnVisibility]
2130
2824
  );
@@ -2171,20 +2865,20 @@ function processClientData(data, state) {
2171
2865
  }
2172
2866
 
2173
2867
  // src/grid/useGridState.ts
2174
- import { useCallback as useCallback3, useEffect as useEffect8, useMemo as useMemo6, useState as useState9 } from "react";
2868
+ import { useCallback as useCallback4, useEffect as useEffect8, useMemo as useMemo7, useState as useState12 } from "react";
2175
2869
 
2176
2870
  // src/hooks/useDebouncedCallback.ts
2177
- import { useEffect as useEffect7, useMemo as useMemo5, useRef as useRef7 } from "react";
2871
+ import { useEffect as useEffect7, useMemo as useMemo6, useRef as useRef10 } from "react";
2178
2872
  function useDebouncedCallback(callback, delayMs) {
2179
- const callbackRef = useRef7(callback);
2873
+ const callbackRef = useRef10(callback);
2180
2874
  callbackRef.current = callback;
2181
- const timeoutRef = useRef7(void 0);
2875
+ const timeoutRef = useRef10(void 0);
2182
2876
  useEffect7(() => {
2183
2877
  return () => {
2184
2878
  clearTimeout(timeoutRef.current);
2185
2879
  };
2186
2880
  }, []);
2187
- return useMemo5(() => {
2881
+ return useMemo6(() => {
2188
2882
  return {
2189
2883
  run: (...args) => {
2190
2884
  clearTimeout(timeoutRef.current);
@@ -2209,10 +2903,10 @@ function nextSortDir(current, descFirst) {
2209
2903
  return void 0;
2210
2904
  }
2211
2905
  function useGridState(dataSource, initialState, externalState) {
2212
- const [state, setState] = useState9(
2906
+ const [state, setState] = useState12(
2213
2907
  () => externalState ?? { ...DEFAULT_STATE, ...initialState }
2214
2908
  );
2215
- const notifyRaw = useCallback3(
2909
+ const notifyRaw = useCallback4(
2216
2910
  (next) => {
2217
2911
  if (dataSource.mode === "server") dataSource.onStateChange(next);
2218
2912
  },
@@ -2222,7 +2916,7 @@ function useGridState(dataSource, initialState, externalState) {
2222
2916
  notifyRaw,
2223
2917
  FILTER_CHANGE_DEBOUNCE_MS
2224
2918
  );
2225
- const notifyNow = useCallback3(
2919
+ const notifyNow = useCallback4(
2226
2920
  (next) => {
2227
2921
  cancelDebouncedNotify();
2228
2922
  notifyRaw(next);
@@ -2234,14 +2928,14 @@ function useGridState(dataSource, initialState, externalState) {
2234
2928
  cancelDebouncedNotify();
2235
2929
  setState(externalState);
2236
2930
  }, [externalState, cancelDebouncedNotify]);
2237
- const filtersByColumn = useMemo6(() => {
2931
+ const filtersByColumn = useMemo7(() => {
2238
2932
  const map = /* @__PURE__ */ new Map();
2239
2933
  for (const child of state.filter?.filters ?? []) {
2240
2934
  if ("operator" in child) map.set(fieldKey(child.field), child);
2241
2935
  }
2242
2936
  return map;
2243
2937
  }, [state.filter]);
2244
- const setColumnFilter = useCallback3(
2938
+ const setColumnFilter = useCallback4(
2245
2939
  (columnId, next) => {
2246
2940
  setState((prev) => {
2247
2941
  const remaining = (prev.filter?.filters ?? []).filter(
@@ -2259,7 +2953,7 @@ function useGridState(dataSource, initialState, externalState) {
2259
2953
  },
2260
2954
  [notifyDebounced]
2261
2955
  );
2262
- const toggleSort = useCallback3(
2956
+ const toggleSort = useCallback4(
2263
2957
  (columnId, additive, descFirst = false) => {
2264
2958
  setState((prev) => {
2265
2959
  const existing = prev.sort.find((s) => s.field === columnId);
@@ -2274,7 +2968,7 @@ function useGridState(dataSource, initialState, externalState) {
2274
2968
  },
2275
2969
  [notifyNow]
2276
2970
  );
2277
- const setPage = useCallback3(
2971
+ const setPage = useCallback4(
2278
2972
  (page) => {
2279
2973
  setState((prev) => {
2280
2974
  const nextState = { ...prev, page };
@@ -2284,7 +2978,7 @@ function useGridState(dataSource, initialState, externalState) {
2284
2978
  },
2285
2979
  [notifyNow]
2286
2980
  );
2287
- const setPageSize = useCallback3(
2981
+ const setPageSize = useCallback4(
2288
2982
  (pageSize) => {
2289
2983
  setState((prev) => {
2290
2984
  const nextState = { ...prev, pageSize, page: 0 };
@@ -2298,11 +2992,12 @@ function useGridState(dataSource, initialState, externalState) {
2298
2992
  }
2299
2993
 
2300
2994
  // src/grid/DataGrid.tsx
2301
- import { Fragment as Fragment3, jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
2995
+ import { Fragment as Fragment4, jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
2302
2996
  var gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });
2303
2997
  var DEFAULT_COLUMN_SIZE = 150;
2304
2998
  var DETAIL_COLUMN_WIDTH = 44;
2305
2999
  var ROW_ACTIONS_COLUMN_WIDTH = 44;
3000
+ var EMPTY_ID_ARRAY = [];
2306
3001
  var STRUCTURAL_ZEBRA_BG_CLASS = "bg-[color-mix(in_srgb,var(--color-foreground)_5%,var(--color-background))]";
2307
3002
  function pinnedOffsets(visibleColumns, side, getSize, leadingOffset) {
2308
3003
  const offsets = /* @__PURE__ */ new Map();
@@ -2329,13 +3024,13 @@ function computeHeaderRuns(columns) {
2329
3024
  }
2330
3025
  return runs;
2331
3026
  }
2332
- function toTanstackColumns(columns, editingCtxRef) {
3027
+ function toTanstackColumns(columns, editingCtxRef, cellEditingCtxRef) {
2333
3028
  return columns.map((column) => ({
2334
3029
  id: column.id,
2335
3030
  header: column.header,
2336
3031
  size: column.width,
2337
3032
  accessorFn: (row) => getColumnValue(column, row),
2338
- cell: (info) => renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current)
3033
+ cell: (info) => cellEditingCtxRef.current ? renderCellModeCell(column, info.row.original, info.getValue(), cellEditingCtxRef.current) : renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current)
2339
3034
  }));
2340
3035
  }
2341
3036
  function DataGrid({
@@ -2367,26 +3062,28 @@ function DataGrid({
2367
3062
  expandedGroups: controlledExpandedGroups,
2368
3063
  onExpandedGroupsChange,
2369
3064
  zebra = true,
2370
- editing
3065
+ editing,
3066
+ cellEditing
2371
3067
  }) {
3068
+ const hasCellEditing = Boolean(cellEditing) && !groupBy;
2372
3069
  const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(
2373
3070
  dataSource,
2374
3071
  initialState,
2375
3072
  gridState
2376
3073
  );
2377
- const [internalSelectedIds, setInternalSelectedIds] = useState10(/* @__PURE__ */ new Set());
3074
+ const [internalSelectedIds, setInternalSelectedIds] = useState13(/* @__PURE__ */ new Set());
2378
3075
  const selectedIds = controlledSelectedIds ?? internalSelectedIds;
2379
3076
  function updateSelectedIds(next) {
2380
3077
  setInternalSelectedIds(next);
2381
3078
  onSelectedIdsChange?.(next);
2382
3079
  }
2383
- const [internalColumnSizing, setInternalColumnSizing] = useState10({});
3080
+ const [internalColumnSizing, setInternalColumnSizing] = useState13({});
2384
3081
  const columnSizing = controlledColumnSizing ?? internalColumnSizing;
2385
3082
  function updateColumnSizing(next) {
2386
3083
  setInternalColumnSizing(next);
2387
3084
  onColumnSizingChange?.(next);
2388
3085
  }
2389
- const [expandedIds, setExpandedIds] = useState10(/* @__PURE__ */ new Set());
3086
+ const [expandedIds, setExpandedIds] = useState13(/* @__PURE__ */ new Set());
2390
3087
  function toggleRowExpanded(id) {
2391
3088
  setExpandedIds((prev) => {
2392
3089
  const next = new Set(prev);
@@ -2404,13 +3101,17 @@ function DataGrid({
2404
3101
  editing,
2405
3102
  getRowId
2406
3103
  );
3104
+ const { ctx: cellEditingCtx, ctxRef: cellEditingCtxRef, applyChanges: applyCellChanges } = useCellEditingState(
3105
+ hasCellEditing ? cellEditing : void 0,
3106
+ getRowId
3107
+ );
2407
3108
  const visibleColumns = useVisibleColumns(columns, columnVisibility);
2408
3109
  const serverRowCount = dataSource.mode === "server" ? dataSource.rowCount : void 0;
2409
- const { rows, rowCount } = useMemo7(() => {
3110
+ const { rows, rowCount } = useMemo8(() => {
2410
3111
  if (dataSource.mode === "client") return processClientData(dataSource.data, state);
2411
3112
  return { rows: dataSource.data, rowCount: serverRowCount ?? 0 };
2412
3113
  }, [dataSource.mode, dataSource.data, serverRowCount, state]);
2413
- const selectedRows = useMemo7(
3114
+ const selectedRows = useMemo8(
2414
3115
  () => rows.filter((row) => selectedIds.has(getRowId(row))),
2415
3116
  [rows, selectedIds, getRowId]
2416
3117
  );
@@ -2429,8 +3130,11 @@ function DataGrid({
2429
3130
  }
2430
3131
  updateSelectedIds(next);
2431
3132
  }
2432
- const tanstackColumns = useMemo7(() => toTanstackColumns(visibleColumns, editingCtxRef), [visibleColumns]);
2433
- const columnById = useMemo7(() => {
3133
+ const tanstackColumns = useMemo8(
3134
+ () => toTanstackColumns(visibleColumns, editingCtxRef, cellEditingCtxRef),
3135
+ [visibleColumns]
3136
+ );
3137
+ const columnById = useMemo8(() => {
2434
3138
  const map = /* @__PURE__ */ new Map();
2435
3139
  for (const column of visibleColumns) map.set(column.id, column);
2436
3140
  return map;
@@ -2448,6 +3152,9 @@ function DataGrid({
2448
3152
  updateColumnSizing(next);
2449
3153
  }
2450
3154
  });
3155
+ function findRowById(rowId) {
3156
+ return table.getRowModel().rowsById[rowId]?.original;
3157
+ }
2451
3158
  const pageCount = Math.max(1, Math.ceil(rowCount / state.pageSize));
2452
3159
  const loading = loadingProp || (dataSource.mode === "server" ? Boolean(dataSource.loading) : false);
2453
3160
  const showSelectionColumn = Boolean(headerActions?.length) || controlledSelectedIds !== void 0;
@@ -2455,7 +3162,7 @@ function DataGrid({
2455
3162
  const showDetailColumn = Boolean(renderDetail);
2456
3163
  const totalColumnCount = visibleColumns.length + (showDetailColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0) + (showRowActionsColumn ? 1 : 0);
2457
3164
  const hasFilterRow = visibleColumns.some((column) => isFilterable(column) && column.filterDisplay === "row");
2458
- const headerRuns = useMemo7(() => computeHeaderRuns(visibleColumns), [visibleColumns]);
3165
+ const headerRuns = useMemo8(() => computeHeaderRuns(visibleColumns), [visibleColumns]);
2459
3166
  const hasHeaderGroups = visibleColumns.some((column) => column.headerGroup);
2460
3167
  function renderFilterWidget(column) {
2461
3168
  const value = filtersByColumn.get(column.id);
@@ -2465,11 +3172,11 @@ function DataGrid({
2465
3172
  const columnSize = (columnId) => table.getColumn(columnId)?.getSize() ?? DEFAULT_COLUMN_SIZE;
2466
3173
  const leadingStructuralWidth = (showDetailColumn ? DETAIL_COLUMN_WIDTH : 0) + (showSelectionColumn ? SELECTION_COLUMN_WIDTH : 0);
2467
3174
  const trailingStructuralWidth = showRowActionsColumn ? ROW_ACTIONS_COLUMN_WIDTH : 0;
2468
- const leftPinnedOffsets = useMemo7(
3175
+ const leftPinnedOffsets = useMemo8(
2469
3176
  () => pinnedOffsets(visibleColumns, "left", columnSize, leadingStructuralWidth),
2470
3177
  [visibleColumns, columnSizing, leadingStructuralWidth]
2471
3178
  );
2472
- const rightPinnedOffsets = useMemo7(
3179
+ const rightPinnedOffsets = useMemo8(
2473
3180
  () => pinnedOffsets(visibleColumns, "right", columnSize, trailingStructuralWidth),
2474
3181
  [visibleColumns, columnSizing, trailingStructuralWidth]
2475
3182
  );
@@ -2517,7 +3224,7 @@ function DataGrid({
2517
3224
  style: pinnedProps?.style
2518
3225
  };
2519
3226
  }
2520
- const bodyCellPropsByColumn = useMemo7(() => {
3227
+ const bodyCellPropsByColumn = useMemo8(() => {
2521
3228
  const map = /* @__PURE__ */ new Map();
2522
3229
  for (const column of visibleColumns) map.set(column.id, bodyCellClassAndStyle(column));
2523
3230
  return map;
@@ -2533,7 +3240,7 @@ function DataGrid({
2533
3240
  );
2534
3241
  return { className, style: pinnedProps.style };
2535
3242
  }
2536
- const leafHeaderCellPropsByColumn = useMemo7(() => {
3243
+ const leafHeaderCellPropsByColumn = useMemo8(() => {
2537
3244
  const map = /* @__PURE__ */ new Map();
2538
3245
  for (const column of visibleColumns) {
2539
3246
  map.set(
@@ -2543,7 +3250,7 @@ function DataGrid({
2543
3250
  }
2544
3251
  return map;
2545
3252
  }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
2546
- const filterHeaderCellPropsByColumn = useMemo7(() => {
3253
+ const filterHeaderCellPropsByColumn = useMemo8(() => {
2547
3254
  const map = /* @__PURE__ */ new Map();
2548
3255
  for (const column of visibleColumns) {
2549
3256
  map.set(column.id, headerCellClassAndStyle(column, "border-b border-border p-2 align-top font-normal"));
@@ -2551,13 +3258,13 @@ function DataGrid({
2551
3258
  return map;
2552
3259
  }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
2553
3260
  const tableRows = table.getRowModel().rows;
2554
- const groupedBuckets = useMemo7(
3261
+ const groupedBuckets = useMemo8(
2555
3262
  () => groupBy ? groupRows(tableRows, (row) => groupBy(row.original)) : void 0,
2556
3263
  [tableRows, groupBy]
2557
3264
  );
2558
3265
  const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));
2559
3266
  const shouldVirtualize = Boolean(virtualize) && tableRows.length > (virtualize?.threshold ?? 100);
2560
- const flatItems = useMemo7(() => {
3267
+ const flatItems = useMemo8(() => {
2561
3268
  if (!shouldVirtualize) return [];
2562
3269
  if (!groupedBuckets) return tableRows.map((row) => ({ kind: "row", row }));
2563
3270
  const items = [];
@@ -2570,7 +3277,7 @@ function DataGrid({
2570
3277
  }
2571
3278
  return items;
2572
3279
  }, [shouldVirtualize, groupedBuckets, tableRows, isGroupExpanded]);
2573
- const scrollRef = useRef8(null);
3280
+ const scrollRef = useRef11(null);
2574
3281
  const virtualizer = useVirtualizer({
2575
3282
  count: flatItems.length,
2576
3283
  getScrollElement: () => scrollRef.current,
@@ -2582,7 +3289,7 @@ function DataGrid({
2582
3289
  const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
2583
3290
  const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
2584
3291
  const lastVisibleIndex = virtualItems.length > 0 ? virtualItems[virtualItems.length - 1].index : -1;
2585
- const notifiedForLengthRef = useRef8(-1);
3292
+ const notifiedForLengthRef = useRef11(-1);
2586
3293
  useEffect9(() => {
2587
3294
  if (!virtualize?.onEndReached) return;
2588
3295
  if (lastVisibleIndex < 0 || lastVisibleIndex < flatItems.length - 1) return;
@@ -2591,25 +3298,226 @@ function DataGrid({
2591
3298
  notifiedForLengthRef.current = tableRows.length;
2592
3299
  virtualize.onEndReached();
2593
3300
  }, [lastVisibleIndex, flatItems.length, tableRows.length, virtualize]);
3301
+ const cellEditingRowIds = useMemo8(
3302
+ () => hasCellEditing ? tableRows.map((row) => row.id) : EMPTY_ID_ARRAY,
3303
+ [hasCellEditing, tableRows]
3304
+ );
3305
+ const cellEditingColumnIds = useMemo8(
3306
+ () => hasCellEditing ? visibleColumns.map((column) => column.id) : EMPTY_ID_ARRAY,
3307
+ [hasCellEditing, visibleColumns]
3308
+ );
3309
+ const cellEditingRowIndex = useMemo8(() => buildIndexMap(cellEditingRowIds), [cellEditingRowIds]);
3310
+ const cellSelection = useCellSelection({
3311
+ rowIds: cellEditingRowIds,
3312
+ columnIds: cellEditingColumnIds,
3313
+ onNavigateToRow: (rowId) => {
3314
+ if (!shouldVirtualize) return;
3315
+ const index = cellEditingRowIndex.get(rowId);
3316
+ if (index !== void 0) virtualizer.scrollToIndex(index);
3317
+ }
3318
+ });
3319
+ const pendingDragCellRef = useRef11(null);
3320
+ const dragRafRef = useRef11(null);
3321
+ useEffect9(() => {
3322
+ if (!cellSelection.isDragging) return;
3323
+ function handleMouseMove(event) {
3324
+ const cellEl = event.target?.closest("[data-cell-row]");
3325
+ const rowId = cellEl?.dataset.cellRow;
3326
+ const columnId = cellEl?.dataset.cellCol;
3327
+ if (rowId === void 0 || columnId === void 0) return;
3328
+ pendingDragCellRef.current = { rowId, columnId };
3329
+ if (dragRafRef.current !== null) return;
3330
+ dragRafRef.current = requestAnimationFrame(() => {
3331
+ dragRafRef.current = null;
3332
+ if (pendingDragCellRef.current) cellSelection.updateDrag(pendingDragCellRef.current);
3333
+ });
3334
+ }
3335
+ function handleMouseUp() {
3336
+ cellSelection.endDrag();
3337
+ }
3338
+ window.addEventListener("mousemove", handleMouseMove);
3339
+ window.addEventListener("mouseup", handleMouseUp);
3340
+ return () => {
3341
+ window.removeEventListener("mousemove", handleMouseMove);
3342
+ window.removeEventListener("mouseup", handleMouseUp);
3343
+ if (dragRafRef.current !== null) cancelAnimationFrame(dragRafRef.current);
3344
+ dragRafRef.current = null;
3345
+ };
3346
+ }, [cellSelection.isDragging, cellSelection.updateDrag, cellSelection.endDrag]);
3347
+ const pendingFillCellRef = useRef11(null);
3348
+ const fillRafRef = useRef11(null);
3349
+ useEffect9(() => {
3350
+ if (!cellSelection.isFillDragging) return;
3351
+ function handleMouseMove(event) {
3352
+ const cellEl = event.target?.closest("[data-cell-row]");
3353
+ const rowId = cellEl?.dataset.cellRow;
3354
+ const columnId = cellEl?.dataset.cellCol;
3355
+ if (rowId === void 0 || columnId === void 0) return;
3356
+ pendingFillCellRef.current = { rowId, columnId };
3357
+ if (fillRafRef.current !== null) return;
3358
+ fillRafRef.current = requestAnimationFrame(() => {
3359
+ fillRafRef.current = null;
3360
+ if (pendingFillCellRef.current) cellSelection.updateFillDrag(pendingFillCellRef.current);
3361
+ });
3362
+ }
3363
+ function handleMouseUp() {
3364
+ const result = cellSelection.endFillDrag();
3365
+ if (!result || !cellEditingCtx) return;
3366
+ const changes = computeFillChanges(
3367
+ result.sourceRange,
3368
+ result.finalRange,
3369
+ cellEditingRowIds,
3370
+ cellEditingColumnIds,
3371
+ visibleColumns,
3372
+ findRowById
3373
+ );
3374
+ if (changes.length > 0) applyCellChanges(changes);
3375
+ }
3376
+ window.addEventListener("mousemove", handleMouseMove);
3377
+ window.addEventListener("mouseup", handleMouseUp);
3378
+ return () => {
3379
+ window.removeEventListener("mousemove", handleMouseMove);
3380
+ window.removeEventListener("mouseup", handleMouseUp);
3381
+ if (fillRafRef.current !== null) cancelAnimationFrame(fillRafRef.current);
3382
+ fillRafRef.current = null;
3383
+ };
3384
+ }, [cellSelection.isFillDragging, cellSelection.updateFillDrag, cellSelection.endFillDrag]);
3385
+ const wasEditingCellRef = useRef11(void 0);
3386
+ useEffect9(() => {
3387
+ const wasEditing = wasEditingCellRef.current !== void 0;
3388
+ wasEditingCellRef.current = cellEditingCtx?.editingCell;
3389
+ if (!wasEditing || cellEditingCtx?.editingCell) return;
3390
+ if (document.activeElement === document.body) scrollRef.current?.focus();
3391
+ });
3392
+ function handleFillHandleMouseDown() {
3393
+ cellSelection.startFillDrag();
3394
+ }
3395
+ function handleCellMouseDown(event) {
3396
+ if (!hasCellEditing) return;
3397
+ const cellEl = event.target.closest("[data-cell-row]");
3398
+ const rowId = cellEl?.dataset.cellRow;
3399
+ const columnId = cellEl?.dataset.cellCol;
3400
+ if (rowId === void 0 || columnId === void 0) return;
3401
+ cellSelection.startSelection({ rowId, columnId }, { extend: event.shiftKey });
3402
+ scrollRef.current?.focus();
3403
+ }
3404
+ const CELL_NAV_KEYS = {
3405
+ ArrowUp: "up",
3406
+ ArrowDown: "down",
3407
+ ArrowLeft: "left",
3408
+ ArrowRight: "right"
3409
+ };
3410
+ function resolveSelectedCell() {
3411
+ const focus = cellSelection.selection?.focus;
3412
+ if (!focus) return void 0;
3413
+ const column = columnById.get(focus.columnId);
3414
+ const row = findRowById(focus.rowId);
3415
+ if (!column || row === void 0) return void 0;
3416
+ return { column, row };
3417
+ }
3418
+ function handleCellKeyDown(event) {
3419
+ if (!hasCellEditing || !cellEditingCtx) return;
3420
+ if (cellEditingCtx.editingCell && !cellEditingCtx.consumeJustCommitted()) return;
3421
+ const direction = CELL_NAV_KEYS[event.key];
3422
+ if (direction) {
3423
+ event.preventDefault();
3424
+ cellSelection.moveSelection(direction, { extend: event.shiftKey });
3425
+ return;
3426
+ }
3427
+ if (event.key === "Tab") {
3428
+ event.preventDefault();
3429
+ cellSelection.moveSelection(event.shiftKey ? "left" : "right");
3430
+ return;
3431
+ }
3432
+ if (event.key === "Enter") {
3433
+ event.preventDefault();
3434
+ cellSelection.moveSelection("down");
3435
+ return;
3436
+ }
3437
+ const isPrintableChar = event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
3438
+ if (event.key !== "F2" && !isPrintableChar) return;
3439
+ const target = resolveSelectedCell();
3440
+ if (!target || !isEditable(target.column, target.row)) return;
3441
+ event.preventDefault();
3442
+ cellEditingCtx.onBeginEdit(
3443
+ { rowId: cellEditingCtx.getRowId(target.row), columnId: target.column.id },
3444
+ isPrintableChar ? event.key : void 0
3445
+ );
3446
+ }
3447
+ function handleCellDoubleClick(event) {
3448
+ if (!hasCellEditing || !cellEditingCtx) return;
3449
+ const cellEl = event.target.closest("[data-cell-row]");
3450
+ const rowId = cellEl?.dataset.cellRow;
3451
+ const columnId = cellEl?.dataset.cellCol;
3452
+ if (rowId === void 0 || columnId === void 0) return;
3453
+ const column = columnById.get(columnId);
3454
+ const row = findRowById(rowId);
3455
+ if (!column || row === void 0 || !isEditable(column, row)) return;
3456
+ cellEditingCtx.onBeginEdit({ rowId, columnId });
3457
+ }
3458
+ function handleCopy(event) {
3459
+ if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell) return;
3460
+ event.preventDefault();
3461
+ const text = rangeToTsv(cellSelection.selection, rows, cellEditingRowIds, visibleColumns, cellEditingColumnIds, getRowId);
3462
+ event.clipboardData.setData("text/plain", text);
3463
+ }
3464
+ function buildCellChange(row, col, raw) {
3465
+ const rowId = cellEditingRowIds[row];
3466
+ const columnId = cellEditingColumnIds[col];
3467
+ if (rowId === void 0 || columnId === void 0) return void 0;
3468
+ const column = columnById.get(columnId);
3469
+ const rowData = findRowById(rowId);
3470
+ if (!column || rowData === void 0 || !isEditable(column, rowData)) return void 0;
3471
+ const coerced = coerceValueForColumn(column, raw);
3472
+ if (!coerced) return void 0;
3473
+ return { rowId, row: rowData, columnId, previousValue: getColumnValue(column, rowData), value: coerced.value };
3474
+ }
3475
+ function handlePaste(event) {
3476
+ if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell) return;
3477
+ const text = event.clipboardData.getData("text/plain");
3478
+ event.preventDefault();
3479
+ const normalized = normalizeRange(cellSelection.selection, cellEditingRowIndex, buildIndexMap(cellEditingColumnIds));
3480
+ if (!normalized) return;
3481
+ const parsed = parseTsv(text);
3482
+ const changes = [];
3483
+ if (parsed.length === 1 && parsed[0]?.length === 1) {
3484
+ const raw = parsed[0][0];
3485
+ for (let r = normalized.rowStart; r <= normalized.rowEnd; r++) {
3486
+ for (let c = normalized.colStart; c <= normalized.colEnd; c++) {
3487
+ const change = buildCellChange(r, c, raw);
3488
+ if (change) changes.push(change);
3489
+ }
3490
+ }
3491
+ } else {
3492
+ for (let ri = 0; ri < parsed.length; ri++) {
3493
+ const line = parsed[ri];
3494
+ for (let ci = 0; ci < line.length; ci++) {
3495
+ const change = buildCellChange(normalized.rowStart + ri, normalized.colStart + ci, line[ci]);
3496
+ if (change) changes.push(change);
3497
+ }
3498
+ }
3499
+ }
3500
+ if (changes.length > 0) applyCellChanges(changes);
3501
+ }
2594
3502
  function renderRow(row, measureRef, dataIndex = row.index) {
2595
3503
  const isExpanded = expandedIds.has(row.id);
2596
3504
  const rowProps = getRowProps?.(row.original);
2597
3505
  const rowTestId = getRowTestId?.(row.original) ?? `row-${row.id}`;
2598
3506
  const isOddRow = zebra && row.index % 2 === 1;
2599
- return /* @__PURE__ */ jsxs18("tbody", { ref: measureRef, "data-index": dataIndex, children: [
2600
- /* @__PURE__ */ jsxs18(
3507
+ return /* @__PURE__ */ jsxs20("tbody", { ref: measureRef, "data-index": dataIndex, children: [
3508
+ /* @__PURE__ */ jsxs20(
2601
3509
  "tr",
2602
3510
  {
2603
3511
  ...rowProps,
2604
3512
  "data-testid": rowTestId,
2605
3513
  className: rowProps?.className ? cn("divide-x divide-border", rowProps.className, isOddRow && "bg-foreground/5") : `divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
2606
3514
  children: [
2607
- showDetailColumn && /* @__PURE__ */ jsx28(
3515
+ showDetailColumn && /* @__PURE__ */ jsx32(
2608
3516
  "td",
2609
3517
  {
2610
3518
  className: isOddRow ? detailCellProps.classNameOdd : detailCellProps.className,
2611
3519
  style: detailCellProps.style,
2612
- children: /* @__PURE__ */ jsx28(
3520
+ children: /* @__PURE__ */ jsx32(
2613
3521
  Button,
2614
3522
  {
2615
3523
  type: "button",
@@ -2621,7 +3529,7 @@ function DataGrid({
2621
3529
  stopRowClick(event);
2622
3530
  toggleRowExpanded(row.id);
2623
3531
  },
2624
- children: /* @__PURE__ */ jsx28(
3532
+ children: /* @__PURE__ */ jsx32(
2625
3533
  ChevronRight,
2626
3534
  {
2627
3535
  className: `h-4 w-4 transition-transform${isExpanded ? " rotate-90" : ""}`,
@@ -2632,12 +3540,12 @@ function DataGrid({
2632
3540
  )
2633
3541
  }
2634
3542
  ),
2635
- showSelectionColumn && /* @__PURE__ */ jsx28(
3543
+ showSelectionColumn && /* @__PURE__ */ jsx32(
2636
3544
  "td",
2637
3545
  {
2638
3546
  className: isOddRow ? selectionCellProps.classNameOdd : selectionCellProps.className,
2639
3547
  style: selectionCellProps.style,
2640
- children: /* @__PURE__ */ jsx28(
3548
+ children: /* @__PURE__ */ jsx32(
2641
3549
  "input",
2642
3550
  {
2643
3551
  type: "checkbox",
@@ -2651,31 +3559,41 @@ function DataGrid({
2651
3559
  ),
2652
3560
  row.getAllCells().map((cell) => {
2653
3561
  const cellProps = bodyCellPropsByColumn.get(cell.column.id);
2654
- return /* @__PURE__ */ jsx28("td", { style: cellProps?.style, className: cellProps?.className ?? BODY_TD_BASE_CLASS, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id);
3562
+ return /* @__PURE__ */ jsx32(
3563
+ "td",
3564
+ {
3565
+ style: cellProps?.style,
3566
+ className: cellProps?.className ?? BODY_TD_BASE_CLASS,
3567
+ "data-cell-row": hasCellEditing ? row.id : void 0,
3568
+ "data-cell-col": hasCellEditing ? cell.column.id : void 0,
3569
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
3570
+ },
3571
+ cell.id
3572
+ );
2655
3573
  }),
2656
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx28(
3574
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx32(
2657
3575
  "td",
2658
3576
  {
2659
3577
  className: isOddRow ? rowActionsCellProps.classNameOdd : rowActionsCellProps.className,
2660
3578
  style: rowActionsCellProps.style,
2661
- children: /* @__PURE__ */ jsx28(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
3579
+ children: /* @__PURE__ */ jsx32(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
2662
3580
  }
2663
3581
  )
2664
3582
  ]
2665
3583
  }
2666
3584
  ),
2667
- 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) }) })
3585
+ showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx32("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx32("td", { colSpan: totalColumnCount, className: "border-b border-border p-2", children: renderDetail(row.original) }) })
2668
3586
  ] }, row.id);
2669
3587
  }
2670
3588
  function renderGroupHeaderTbody(key, rows2, measureRef, dataIndex) {
2671
3589
  const expanded = isGroupExpanded(key);
2672
- return /* @__PURE__ */ jsx28("tbody", { ref: measureRef, "data-index": dataIndex, children: /* @__PURE__ */ jsx28("tr", { "data-testid": `group-header-${key}`, children: /* @__PURE__ */ jsx28(
3590
+ return /* @__PURE__ */ jsx32("tbody", { ref: measureRef, "data-index": dataIndex, children: /* @__PURE__ */ jsx32("tr", { "data-testid": `group-header-${key}`, children: /* @__PURE__ */ jsx32(
2673
3591
  "td",
2674
3592
  {
2675
3593
  colSpan: totalColumnCount,
2676
3594
  className: cn("sticky z-20 border-b p-2 font-medium", zebra ? "bg-muted" : "bg-background"),
2677
3595
  style: { top: groupHeaderTop },
2678
- children: /* @__PURE__ */ jsxs18(
3596
+ children: /* @__PURE__ */ jsxs20(
2679
3597
  "button",
2680
3598
  {
2681
3599
  type: "button",
@@ -2683,7 +3601,7 @@ function DataGrid({
2683
3601
  onClick: () => toggleGroupExpanded(key),
2684
3602
  "aria-expanded": expanded,
2685
3603
  children: [
2686
- /* @__PURE__ */ jsx28(
3604
+ /* @__PURE__ */ jsx32(
2687
3605
  ChevronRight,
2688
3606
  {
2689
3607
  className: `h-4 w-4 shrink-0 transition-transform${expanded ? " rotate-90" : ""}`,
@@ -2700,7 +3618,7 @@ function DataGrid({
2700
3618
  function renderGroupedBucket(bucket) {
2701
3619
  const expanded = isGroupExpanded(bucket.key);
2702
3620
  const originalRows = bucket.items.map((row) => row.original);
2703
- return /* @__PURE__ */ jsxs18(Fragment2, { children: [
3621
+ return /* @__PURE__ */ jsxs20(Fragment3, { children: [
2704
3622
  renderGroupHeaderTbody(bucket.key, originalRows),
2705
3623
  expanded && bucket.items.map((row) => renderRow(row))
2706
3624
  ] }, `group-${bucket.key}`);
@@ -2722,8 +3640,8 @@ function DataGrid({
2722
3640
  // containers (that one scrolling vertically, this component's own
2723
3641
  // `overflow-x-auto` div scrolling horizontally) fighting over which one
2724
3642
  // a sticky header/pinned column is actually stuck relative to.
2725
- /* @__PURE__ */ jsxs18("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
2726
- editing && pendingEdits.size > 0 && /* @__PURE__ */ jsx28(
3643
+ /* @__PURE__ */ jsxs20("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
3644
+ editing && pendingEdits.size > 0 && /* @__PURE__ */ jsx32(
2727
3645
  EditingBar,
2728
3646
  {
2729
3647
  editing,
@@ -2733,217 +3651,234 @@ function DataGrid({
2733
3651
  onDiscard: handleDiscardEdits
2734
3652
  }
2735
3653
  ),
2736
- showSelectionColumn && headerActions && /* @__PURE__ */ jsxs18("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
2737
- /* @__PURE__ */ jsxs18("span", { children: [
3654
+ showSelectionColumn && headerActions && /* @__PURE__ */ jsxs20("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
3655
+ /* @__PURE__ */ jsxs20("span", { children: [
2738
3656
  selectedRows.length,
2739
3657
  " selected"
2740
3658
  ] }),
2741
- /* @__PURE__ */ jsx28(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
3659
+ /* @__PURE__ */ jsx32(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
2742
3660
  ] }),
2743
- /* @__PURE__ */ jsxs18("div", { className: "relative min-h-0 flex-1", children: [
2744
- loading && /* @__PURE__ */ jsx28(
3661
+ /* @__PURE__ */ jsxs20("div", { className: "relative min-h-0 flex-1", children: [
3662
+ loading && /* @__PURE__ */ jsx32(
2745
3663
  "div",
2746
3664
  {
2747
3665
  "data-testid": "datagrid-loading-overlay",
2748
3666
  className: "absolute inset-0 z-30 flex items-center justify-center bg-background/60",
2749
- children: /* @__PURE__ */ jsx28(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
3667
+ children: /* @__PURE__ */ jsx32(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
2750
3668
  }
2751
3669
  ),
2752
- /* @__PURE__ */ jsx28(
3670
+ /* @__PURE__ */ jsxs20(
2753
3671
  "div",
2754
3672
  {
2755
3673
  ref: scrollRef,
2756
3674
  "data-testid": testId,
2757
- className: "h-full overflow-auto rounded-md border",
3675
+ className: cn("h-full overflow-auto rounded-md border", hasCellEditing && "relative"),
2758
3676
  style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : void 0,
2759
- children: /* @__PURE__ */ jsxs18("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
2760
- /* @__PURE__ */ jsxs18("thead", { ref: theadRef, className: "bg-muted", children: [
2761
- table.getHeaderGroups().map((headerGroup) => {
2762
- const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));
2763
- function renderLeafHeaderCell(column, rowSpan) {
2764
- const header = headerById.get(column.id);
2765
- if (!header) return null;
2766
- const sortEntry = state.sort.find((s) => s.field === column.id);
2767
- const sortable = isSortable(column);
2768
- const filterable = isFilterable(column) && column.filterDisplay !== "row";
2769
- const cellProps = leafHeaderCellPropsByColumn.get(column.id);
2770
- const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;
2771
- return /* @__PURE__ */ jsxs18("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
2772
- /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1", children: [
2773
- sortable ? /* @__PURE__ */ jsxs18(
2774
- "button",
3677
+ tabIndex: hasCellEditing ? 0 : void 0,
3678
+ onMouseDown: hasCellEditing ? handleCellMouseDown : void 0,
3679
+ onDoubleClick: hasCellEditing ? handleCellDoubleClick : void 0,
3680
+ onKeyDown: hasCellEditing ? handleCellKeyDown : void 0,
3681
+ onCopy: hasCellEditing ? handleCopy : void 0,
3682
+ onPaste: hasCellEditing ? handlePaste : void 0,
3683
+ children: [
3684
+ /* @__PURE__ */ jsxs20("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
3685
+ /* @__PURE__ */ jsxs20("thead", { ref: theadRef, className: "bg-muted", children: [
3686
+ table.getHeaderGroups().map((headerGroup) => {
3687
+ const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));
3688
+ function renderLeafHeaderCell(column, rowSpan) {
3689
+ const header = headerById.get(column.id);
3690
+ if (!header) return null;
3691
+ const sortEntry = state.sort.find((s) => s.field === column.id);
3692
+ const sortable = isSortable(column);
3693
+ const filterable = isFilterable(column) && column.filterDisplay !== "row";
3694
+ const cellProps = leafHeaderCellPropsByColumn.get(column.id);
3695
+ const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;
3696
+ return /* @__PURE__ */ jsxs20("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
3697
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-1", children: [
3698
+ sortable ? /* @__PURE__ */ jsxs20(
3699
+ "button",
3700
+ {
3701
+ type: "button",
3702
+ className: "flex items-center gap-1 rounded-sm px-1 hover:bg-accent hover:text-accent-foreground",
3703
+ onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
3704
+ children: [
3705
+ headerContent,
3706
+ sortEntry?.dir === "asc" && /* @__PURE__ */ jsx32(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
3707
+ sortEntry?.dir === "desc" && /* @__PURE__ */ jsx32(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
3708
+ ]
3709
+ }
3710
+ ) : (
3711
+ // Not wrapped in a `<button>` (unlike the sortable branch above): a
3712
+ // disabled `<button>` swallows pointer events for its entire subtree,
3713
+ // silently breaking any interactive content a column's own `renderHeader`
3714
+ // embeds directly (bare filter inputs, "select/reject all" buttons) —
3715
+ // clicks never reach them at all, even though nothing looks wrong visually.
3716
+ /* @__PURE__ */ jsx32("div", { className: "flex items-center gap-1 px-1", children: headerContent })
3717
+ ),
3718
+ filterable && /* @__PURE__ */ jsxs20(Popover, { children: [
3719
+ /* @__PURE__ */ jsx32(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx32(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx32(
3720
+ FunnelIcon7,
3721
+ {
3722
+ className: cn(
3723
+ "h-3 w-3",
3724
+ filtersByColumn.get(column.id) !== void 0 ? "opacity-100 text-primary" : "opacity-40"
3725
+ ),
3726
+ "aria-hidden": true
3727
+ }
3728
+ ) }) }),
3729
+ /* @__PURE__ */ jsx32(PopoverContent, { children: renderFilterWidget(column) })
3730
+ ] })
3731
+ ] }),
3732
+ enableColumnResizing && /* @__PURE__ */ jsx32(
3733
+ "div",
3734
+ {
3735
+ "data-testid": `resize-handle-${column.id}`,
3736
+ onMouseDown: header.getResizeHandler(),
3737
+ onTouchStart: header.getResizeHandler(),
3738
+ className: "absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent"
3739
+ }
3740
+ )
3741
+ ] }, header.id);
3742
+ }
3743
+ return /* @__PURE__ */ jsxs20(Fragment3, { children: [
3744
+ hasHeaderGroups && /* @__PURE__ */ jsxs20("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
3745
+ showDetailColumn && /* @__PURE__ */ jsx32(
3746
+ "th",
3747
+ {
3748
+ rowSpan: 2,
3749
+ className: detailHeaderCellProps.className,
3750
+ style: detailHeaderCellProps.style,
3751
+ "aria-hidden": true
3752
+ }
3753
+ ),
3754
+ showSelectionColumn && /* @__PURE__ */ jsx32(
3755
+ "th",
2775
3756
  {
2776
- type: "button",
2777
- className: "flex items-center gap-1 rounded-sm px-1 hover:bg-accent hover:text-accent-foreground",
2778
- onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
2779
- children: [
2780
- headerContent,
2781
- sortEntry?.dir === "asc" && /* @__PURE__ */ jsx28(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
2782
- sortEntry?.dir === "desc" && /* @__PURE__ */ jsx28(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
2783
- ]
3757
+ rowSpan: 2,
3758
+ className: selectionHeaderCellProps.className,
3759
+ style: selectionHeaderCellProps.style,
3760
+ children: /* @__PURE__ */ jsx32(
3761
+ "input",
3762
+ {
3763
+ type: "checkbox",
3764
+ "aria-label": "Select all rows on this page",
3765
+ checked: allOnPageSelected,
3766
+ onChange: toggleAllOnPageSelected
3767
+ }
3768
+ )
2784
3769
  }
2785
- ) : (
2786
- // Not wrapped in a `<button>` (unlike the sortable branch above): a
2787
- // disabled `<button>` swallows pointer events for its entire subtree,
2788
- // silently breaking any interactive content a column's own `renderHeader`
2789
- // embeds directly (bare filter inputs, "select/reject all" buttons) —
2790
- // clicks never reach them at all, even though nothing looks wrong visually.
2791
- /* @__PURE__ */ jsx28("div", { className: "flex items-center gap-1 px-1", children: headerContent })
2792
3770
  ),
2793
- filterable && /* @__PURE__ */ jsxs18(Popover, { children: [
2794
- /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx28(
2795
- FunnelIcon7,
3771
+ headerRuns.map(
3772
+ (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx32(
3773
+ "th",
2796
3774
  {
2797
- className: cn(
2798
- "h-3 w-3",
2799
- filtersByColumn.get(column.id) !== void 0 ? "opacity-100 text-primary" : "opacity-40"
2800
- ),
2801
- "aria-hidden": true
2802
- }
2803
- ) }) }),
2804
- /* @__PURE__ */ jsx28(PopoverContent, { children: renderFilterWidget(column) })
2805
- ] })
2806
- ] }),
2807
- enableColumnResizing && /* @__PURE__ */ jsx28(
2808
- "div",
2809
- {
2810
- "data-testid": `resize-handle-${column.id}`,
2811
- onMouseDown: header.getResizeHandler(),
2812
- onTouchStart: header.getResizeHandler(),
2813
- className: "absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent"
2814
- }
2815
- )
2816
- ] }, header.id);
2817
- }
2818
- return /* @__PURE__ */ jsxs18(Fragment2, { children: [
2819
- hasHeaderGroups && /* @__PURE__ */ jsxs18("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
2820
- showDetailColumn && /* @__PURE__ */ jsx28(
2821
- "th",
2822
- {
2823
- rowSpan: 2,
2824
- className: detailHeaderCellProps.className,
2825
- style: detailHeaderCellProps.style,
2826
- "aria-hidden": true
2827
- }
2828
- ),
2829
- showSelectionColumn && /* @__PURE__ */ jsx28(
2830
- "th",
2831
- {
2832
- rowSpan: 2,
2833
- className: selectionHeaderCellProps.className,
2834
- style: selectionHeaderCellProps.style,
2835
- children: /* @__PURE__ */ jsx28(
2836
- "input",
2837
- {
2838
- type: "checkbox",
2839
- "aria-label": "Select all rows on this page",
2840
- checked: allOnPageSelected,
2841
- onChange: toggleAllOnPageSelected
2842
- }
3775
+ colSpan: run.columns.length,
3776
+ className: "border-b border-border p-2 text-center font-medium",
3777
+ children: run.label
3778
+ },
3779
+ `group-${run.label}-${run.columns[0].id}`
2843
3780
  )
2844
- }
2845
- ),
2846
- headerRuns.map(
2847
- (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx28(
3781
+ ),
3782
+ showRowActionsColumn && /* @__PURE__ */ jsx32(
2848
3783
  "th",
2849
3784
  {
2850
- colSpan: run.columns.length,
2851
- className: "border-b border-border p-2 text-center font-medium",
2852
- children: run.label
2853
- },
2854
- `group-${run.label}-${run.columns[0].id}`
3785
+ rowSpan: 2,
3786
+ className: rowActionsHeaderCellProps.className,
3787
+ style: rowActionsHeaderCellProps.style,
3788
+ "aria-label": "Row actions"
3789
+ }
2855
3790
  )
2856
- ),
2857
- showRowActionsColumn && /* @__PURE__ */ jsx28(
2858
- "th",
2859
- {
2860
- rowSpan: 2,
2861
- className: rowActionsHeaderCellProps.className,
2862
- style: rowActionsHeaderCellProps.style,
2863
- "aria-label": "Row actions"
2864
- }
2865
- )
2866
- ] }, `${headerGroup.id}-groups`),
2867
- /* @__PURE__ */ jsxs18("tr", { className: "divide-x divide-border", children: [
2868
- !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx28(
2869
- "th",
2870
- {
2871
- className: detailHeaderCellProps.className,
2872
- style: detailHeaderCellProps.style,
2873
- "aria-hidden": true
2874
- }
2875
- ),
2876
- !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx28(
2877
- "th",
2878
- {
2879
- className: selectionHeaderCellProps.className,
2880
- style: selectionHeaderCellProps.style,
2881
- children: /* @__PURE__ */ jsx28(
2882
- "input",
2883
- {
2884
- type: "checkbox",
2885
- "aria-label": "Select all rows on this page",
2886
- checked: allOnPageSelected,
2887
- onChange: toggleAllOnPageSelected
2888
- }
2889
- )
2890
- }
2891
- ),
2892
- headerGroup.headers.map((header) => {
2893
- const column = columnById.get(header.id);
2894
- if (!column || hasHeaderGroups && !column.headerGroup) return null;
2895
- return renderLeafHeaderCell(column);
2896
- }),
2897
- !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx28(
2898
- "th",
2899
- {
2900
- className: rowActionsHeaderCellProps.className,
2901
- style: rowActionsHeaderCellProps.style,
2902
- "aria-label": "Row actions"
2903
- }
2904
- )
2905
- ] }, headerGroup.id)
2906
- ] }, headerGroup.id);
2907
- }),
2908
- hasFilterRow && /* @__PURE__ */ jsxs18("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
2909
- showDetailColumn && /* @__PURE__ */ jsx28("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
2910
- showSelectionColumn && /* @__PURE__ */ jsx28(
2911
- "th",
2912
- {
2913
- className: selectionHeaderCellProps.className,
2914
- style: selectionHeaderCellProps.style
2915
- }
2916
- ),
2917
- visibleColumns.map((column) => {
2918
- const cellProps = filterHeaderCellPropsByColumn.get(column.id);
2919
- return /* @__PURE__ */ jsx28("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
3791
+ ] }, `${headerGroup.id}-groups`),
3792
+ /* @__PURE__ */ jsxs20("tr", { className: "divide-x divide-border", children: [
3793
+ !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx32(
3794
+ "th",
3795
+ {
3796
+ className: detailHeaderCellProps.className,
3797
+ style: detailHeaderCellProps.style,
3798
+ "aria-hidden": true
3799
+ }
3800
+ ),
3801
+ !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx32(
3802
+ "th",
3803
+ {
3804
+ className: selectionHeaderCellProps.className,
3805
+ style: selectionHeaderCellProps.style,
3806
+ children: /* @__PURE__ */ jsx32(
3807
+ "input",
3808
+ {
3809
+ type: "checkbox",
3810
+ "aria-label": "Select all rows on this page",
3811
+ checked: allOnPageSelected,
3812
+ onChange: toggleAllOnPageSelected
3813
+ }
3814
+ )
3815
+ }
3816
+ ),
3817
+ headerGroup.headers.map((header) => {
3818
+ const column = columnById.get(header.id);
3819
+ if (!column || hasHeaderGroups && !column.headerGroup) return null;
3820
+ return renderLeafHeaderCell(column);
3821
+ }),
3822
+ !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx32(
3823
+ "th",
3824
+ {
3825
+ className: rowActionsHeaderCellProps.className,
3826
+ style: rowActionsHeaderCellProps.style,
3827
+ "aria-label": "Row actions"
3828
+ }
3829
+ )
3830
+ ] }, headerGroup.id)
3831
+ ] }, headerGroup.id);
2920
3832
  }),
2921
- showRowActionsColumn && /* @__PURE__ */ jsx28(
2922
- "th",
2923
- {
2924
- className: rowActionsHeaderCellProps.className,
2925
- style: rowActionsHeaderCellProps.style
2926
- }
2927
- )
2928
- ] })
3833
+ hasFilterRow && /* @__PURE__ */ jsxs20("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
3834
+ showDetailColumn && /* @__PURE__ */ jsx32("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
3835
+ showSelectionColumn && /* @__PURE__ */ jsx32(
3836
+ "th",
3837
+ {
3838
+ className: selectionHeaderCellProps.className,
3839
+ style: selectionHeaderCellProps.style
3840
+ }
3841
+ ),
3842
+ visibleColumns.map((column) => {
3843
+ const cellProps = filterHeaderCellPropsByColumn.get(column.id);
3844
+ return /* @__PURE__ */ jsx32("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
3845
+ }),
3846
+ showRowActionsColumn && /* @__PURE__ */ jsx32(
3847
+ "th",
3848
+ {
3849
+ className: rowActionsHeaderCellProps.className,
3850
+ style: rowActionsHeaderCellProps.style
3851
+ }
3852
+ )
3853
+ ] })
3854
+ ] }),
3855
+ rows.length === 0 ? /* @__PURE__ */ jsx32("tbody", { children: /* @__PURE__ */ jsx32("tr", { children: /* @__PURE__ */ jsx32("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? "Loading..." : "No results." }) }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs20(Fragment4, { children: [
3856
+ paddingTop > 0 && /* @__PURE__ */ jsx32("tbody", { children: /* @__PURE__ */ jsx32("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx32("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
3857
+ virtualItems.map(
3858
+ (virtualItem) => renderFlatItem(
3859
+ flatItems[virtualItem.index],
3860
+ virtualItem.index,
3861
+ (el) => virtualizer.measureElement(el)
3862
+ )
3863
+ ),
3864
+ paddingBottom > 0 && /* @__PURE__ */ jsx32("tbody", { children: /* @__PURE__ */ jsx32("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx32("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingBottom } }) }) })
3865
+ ] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
2929
3866
  ] }),
2930
- 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: [
2931
- 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 } }) }) }),
2932
- virtualItems.map(
2933
- (virtualItem) => renderFlatItem(
2934
- flatItems[virtualItem.index],
2935
- virtualItem.index,
2936
- (el) => virtualizer.measureElement(el)
2937
- )
2938
- ),
2939
- 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 } }) }) })
2940
- ] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
2941
- ] })
3867
+ hasCellEditing && /* @__PURE__ */ jsx32(
3868
+ SelectionOverlay,
3869
+ {
3870
+ containerRef: scrollRef,
3871
+ range: cellSelection.effectiveRange,
3872
+ fillPreviewRange: cellSelection.fillPreviewRange,
3873
+ onFillHandleMouseDown: cellSelection.selection && !cellSelection.isDragging && !cellEditingCtx?.editingCell ? handleFillHandleMouseDown : void 0
3874
+ }
3875
+ )
3876
+ ]
2942
3877
  }
2943
3878
  )
2944
3879
  ] }),
2945
- showPagination && /* @__PURE__ */ jsxs18("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
2946
- /* @__PURE__ */ jsxs18("span", { children: [
3880
+ showPagination && /* @__PURE__ */ jsxs20("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
3881
+ /* @__PURE__ */ jsxs20("span", { children: [
2947
3882
  "Page ",
2948
3883
  state.page + 1,
2949
3884
  " of ",
@@ -2954,8 +3889,8 @@ function DataGrid({
2954
3889
  rowCount === 1 ? "" : "s",
2955
3890
  ")"
2956
3891
  ] }),
2957
- /* @__PURE__ */ jsxs18("div", { className: "flex gap-2", children: [
2958
- /* @__PURE__ */ jsx28(
3892
+ /* @__PURE__ */ jsxs20("div", { className: "flex gap-2", children: [
3893
+ /* @__PURE__ */ jsx32(
2959
3894
  Button,
2960
3895
  {
2961
3896
  variant: "outline",
@@ -2965,7 +3900,7 @@ function DataGrid({
2965
3900
  children: "Previous"
2966
3901
  }
2967
3902
  ),
2968
- /* @__PURE__ */ jsx28(
3903
+ /* @__PURE__ */ jsx32(
2969
3904
  Button,
2970
3905
  {
2971
3906
  variant: "outline",
@@ -2984,7 +3919,7 @@ function DataGrid({
2984
3919
  // src/tree/TreeDataGrid.tsx
2985
3920
  import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
2986
3921
  import { AlertCircle, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2, Loader2 as Loader22 } from "lucide-react";
2987
- import { Fragment as Fragment4, useEffect as useEffect10, useMemo as useMemo9, useRef as useRef9 } from "react";
3922
+ import { Fragment as Fragment5, useEffect as useEffect10, useMemo as useMemo10, useRef as useRef12 } from "react";
2988
3923
 
2989
3924
  // src/tree/flattenTree.ts
2990
3925
  function flattenTree(roots, accessors, expanded, childrenOverride) {
@@ -3029,7 +3964,7 @@ function computeSelectionStates(roots, accessors, childrenOverride, selectedIds)
3029
3964
  }
3030
3965
 
3031
3966
  // src/tree/useTreeState.ts
3032
- import { useCallback as useCallback4, useMemo as useMemo8, useState as useState11 } from "react";
3967
+ import { useCallback as useCallback5, useMemo as useMemo9, useState as useState14 } from "react";
3033
3968
  function useTreeState({
3034
3969
  data,
3035
3970
  getRowId,
@@ -3041,9 +3976,9 @@ function useTreeState({
3041
3976
  childrenMap: controlledChildrenMap,
3042
3977
  onChildrenMapChange
3043
3978
  }) {
3044
- const [internalExpanded, setInternalExpanded] = useState11({});
3979
+ const [internalExpanded, setInternalExpanded] = useState14({});
3045
3980
  const expanded = controlledExpanded ?? internalExpanded;
3046
- const setExpanded = useCallback4(
3981
+ const setExpanded = useCallback5(
3047
3982
  (updater) => {
3048
3983
  if (controlledExpanded) {
3049
3984
  onExpandedChange?.(updater(controlledExpanded));
@@ -3053,9 +3988,9 @@ function useTreeState({
3053
3988
  },
3054
3989
  [controlledExpanded, onExpandedChange]
3055
3990
  );
3056
- const [internalChildrenMap, setInternalChildrenMap] = useState11(/* @__PURE__ */ new Map());
3991
+ const [internalChildrenMap, setInternalChildrenMap] = useState14(/* @__PURE__ */ new Map());
3057
3992
  const childrenMap = controlledChildrenMap ?? internalChildrenMap;
3058
- const setChildrenMap = useCallback4(
3993
+ const setChildrenMap = useCallback5(
3059
3994
  (updater) => {
3060
3995
  if (controlledChildrenMap) {
3061
3996
  onChildrenMapChange?.(updater(controlledChildrenMap));
@@ -3065,13 +4000,13 @@ function useTreeState({
3065
4000
  },
3066
4001
  [controlledChildrenMap, onChildrenMapChange]
3067
4002
  );
3068
- const [loadingIds, setLoadingIds] = useState11(/* @__PURE__ */ new Set());
3069
- const [errorIds, setErrorIds] = useState11(/* @__PURE__ */ new Map());
3070
- const resolveHasChildren = useMemo8(
4003
+ const [loadingIds, setLoadingIds] = useState14(/* @__PURE__ */ new Set());
4004
+ const [errorIds, setErrorIds] = useState14(/* @__PURE__ */ new Map());
4005
+ const resolveHasChildren = useMemo9(
3071
4006
  () => hasChildren ?? ((row) => (getChildren(row)?.length ?? 0) > 0),
3072
4007
  [hasChildren, getChildren]
3073
4008
  );
3074
- const loadChildrenFor = useCallback4(
4009
+ const loadChildrenFor = useCallback5(
3075
4010
  async (row) => {
3076
4011
  const id = getRowId(row);
3077
4012
  const cached = childrenMap.get(id);
@@ -3104,7 +4039,7 @@ function useTreeState({
3104
4039
  },
3105
4040
  [childrenMap, getChildren, getRowId, onLoadChildren, setChildrenMap]
3106
4041
  );
3107
- const toggleExpand = useCallback4(
4042
+ const toggleExpand = useCallback5(
3108
4043
  (row) => {
3109
4044
  const id = getRowId(row);
3110
4045
  const willExpand = !expanded[id];
@@ -3116,14 +4051,14 @@ function useTreeState({
3116
4051
  },
3117
4052
  [expanded, getRowId, loadChildrenFor, resolveHasChildren, setExpanded]
3118
4053
  );
3119
- const retry = useCallback4(
4054
+ const retry = useCallback5(
3120
4055
  (row) => {
3121
4056
  loadChildrenFor(row).catch(() => {
3122
4057
  });
3123
4058
  },
3124
4059
  [loadChildrenFor]
3125
4060
  );
3126
- const expandToLevel = useCallback4(
4061
+ const expandToLevel = useCallback5(
3127
4062
  async (level) => {
3128
4063
  const nextExpanded = { ...expanded };
3129
4064
  async function visit(row, depth) {
@@ -3146,7 +4081,7 @@ function useTreeState({
3146
4081
  }
3147
4082
 
3148
4083
  // src/tree/TreeDataGrid.tsx
3149
- import { Fragment as Fragment5, jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
4084
+ import { Fragment as Fragment6, jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
3150
4085
  var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
3151
4086
  var UNCHECKED_STATE = { checked: false, indeterminate: false };
3152
4087
  function renderCell(column, row, editingCtx) {
@@ -3162,8 +4097,8 @@ function TreeCell({
3162
4097
  onRetry,
3163
4098
  editingCtx
3164
4099
  }) {
3165
- return /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
3166
- flatRow.hasChildren ? /* @__PURE__ */ jsx29(
4100
+ return /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
4101
+ flatRow.hasChildren ? /* @__PURE__ */ jsx33(
3167
4102
  "button",
3168
4103
  {
3169
4104
  type: "button",
@@ -3175,14 +4110,14 @@ function TreeCell({
3175
4110
  "aria-label": flatRow.isExpanded ? "Collapse" : "Expand",
3176
4111
  "aria-expanded": flatRow.isExpanded,
3177
4112
  className: "flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50",
3178
- 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" })
4113
+ children: isLoading ? /* @__PURE__ */ jsx33(Loader22, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx33(ChevronDown2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx33(ChevronRight2, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
3179
4114
  }
3180
- ) : /* @__PURE__ */ jsx29("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
3181
- /* @__PURE__ */ jsx29("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
3182
- flatRow.isExpanded && error ? /* @__PURE__ */ jsxs19("span", { className: "ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive", children: [
3183
- /* @__PURE__ */ jsx29(AlertCircle, { className: "h-3 w-3", "aria-hidden": "true" }),
4115
+ ) : /* @__PURE__ */ jsx33("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
4116
+ /* @__PURE__ */ jsx33("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
4117
+ flatRow.isExpanded && error ? /* @__PURE__ */ jsxs21("span", { className: "ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive", children: [
4118
+ /* @__PURE__ */ jsx33(AlertCircle, { className: "h-3 w-3", "aria-hidden": "true" }),
3184
4119
  "Failed to load.",
3185
- /* @__PURE__ */ jsx29("button", { type: "button", className: "underline underline-offset-2", onClick: onRetry, children: "Retry" })
4120
+ /* @__PURE__ */ jsx33("button", { type: "button", className: "underline underline-offset-2", onClick: onRetry, children: "Retry" })
3186
4121
  ] }) : null
3187
4122
  ] });
3188
4123
  }
@@ -3219,7 +4154,7 @@ function TreeDataGrid({
3219
4154
  zebra = true,
3220
4155
  editing
3221
4156
  }) {
3222
- const accessors = useMemo9(
4157
+ const accessors = useMemo10(
3223
4158
  () => ({ getRowId, getChildren, hasChildren }),
3224
4159
  [getRowId, getChildren, hasChildren]
3225
4160
  );
@@ -3235,7 +4170,7 @@ function TreeDataGrid({
3235
4170
  childrenMap: controlledChildrenMap,
3236
4171
  onChildrenMapChange
3237
4172
  });
3238
- const didAutoExpand = useRef9(false);
4173
+ const didAutoExpand = useRef12(false);
3239
4174
  useEffect10(() => {
3240
4175
  if (didAutoExpand.current) return;
3241
4176
  didAutoExpand.current = true;
@@ -3247,11 +4182,11 @@ function TreeDataGrid({
3247
4182
  controlledExpandedGroups,
3248
4183
  onExpandedGroupsChange
3249
4184
  );
3250
- const flatRows = useMemo9(
4185
+ const flatRows = useMemo10(
3251
4186
  () => groupBy ? [] : flattenTree(data, accessors, expanded, childrenMap),
3252
4187
  [groupBy, data, accessors, expanded, childrenMap]
3253
4188
  );
3254
- const groupedBuckets = useMemo9(
4189
+ const groupedBuckets = useMemo10(
3255
4190
  () => groupBy ? groupRows(data, groupBy).map((bucket) => ({
3256
4191
  key: bucket.key,
3257
4192
  roots: bucket.items,
@@ -3259,7 +4194,7 @@ function TreeDataGrid({
3259
4194
  })) : void 0,
3260
4195
  [groupBy, data, accessors, expanded, childrenMap]
3261
4196
  );
3262
- const groupedRender = useMemo9(() => {
4197
+ const groupedRender = useMemo10(() => {
3263
4198
  if (!groupedBuckets) return void 0;
3264
4199
  const startIndexByBucket = /* @__PURE__ */ new Map();
3265
4200
  const rows = [];
@@ -3276,7 +4211,7 @@ function TreeDataGrid({
3276
4211
  const showSelectionColumn = controlledSelectedIds !== void 0;
3277
4212
  const totalColumnCount = visibleColumns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);
3278
4213
  const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));
3279
- const selectionStates = useMemo9(
4214
+ const selectionStates = useMemo10(
3280
4215
  () => showSelectionColumn ? computeSelectionStates(data, accessors, childrenMap, controlledSelectedIds ?? EMPTY_SELECTED_IDS) : void 0,
3281
4216
  [showSelectionColumn, data, accessors, childrenMap, controlledSelectedIds]
3282
4217
  );
@@ -3292,7 +4227,7 @@ function TreeDataGrid({
3292
4227
  else next.add(id);
3293
4228
  onSelectedIdsChange?.(next);
3294
4229
  }
3295
- const selectableFlatRows = useMemo9(
4230
+ const selectableFlatRows = useMemo10(
3296
4231
  () => allFlatRows.filter((flatRow) => !(isRowSelectionDisabled?.(flatRow.row) ?? false)),
3297
4232
  [allFlatRows, isRowSelectionDisabled]
3298
4233
  );
@@ -3306,12 +4241,12 @@ function TreeDataGrid({
3306
4241
  }
3307
4242
  onSelectedIdsChange?.(next);
3308
4243
  }
3309
- const bodyTdClassByColumn = useMemo9(() => {
4244
+ const bodyTdClassByColumn = useMemo10(() => {
3310
4245
  const map = /* @__PURE__ */ new Map();
3311
4246
  for (const column of visibleColumns) map.set(column.id, `p-2 ${alignClassName(column)}`);
3312
4247
  return map;
3313
4248
  }, [visibleColumns]);
3314
- const scrollRef = useRef9(null);
4249
+ const scrollRef = useRef12(null);
3315
4250
  const shouldVirtualize = !groupBy && flatRows.length > virtualizeThreshold;
3316
4251
  const virtualizer = useVirtualizer2({
3317
4252
  count: flatRows.length,
@@ -3324,7 +4259,7 @@ function TreeDataGrid({
3324
4259
  const rowProps = getRowProps?.(flatRow.row, flatRow.depth, index);
3325
4260
  const rowTestId = getRowTestId?.(flatRow.row) ?? `tree-row-${flatRow.id}`;
3326
4261
  const isOddRow = zebra && index % 2 === 1;
3327
- return /* @__PURE__ */ jsxs19(
4262
+ return /* @__PURE__ */ jsxs21(
3328
4263
  "tr",
3329
4264
  {
3330
4265
  ref: measureRef,
@@ -3337,9 +4272,9 @@ function TreeDataGrid({
3337
4272
  isOddRow && "bg-foreground/5"
3338
4273
  ) : `border-b border-border divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
3339
4274
  children: [
3340
- showSelectionColumn && /* @__PURE__ */ jsx29("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
4275
+ showSelectionColumn && /* @__PURE__ */ jsx33("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
3341
4276
  const state = resolveSelectionState(flatRow.row, flatRow.id);
3342
- return /* @__PURE__ */ jsx29(
4277
+ return /* @__PURE__ */ jsx33(
3343
4278
  Checkbox,
3344
4279
  {
3345
4280
  "aria-label": `Select row ${flatRow.id}`,
@@ -3350,7 +4285,7 @@ function TreeDataGrid({
3350
4285
  }
3351
4286
  );
3352
4287
  })() }),
3353
- visibleColumns.map((column) => /* @__PURE__ */ jsx29("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx29(
4288
+ visibleColumns.map((column) => /* @__PURE__ */ jsx33("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx33(
3354
4289
  TreeCell,
3355
4290
  {
3356
4291
  flatRow,
@@ -3363,7 +4298,7 @@ function TreeDataGrid({
3363
4298
  editingCtx: editingState.ctx
3364
4299
  }
3365
4300
  ) : renderCell(column, flatRow.row, editingState.ctx) }, column.id)),
3366
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx29("td", { className: "p-2", children: /* @__PURE__ */ jsx29(
4301
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx33("td", { className: "p-2", children: /* @__PURE__ */ jsx33(
3367
4302
  ActionsMenu,
3368
4303
  {
3369
4304
  items: rowActions,
@@ -3379,8 +4314,8 @@ function TreeDataGrid({
3379
4314
  const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];
3380
4315
  const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
3381
4316
  const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
3382
- return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: [
3383
- editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx29(
4317
+ return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: [
4318
+ editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx33(
3384
4319
  EditingBar,
3385
4320
  {
3386
4321
  editing,
@@ -3391,15 +4326,15 @@ function TreeDataGrid({
3391
4326
  testIdPrefix: "tree-datagrid"
3392
4327
  }
3393
4328
  ),
3394
- /* @__PURE__ */ jsx29(
4329
+ /* @__PURE__ */ jsx33(
3395
4330
  "div",
3396
4331
  {
3397
4332
  ref: scrollRef,
3398
4333
  className: shouldVirtualize ? "overflow-y-auto" : void 0,
3399
4334
  style: shouldVirtualize ? { maxHeight: maxBodyHeight } : void 0,
3400
- children: /* @__PURE__ */ jsxs19("table", { className: "w-full border-collapse text-sm", children: [
3401
- /* @__PURE__ */ jsx29("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs19("tr", { className: "divide-x divide-border", children: [
3402
- showSelectionColumn && /* @__PURE__ */ jsx29("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx29(
4335
+ children: /* @__PURE__ */ jsxs21("table", { className: "w-full border-collapse text-sm", children: [
4336
+ /* @__PURE__ */ jsx33("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs21("tr", { className: "divide-x divide-border", children: [
4337
+ showSelectionColumn && /* @__PURE__ */ jsx33("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx33(
3403
4338
  Checkbox,
3404
4339
  {
3405
4340
  "aria-label": "Select all visible rows",
@@ -3407,7 +4342,7 @@ function TreeDataGrid({
3407
4342
  onCheckedChange: toggleAllVisibleSelected
3408
4343
  }
3409
4344
  ) }),
3410
- visibleColumns.map((column) => /* @__PURE__ */ jsx29(
4345
+ visibleColumns.map((column) => /* @__PURE__ */ jsx33(
3411
4346
  "th",
3412
4347
  {
3413
4348
  style: column.width ? { width: column.width } : void 0,
@@ -3416,12 +4351,12 @@ function TreeDataGrid({
3416
4351
  },
3417
4352
  column.id
3418
4353
  )),
3419
- showRowActionsColumn && /* @__PURE__ */ jsx29("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
4354
+ showRowActionsColumn && /* @__PURE__ */ jsx33("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
3420
4355
  ] }) }),
3421
- /* @__PURE__ */ jsx29("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx29("tr", { children: /* @__PURE__ */ jsx29("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: "No results." }) }) : groupedBuckets ? groupedBuckets.map((bucket) => {
4356
+ /* @__PURE__ */ jsx33("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx33("tr", { children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: "No results." }) }) : groupedBuckets ? groupedBuckets.map((bucket) => {
3422
4357
  const groupExpanded = isGroupExpanded(bucket.key);
3423
- return /* @__PURE__ */ jsxs19(Fragment4, { children: [
3424
- /* @__PURE__ */ jsx29("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx29(
4358
+ return /* @__PURE__ */ jsxs21(Fragment5, { children: [
4359
+ /* @__PURE__ */ jsx33("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx33(
3425
4360
  "td",
3426
4361
  {
3427
4362
  colSpan: totalColumnCount,
@@ -3430,7 +4365,7 @@ function TreeDataGrid({
3430
4365
  zebra ? "bg-muted" : "bg-background"
3431
4366
  ),
3432
4367
  style: { top: groupHeaderTop },
3433
- children: /* @__PURE__ */ jsxs19(
4368
+ children: /* @__PURE__ */ jsxs21(
3434
4369
  "button",
3435
4370
  {
3436
4371
  type: "button",
@@ -3438,7 +4373,7 @@ function TreeDataGrid({
3438
4373
  onClick: () => toggleGroupExpanded(bucket.key),
3439
4374
  "aria-expanded": groupExpanded,
3440
4375
  children: [
3441
- /* @__PURE__ */ jsx29(
4376
+ /* @__PURE__ */ jsx33(
3442
4377
  ChevronRight2,
3443
4378
  {
3444
4379
  className: `h-4 w-4 shrink-0 transition-transform${groupExpanded ? " rotate-90" : ""}`,
@@ -3456,12 +4391,12 @@ function TreeDataGrid({
3456
4391
  return bucket.flatRows.map((flatRow, i) => renderRow(flatRow, startIndex + i));
3457
4392
  })()
3458
4393
  ] }, `group-${bucket.key}`);
3459
- }) : shouldVirtualize ? /* @__PURE__ */ jsxs19(Fragment5, { children: [
3460
- paddingTop > 0 && /* @__PURE__ */ jsx29("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx29("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
4394
+ }) : shouldVirtualize ? /* @__PURE__ */ jsxs21(Fragment6, { children: [
4395
+ paddingTop > 0 && /* @__PURE__ */ jsx33("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
3461
4396
  virtualItems.map(
3462
4397
  (virtualItem) => renderRow(flatRows[virtualItem.index], virtualItem.index, (el) => virtualizer.measureElement(el))
3463
4398
  ),
3464
- paddingBottom > 0 && /* @__PURE__ */ jsx29("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx29(
4399
+ paddingBottom > 0 && /* @__PURE__ */ jsx33("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx33(
3465
4400
  "td",
3466
4401
  {
3467
4402
  colSpan: totalColumnCount,
@@ -3486,40 +4421,54 @@ export {
3486
4421
  EditingBar,
3487
4422
  EnumEditor,
3488
4423
  EnumFilter,
4424
+ MultilineStringEditor,
3489
4425
  NumberComparisonFilter,
3490
4426
  NumberEditor,
3491
4427
  NumberHistogramFilter,
3492
4428
  NumberRangeFilter,
4429
+ SelectionOverlay,
3493
4430
  StringEditor,
3494
4431
  StringFilter,
3495
4432
  TreeDataGrid,
3496
4433
  UNARY_OPERATORS,
3497
4434
  alignClassName,
4435
+ buildIndexMap,
3498
4436
  canHideColumn,
3499
4437
  canHideGroup,
4438
+ cellsInRange,
4439
+ coerceValueForColumn,
4440
+ computeFillChanges,
3500
4441
  countVisible,
3501
4442
  defaultAlign,
3502
4443
  defaultFormat,
3503
4444
  editErrorId,
3504
4445
  evaluateFilter,
4446
+ extendRangeForFill,
3505
4447
  facetedNumberValues,
3506
4448
  fieldKey,
3507
4449
  flattenTree,
3508
4450
  getColumnValue,
3509
4451
  groupColumns,
4452
+ isCellInRange,
3510
4453
  isColumnVisible,
3511
4454
  isCompositeFilterDescriptor,
3512
4455
  isEditable,
3513
4456
  isFilterDescriptor,
3514
4457
  isFilterable,
3515
4458
  isSortable,
4459
+ normalizeRange,
4460
+ parseTsv,
3516
4461
  processClientData,
4462
+ rangeToTsv,
4463
+ renderCellModeCell,
3517
4464
  renderDefaultEditWidget,
3518
4465
  renderDefaultFilterWidget,
3519
4466
  renderEditableCell,
3520
4467
  resolveMenuItems,
3521
4468
  storageKeyFor,
3522
4469
  toDate,
4470
+ useCellEditingState,
4471
+ useCellSelection,
3523
4472
  useDebouncedCallback,
3524
4473
  useEditingState,
3525
4474
  useGridState,