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