@bmsuisse/datagrid 0.23.2 → 0.24.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 +75 -2
- package/dist/index.js +399 -135
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -192,7 +192,8 @@ var buttonVariants = cva(
|
|
|
192
192
|
var Button = forwardRef(
|
|
193
193
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
194
194
|
const Comp = asChild ? Slot : "button";
|
|
195
|
-
|
|
195
|
+
const variantClass = buttonVariants({ variant, size });
|
|
196
|
+
return /* @__PURE__ */ jsx(Comp, { className: className ? cn(variantClass, className) : variantClass, ref, ...props });
|
|
196
197
|
}
|
|
197
198
|
);
|
|
198
199
|
Button.displayName = "Button";
|
|
@@ -337,7 +338,13 @@ function BooleanFilter({
|
|
|
337
338
|
"aria-label": `Filter ${column.header}`,
|
|
338
339
|
"data-testid": `filter-${column.id}`,
|
|
339
340
|
children: [
|
|
340
|
-
/* @__PURE__ */ jsx4(
|
|
341
|
+
/* @__PURE__ */ jsx4(
|
|
342
|
+
FunnelIcon,
|
|
343
|
+
{
|
|
344
|
+
className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
|
|
345
|
+
"aria-hidden": true
|
|
346
|
+
}
|
|
347
|
+
),
|
|
341
348
|
isFiltered && /* @__PURE__ */ jsx4("span", { className: "truncate", children: summary })
|
|
342
349
|
]
|
|
343
350
|
}
|
|
@@ -591,7 +598,13 @@ function EnumFilter({
|
|
|
591
598
|
"aria-label": `Filter ${column.header}`,
|
|
592
599
|
"data-testid": `filter-${column.id}`,
|
|
593
600
|
children: [
|
|
594
|
-
/* @__PURE__ */ jsx9(
|
|
601
|
+
/* @__PURE__ */ jsx9(
|
|
602
|
+
FunnelIcon2,
|
|
603
|
+
{
|
|
604
|
+
className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
|
|
605
|
+
"aria-hidden": true
|
|
606
|
+
}
|
|
607
|
+
),
|
|
595
608
|
isFiltered && /* @__PURE__ */ jsx9("span", { className: "truncate", children: summary })
|
|
596
609
|
]
|
|
597
610
|
}
|
|
@@ -648,6 +661,15 @@ function toNumber(value) {
|
|
|
648
661
|
}
|
|
649
662
|
return null;
|
|
650
663
|
}
|
|
664
|
+
var currencyFormatters = /* @__PURE__ */ new Map();
|
|
665
|
+
function currencyFormatter(currency) {
|
|
666
|
+
let formatter = currencyFormatters.get(currency);
|
|
667
|
+
if (!formatter) {
|
|
668
|
+
formatter = new Intl.NumberFormat(void 0, { style: "currency", currency });
|
|
669
|
+
currencyFormatters.set(currency, formatter);
|
|
670
|
+
}
|
|
671
|
+
return formatter;
|
|
672
|
+
}
|
|
651
673
|
function defaultFormat(column, value) {
|
|
652
674
|
if (value === null || value === void 0) return "";
|
|
653
675
|
switch (column.type) {
|
|
@@ -660,10 +682,7 @@ function defaultFormat(column, value) {
|
|
|
660
682
|
case "currency": {
|
|
661
683
|
const num = toNumber(value);
|
|
662
684
|
if (num === null) return String(value);
|
|
663
|
-
return
|
|
664
|
-
style: "currency",
|
|
665
|
-
currency: column.currency ?? "USD"
|
|
666
|
-
}).format(num);
|
|
685
|
+
return currencyFormatter(column.currency ?? "USD").format(num);
|
|
667
686
|
}
|
|
668
687
|
case "date": {
|
|
669
688
|
const date = toDate(value);
|
|
@@ -870,7 +889,13 @@ function NumberComparisonFilter({
|
|
|
870
889
|
"aria-label": `Filter ${column.header}`,
|
|
871
890
|
"data-testid": `filter-${column.id}`,
|
|
872
891
|
children: [
|
|
873
|
-
/* @__PURE__ */ jsx10(
|
|
892
|
+
/* @__PURE__ */ jsx10(
|
|
893
|
+
FunnelIcon3,
|
|
894
|
+
{
|
|
895
|
+
className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
|
|
896
|
+
"aria-hidden": true
|
|
897
|
+
}
|
|
898
|
+
),
|
|
874
899
|
isFiltered && /* @__PURE__ */ jsx10("span", { className: "truncate", children: summary })
|
|
875
900
|
]
|
|
876
901
|
}
|
|
@@ -964,7 +989,13 @@ function NumberRangeFilter({
|
|
|
964
989
|
"aria-label": `Filter ${column.header}`,
|
|
965
990
|
"data-testid": `filter-${column.id}`,
|
|
966
991
|
children: [
|
|
967
|
-
/* @__PURE__ */ jsx11(
|
|
992
|
+
/* @__PURE__ */ jsx11(
|
|
993
|
+
FunnelIcon4,
|
|
994
|
+
{
|
|
995
|
+
className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
|
|
996
|
+
"aria-hidden": true
|
|
997
|
+
}
|
|
998
|
+
),
|
|
968
999
|
isFiltered && /* @__PURE__ */ jsx11("span", { className: "truncate", children: summary })
|
|
969
1000
|
]
|
|
970
1001
|
}
|
|
@@ -1177,7 +1208,13 @@ function NumberHistogramFilter({
|
|
|
1177
1208
|
"aria-label": `Filter ${column.header}`,
|
|
1178
1209
|
"data-testid": `filter-${column.id}`,
|
|
1179
1210
|
children: [
|
|
1180
|
-
/* @__PURE__ */ jsx12(
|
|
1211
|
+
/* @__PURE__ */ jsx12(
|
|
1212
|
+
FunnelIcon5,
|
|
1213
|
+
{
|
|
1214
|
+
className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
|
|
1215
|
+
"aria-hidden": true
|
|
1216
|
+
}
|
|
1217
|
+
),
|
|
1181
1218
|
isFiltered && /* @__PURE__ */ jsx12("span", { className: "truncate", children: summary })
|
|
1182
1219
|
]
|
|
1183
1220
|
}
|
|
@@ -1293,7 +1330,13 @@ function StringFilter({
|
|
|
1293
1330
|
"aria-label": `Filter ${column.header}`,
|
|
1294
1331
|
"data-testid": `filter-${column.id}`,
|
|
1295
1332
|
children: [
|
|
1296
|
-
/* @__PURE__ */ jsx13(
|
|
1333
|
+
/* @__PURE__ */ jsx13(
|
|
1334
|
+
FunnelIcon6,
|
|
1335
|
+
{
|
|
1336
|
+
className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
|
|
1337
|
+
"aria-hidden": true
|
|
1338
|
+
}
|
|
1339
|
+
),
|
|
1297
1340
|
isFiltered && /* @__PURE__ */ jsx13("span", { className: "truncate", children: text })
|
|
1298
1341
|
]
|
|
1299
1342
|
}
|
|
@@ -1508,6 +1551,7 @@ function resolveMenuItems(items, ctx) {
|
|
|
1508
1551
|
|
|
1509
1552
|
// src/menu/ActionsMenu.tsx
|
|
1510
1553
|
import { MoreVertical } from "lucide-react";
|
|
1554
|
+
import { useMemo as useMemo3 } from "react";
|
|
1511
1555
|
|
|
1512
1556
|
// src/components/ui/dropdown-menu.tsx
|
|
1513
1557
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
@@ -1563,7 +1607,10 @@ DropdownMenuLabel.displayName = "DropdownMenuLabel";
|
|
|
1563
1607
|
// src/menu/ActionsMenu.tsx
|
|
1564
1608
|
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1565
1609
|
function ActionsMenu({ items, ctx, triggerLabel }) {
|
|
1566
|
-
const resolved =
|
|
1610
|
+
const resolved = useMemo3(
|
|
1611
|
+
() => resolveMenuItems(items, ctx),
|
|
1612
|
+
[items, ctx.row, ctx.selectedRows]
|
|
1613
|
+
);
|
|
1567
1614
|
if (resolved.length === 0) return null;
|
|
1568
1615
|
return /* @__PURE__ */ jsxs11(DropdownMenu, { children: [
|
|
1569
1616
|
/* @__PURE__ */ jsx18(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx18(Button, { variant: "ghost", size: "icon", "aria-label": triggerLabel, onClick: stopRowClick, children: /* @__PURE__ */ jsx18(MoreVertical, { className: "h-4 w-4", "aria-hidden": true }) }) }),
|
|
@@ -1585,7 +1632,22 @@ import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon as FunnelIcon7 } from "@
|
|
|
1585
1632
|
import { columnResizingFeature, columnSizingFeature, flexRender, tableFeatures, useTable } from "@tanstack/react-table";
|
|
1586
1633
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
1587
1634
|
import { ChevronRight, Loader2 } from "lucide-react";
|
|
1588
|
-
import { Fragment as Fragment2, useEffect as useEffect7, useMemo as
|
|
1635
|
+
import { Fragment as Fragment2, useEffect as useEffect7, useMemo as useMemo6, useRef as useRef5, useState as useState6 } from "react";
|
|
1636
|
+
|
|
1637
|
+
// src/lib/structuralColumns.ts
|
|
1638
|
+
var SELECTION_COLUMN_WIDTH = 44;
|
|
1639
|
+
|
|
1640
|
+
// src/grid/groupRows.ts
|
|
1641
|
+
function groupRows(items, groupBy) {
|
|
1642
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1643
|
+
for (const item of items) {
|
|
1644
|
+
const key = groupBy(item);
|
|
1645
|
+
const bucket = buckets.get(key);
|
|
1646
|
+
if (bucket) bucket.push(item);
|
|
1647
|
+
else buckets.set(key, [item]);
|
|
1648
|
+
}
|
|
1649
|
+
return Array.from(buckets, ([key, groupItems]) => ({ key, items: groupItems }));
|
|
1650
|
+
}
|
|
1589
1651
|
|
|
1590
1652
|
// src/grid/processClientData.ts
|
|
1591
1653
|
function makeComparator(sort) {
|
|
@@ -1613,10 +1675,10 @@ function processClientData(data, state) {
|
|
|
1613
1675
|
}
|
|
1614
1676
|
|
|
1615
1677
|
// src/grid/useGridState.ts
|
|
1616
|
-
import { useCallback as useCallback2, useEffect as useEffect6, useMemo as
|
|
1678
|
+
import { useCallback as useCallback2, useEffect as useEffect6, useMemo as useMemo5, useState as useState5 } from "react";
|
|
1617
1679
|
|
|
1618
1680
|
// src/hooks/useDebouncedCallback.ts
|
|
1619
|
-
import { useEffect as useEffect5, useMemo as
|
|
1681
|
+
import { useEffect as useEffect5, useMemo as useMemo4, useRef as useRef4 } from "react";
|
|
1620
1682
|
function useDebouncedCallback(callback, delayMs) {
|
|
1621
1683
|
const callbackRef = useRef4(callback);
|
|
1622
1684
|
callbackRef.current = callback;
|
|
@@ -1626,7 +1688,7 @@ function useDebouncedCallback(callback, delayMs) {
|
|
|
1626
1688
|
clearTimeout(timeoutRef.current);
|
|
1627
1689
|
};
|
|
1628
1690
|
}, []);
|
|
1629
|
-
return
|
|
1691
|
+
return useMemo4(() => {
|
|
1630
1692
|
return {
|
|
1631
1693
|
run: (...args) => {
|
|
1632
1694
|
clearTimeout(timeoutRef.current);
|
|
@@ -1676,7 +1738,7 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
1676
1738
|
cancelDebouncedNotify();
|
|
1677
1739
|
setState(externalState);
|
|
1678
1740
|
}, [externalState, cancelDebouncedNotify]);
|
|
1679
|
-
const filtersByColumn =
|
|
1741
|
+
const filtersByColumn = useMemo5(() => {
|
|
1680
1742
|
const map = /* @__PURE__ */ new Map();
|
|
1681
1743
|
for (const child of state.filter?.filters ?? []) {
|
|
1682
1744
|
if ("operator" in child) map.set(fieldKey(child.field), child);
|
|
@@ -1744,8 +1806,8 @@ import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-r
|
|
|
1744
1806
|
var gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });
|
|
1745
1807
|
var DEFAULT_COLUMN_SIZE = 150;
|
|
1746
1808
|
var DETAIL_COLUMN_WIDTH = 44;
|
|
1747
|
-
var SELECTION_COLUMN_WIDTH = 44;
|
|
1748
1809
|
var ROW_ACTIONS_COLUMN_WIDTH = 44;
|
|
1810
|
+
var STRUCTURAL_ZEBRA_BG_CLASS = "bg-[color-mix(in_srgb,var(--color-foreground)_5%,var(--color-background))]";
|
|
1749
1811
|
function pinnedOffsets(visibleColumns, side, getSize, leadingOffset) {
|
|
1750
1812
|
const offsets = /* @__PURE__ */ new Map();
|
|
1751
1813
|
let cursor = leadingOffset;
|
|
@@ -1802,7 +1864,12 @@ function DataGrid({
|
|
|
1802
1864
|
selectedIds: controlledSelectedIds,
|
|
1803
1865
|
onSelectedIdsChange,
|
|
1804
1866
|
testId,
|
|
1805
|
-
loading: loadingProp = false
|
|
1867
|
+
loading: loadingProp = false,
|
|
1868
|
+
groupBy,
|
|
1869
|
+
renderGroupHeader,
|
|
1870
|
+
defaultGroupsExpanded = true,
|
|
1871
|
+
expandedGroups: controlledExpandedGroups,
|
|
1872
|
+
onExpandedGroupsChange
|
|
1806
1873
|
}) {
|
|
1807
1874
|
const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(
|
|
1808
1875
|
dataSource,
|
|
@@ -1830,16 +1897,28 @@ function DataGrid({
|
|
|
1830
1897
|
return next;
|
|
1831
1898
|
});
|
|
1832
1899
|
}
|
|
1833
|
-
const
|
|
1900
|
+
const [internalExpandedGroups, setInternalExpandedGroups] = useState6({});
|
|
1901
|
+
const expandedGroupsState = controlledExpandedGroups ?? internalExpandedGroups;
|
|
1902
|
+
function updateExpandedGroups(next) {
|
|
1903
|
+
setInternalExpandedGroups(next);
|
|
1904
|
+
onExpandedGroupsChange?.(next);
|
|
1905
|
+
}
|
|
1906
|
+
function isGroupExpanded(key) {
|
|
1907
|
+
return expandedGroupsState[key] ?? defaultGroupsExpanded;
|
|
1908
|
+
}
|
|
1909
|
+
function toggleGroupExpanded(key) {
|
|
1910
|
+
updateExpandedGroups({ ...expandedGroupsState, [key]: !isGroupExpanded(key) });
|
|
1911
|
+
}
|
|
1912
|
+
const visibleColumns = useMemo6(
|
|
1834
1913
|
() => columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,
|
|
1835
1914
|
[columns, columnVisibility]
|
|
1836
1915
|
);
|
|
1837
1916
|
const serverRowCount = dataSource.mode === "server" ? dataSource.rowCount : void 0;
|
|
1838
|
-
const { rows, rowCount } =
|
|
1917
|
+
const { rows, rowCount } = useMemo6(() => {
|
|
1839
1918
|
if (dataSource.mode === "client") return processClientData(dataSource.data, state);
|
|
1840
1919
|
return { rows: dataSource.data, rowCount: serverRowCount ?? 0 };
|
|
1841
1920
|
}, [dataSource.mode, dataSource.data, serverRowCount, state]);
|
|
1842
|
-
const selectedRows =
|
|
1921
|
+
const selectedRows = useMemo6(
|
|
1843
1922
|
() => rows.filter((row) => selectedIds.has(getRowId(row))),
|
|
1844
1923
|
[rows, selectedIds, getRowId]
|
|
1845
1924
|
);
|
|
@@ -1858,8 +1937,8 @@ function DataGrid({
|
|
|
1858
1937
|
}
|
|
1859
1938
|
updateSelectedIds(next);
|
|
1860
1939
|
}
|
|
1861
|
-
const tanstackColumns =
|
|
1862
|
-
const columnById =
|
|
1940
|
+
const tanstackColumns = useMemo6(() => toTanstackColumns(visibleColumns), [visibleColumns]);
|
|
1941
|
+
const columnById = useMemo6(() => {
|
|
1863
1942
|
const map = /* @__PURE__ */ new Map();
|
|
1864
1943
|
for (const column of visibleColumns) map.set(column.id, column);
|
|
1865
1944
|
return map;
|
|
@@ -1884,7 +1963,7 @@ function DataGrid({
|
|
|
1884
1963
|
const showDetailColumn = Boolean(renderDetail);
|
|
1885
1964
|
const totalColumnCount = visibleColumns.length + (showDetailColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0) + (showRowActionsColumn ? 1 : 0);
|
|
1886
1965
|
const hasFilterRow = visibleColumns.some((column) => isFilterable(column) && column.filterDisplay === "row");
|
|
1887
|
-
const headerRuns =
|
|
1966
|
+
const headerRuns = useMemo6(() => computeHeaderRuns(visibleColumns), [visibleColumns]);
|
|
1888
1967
|
const hasHeaderGroups = visibleColumns.some((column) => column.headerGroup);
|
|
1889
1968
|
function renderFilterWidget(column) {
|
|
1890
1969
|
const value = filtersByColumn.get(column.id);
|
|
@@ -1894,22 +1973,34 @@ function DataGrid({
|
|
|
1894
1973
|
const columnSize = (columnId) => table.getColumn(columnId)?.getSize() ?? DEFAULT_COLUMN_SIZE;
|
|
1895
1974
|
const leadingStructuralWidth = (showDetailColumn ? DETAIL_COLUMN_WIDTH : 0) + (showSelectionColumn ? SELECTION_COLUMN_WIDTH : 0);
|
|
1896
1975
|
const trailingStructuralWidth = showRowActionsColumn ? ROW_ACTIONS_COLUMN_WIDTH : 0;
|
|
1897
|
-
const leftPinnedOffsets =
|
|
1898
|
-
|
|
1976
|
+
const leftPinnedOffsets = useMemo6(
|
|
1977
|
+
() => pinnedOffsets(visibleColumns, "left", columnSize, leadingStructuralWidth),
|
|
1978
|
+
[visibleColumns, columnSizing, leadingStructuralWidth]
|
|
1979
|
+
);
|
|
1980
|
+
const rightPinnedOffsets = useMemo6(
|
|
1981
|
+
() => pinnedOffsets(visibleColumns, "right", columnSize, trailingStructuralWidth),
|
|
1982
|
+
[visibleColumns, columnSizing, trailingStructuralWidth]
|
|
1983
|
+
);
|
|
1899
1984
|
function pinnedCellProps(column, area = "body") {
|
|
1900
1985
|
const bg = area === "header" ? "bg-muted" : "bg-background";
|
|
1901
1986
|
if (column.pinned === "left") {
|
|
1902
|
-
return { className:
|
|
1987
|
+
return { className: `sticky z-10 ${bg}`, style: { left: leftPinnedOffsets.get(column.id), width: columnSize(column.id) } };
|
|
1903
1988
|
}
|
|
1904
1989
|
if (column.pinned === "right") {
|
|
1905
|
-
return { className:
|
|
1990
|
+
return { className: `sticky z-10 ${bg}`, style: { right: rightPinnedOffsets.get(column.id), width: columnSize(column.id) } };
|
|
1906
1991
|
}
|
|
1907
1992
|
if (enableColumnResizing) return { style: { width: columnSize(column.id) } };
|
|
1908
1993
|
return { style: column.width ? { width: column.width } : void 0 };
|
|
1909
1994
|
}
|
|
1910
1995
|
function structuralCellProps(side, offset, width, area = "body") {
|
|
1996
|
+
const base = "border-b border-border p-1 sticky z-10";
|
|
1911
1997
|
const bg = area === "header" ? "bg-muted" : "bg-background";
|
|
1912
|
-
|
|
1998
|
+
const bgOdd = area === "header" ? "bg-muted" : STRUCTURAL_ZEBRA_BG_CLASS;
|
|
1999
|
+
return {
|
|
2000
|
+
className: `${base} ${bg}`,
|
|
2001
|
+
classNameOdd: `${base} ${bgOdd}`,
|
|
2002
|
+
style: { [side]: offset, width }
|
|
2003
|
+
};
|
|
1913
2004
|
}
|
|
1914
2005
|
const detailCellProps = structuralCellProps("left", 0, DETAIL_COLUMN_WIDTH);
|
|
1915
2006
|
const selectionCellProps = structuralCellProps(
|
|
@@ -1926,8 +2017,45 @@ function DataGrid({
|
|
|
1926
2017
|
"header"
|
|
1927
2018
|
);
|
|
1928
2019
|
const rowActionsHeaderCellProps = structuralCellProps("right", 0, ROW_ACTIONS_COLUMN_WIDTH, "header");
|
|
2020
|
+
const BODY_TD_BASE_CLASS = "border-b border-border p-2";
|
|
2021
|
+
function bodyCellClassAndStyle(column) {
|
|
2022
|
+
const pinnedProps = column.pinned || enableColumnResizing ? pinnedCellProps(column) : void 0;
|
|
2023
|
+
return {
|
|
2024
|
+
className: pinnedProps?.className ? `${BODY_TD_BASE_CLASS} ${pinnedProps.className}` : BODY_TD_BASE_CLASS,
|
|
2025
|
+
style: pinnedProps?.style
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
const bodyCellPropsByColumn = useMemo6(() => {
|
|
2029
|
+
const map = /* @__PURE__ */ new Map();
|
|
2030
|
+
for (const column of visibleColumns) map.set(column.id, bodyCellClassAndStyle(column));
|
|
2031
|
+
return map;
|
|
2032
|
+
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
2033
|
+
function headerCellClassAndStyle(column, base) {
|
|
2034
|
+
const pinnedProps = pinnedCellProps(column, "header");
|
|
2035
|
+
const align = alignClassName(column);
|
|
2036
|
+
const className = pinnedProps.className ? `${base} ${align} ${pinnedProps.className}` : `${base} ${align}`;
|
|
2037
|
+
return { className, style: pinnedProps.style };
|
|
2038
|
+
}
|
|
2039
|
+
const leafHeaderCellPropsByColumn = useMemo6(() => {
|
|
2040
|
+
const map = /* @__PURE__ */ new Map();
|
|
2041
|
+
for (const column of visibleColumns) {
|
|
2042
|
+
map.set(column.id, headerCellClassAndStyle(column, "relative border-b border-border p-2 font-medium"));
|
|
2043
|
+
}
|
|
2044
|
+
return map;
|
|
2045
|
+
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
2046
|
+
const filterHeaderCellPropsByColumn = useMemo6(() => {
|
|
2047
|
+
const map = /* @__PURE__ */ new Map();
|
|
2048
|
+
for (const column of visibleColumns) {
|
|
2049
|
+
map.set(column.id, headerCellClassAndStyle(column, "border-b border-border p-2 align-top font-normal"));
|
|
2050
|
+
}
|
|
2051
|
+
return map;
|
|
2052
|
+
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
1929
2053
|
const tableRows = table.getRowModel().rows;
|
|
1930
|
-
const shouldVirtualize = Boolean(virtualize) && tableRows.length > (virtualize?.threshold ?? 100);
|
|
2054
|
+
const shouldVirtualize = Boolean(virtualize) && !groupBy && tableRows.length > (virtualize?.threshold ?? 100);
|
|
2055
|
+
const groupedBuckets = useMemo6(
|
|
2056
|
+
() => groupBy ? groupRows(tableRows, (row) => groupBy(row.original)) : void 0,
|
|
2057
|
+
[tableRows, groupBy]
|
|
2058
|
+
);
|
|
1931
2059
|
const scrollRef = useRef5(null);
|
|
1932
2060
|
const virtualizer = useVirtualizer({
|
|
1933
2061
|
count: tableRows.length,
|
|
@@ -1959,45 +2087,97 @@ function DataGrid({
|
|
|
1959
2087
|
{
|
|
1960
2088
|
...rowProps,
|
|
1961
2089
|
"data-testid": rowTestId,
|
|
1962
|
-
className: cn(rowProps
|
|
2090
|
+
className: rowProps?.className ? cn("divide-x divide-border", rowProps.className, row.index % 2 === 1 && "bg-foreground/5") : `divide-x divide-border${row.index % 2 === 1 ? " bg-foreground/5" : ""}`,
|
|
1963
2091
|
children: [
|
|
1964
|
-
showDetailColumn && /* @__PURE__ */ jsx19(
|
|
1965
|
-
|
|
2092
|
+
showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2093
|
+
"td",
|
|
1966
2094
|
{
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2095
|
+
className: row.index % 2 === 1 ? detailCellProps.classNameOdd : detailCellProps.className,
|
|
2096
|
+
style: detailCellProps.style,
|
|
2097
|
+
children: /* @__PURE__ */ jsx19(
|
|
2098
|
+
Button,
|
|
2099
|
+
{
|
|
2100
|
+
type: "button",
|
|
2101
|
+
variant: "ghost",
|
|
2102
|
+
size: "icon",
|
|
2103
|
+
"aria-label": `${isExpanded ? "Collapse" : "Expand"} row ${row.id}`,
|
|
2104
|
+
"aria-expanded": isExpanded,
|
|
2105
|
+
onClick: (event) => {
|
|
2106
|
+
stopRowClick(event);
|
|
2107
|
+
toggleRowExpanded(row.id);
|
|
2108
|
+
},
|
|
2109
|
+
children: /* @__PURE__ */ jsx19(
|
|
2110
|
+
ChevronRight,
|
|
2111
|
+
{
|
|
2112
|
+
className: `h-4 w-4 transition-transform${isExpanded ? " rotate-90" : ""}`,
|
|
2113
|
+
"aria-hidden": true
|
|
2114
|
+
}
|
|
2115
|
+
)
|
|
2116
|
+
}
|
|
2117
|
+
)
|
|
1977
2118
|
}
|
|
1978
|
-
)
|
|
1979
|
-
showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
1980
|
-
"
|
|
2119
|
+
),
|
|
2120
|
+
showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2121
|
+
"td",
|
|
1981
2122
|
{
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
2123
|
+
className: row.index % 2 === 1 ? selectionCellProps.classNameOdd : selectionCellProps.className,
|
|
2124
|
+
style: selectionCellProps.style,
|
|
2125
|
+
children: /* @__PURE__ */ jsx19(
|
|
2126
|
+
"input",
|
|
2127
|
+
{
|
|
2128
|
+
type: "checkbox",
|
|
2129
|
+
"aria-label": `Select row ${row.id}`,
|
|
2130
|
+
checked: selectedIds.has(row.id),
|
|
2131
|
+
onClick: stopRowClick,
|
|
2132
|
+
onChange: () => toggleRowSelected(row.id)
|
|
2133
|
+
}
|
|
2134
|
+
)
|
|
1987
2135
|
}
|
|
1988
|
-
)
|
|
2136
|
+
),
|
|
1989
2137
|
row.getAllCells().map((cell) => {
|
|
1990
|
-
const
|
|
1991
|
-
|
|
1992
|
-
return /* @__PURE__ */ jsx19("td", { style: pinnedProps?.style, className: cn("border-b p-2", pinnedProps?.className), children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id);
|
|
2138
|
+
const cellProps = bodyCellPropsByColumn.get(cell.column.id);
|
|
2139
|
+
return /* @__PURE__ */ jsx19("td", { style: cellProps?.style, className: cellProps?.className ?? BODY_TD_BASE_CLASS, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id);
|
|
1993
2140
|
}),
|
|
1994
|
-
showRowActionsColumn && rowActions && /* @__PURE__ */ jsx19(
|
|
2141
|
+
showRowActionsColumn && rowActions && /* @__PURE__ */ jsx19(
|
|
2142
|
+
"td",
|
|
2143
|
+
{
|
|
2144
|
+
className: row.index % 2 === 1 ? rowActionsCellProps.classNameOdd : rowActionsCellProps.className,
|
|
2145
|
+
style: rowActionsCellProps.style,
|
|
2146
|
+
children: /* @__PURE__ */ jsx19(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
|
|
2147
|
+
}
|
|
2148
|
+
)
|
|
1995
2149
|
]
|
|
1996
2150
|
}
|
|
1997
2151
|
),
|
|
1998
|
-
showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx19("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, className: "border-b p-2", children: renderDetail(row.original) }) })
|
|
2152
|
+
showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx19("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, className: "border-b border-border p-2", children: renderDetail(row.original) }) })
|
|
1999
2153
|
] }, row.id);
|
|
2000
2154
|
}
|
|
2155
|
+
function renderGroupedBucket(bucket) {
|
|
2156
|
+
const expanded = isGroupExpanded(bucket.key);
|
|
2157
|
+
const originalRows = bucket.items.map((row) => row.original);
|
|
2158
|
+
return /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
2159
|
+
/* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, className: "border-b bg-muted/50 p-2 font-medium", children: /* @__PURE__ */ jsxs12(
|
|
2160
|
+
"button",
|
|
2161
|
+
{
|
|
2162
|
+
type: "button",
|
|
2163
|
+
className: "flex w-full items-center gap-1 text-left",
|
|
2164
|
+
onClick: () => toggleGroupExpanded(bucket.key),
|
|
2165
|
+
"aria-expanded": expanded,
|
|
2166
|
+
children: [
|
|
2167
|
+
/* @__PURE__ */ jsx19(
|
|
2168
|
+
ChevronRight,
|
|
2169
|
+
{
|
|
2170
|
+
className: `h-4 w-4 shrink-0 transition-transform${expanded ? " rotate-90" : ""}`,
|
|
2171
|
+
"aria-hidden": true
|
|
2172
|
+
}
|
|
2173
|
+
),
|
|
2174
|
+
renderGroupHeader ? renderGroupHeader(bucket.key, originalRows, expanded) : `${bucket.key} (${originalRows.length})`
|
|
2175
|
+
]
|
|
2176
|
+
}
|
|
2177
|
+
) }) }) }),
|
|
2178
|
+
expanded && bucket.items.map((row) => renderRow(row))
|
|
2179
|
+
] }, `group-${bucket.key}`);
|
|
2180
|
+
}
|
|
2001
2181
|
return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-2", "data-testid": "datagrid", children: [
|
|
2002
2182
|
showSelectionColumn && headerActions && /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between text-sm", children: [
|
|
2003
2183
|
/* @__PURE__ */ jsxs12("span", { children: [
|
|
@@ -2032,58 +2212,58 @@ function DataGrid({
|
|
|
2032
2212
|
const sortEntry = state.sort.find((s) => s.field === column.id);
|
|
2033
2213
|
const sortable = isSortable(column);
|
|
2034
2214
|
const filterable = isFilterable(column) && column.filterDisplay !== "row";
|
|
2035
|
-
const
|
|
2036
|
-
return /* @__PURE__ */ jsxs12(
|
|
2037
|
-
"
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
"button",
|
|
2046
|
-
{
|
|
2047
|
-
type: "button",
|
|
2048
|
-
className: cn(
|
|
2049
|
-
"flex items-center gap-1 rounded-sm px-1",
|
|
2050
|
-
sortable && "hover:bg-accent hover:text-accent-foreground"
|
|
2051
|
-
),
|
|
2052
|
-
disabled: !sortable,
|
|
2053
|
-
onClick: (event) => sortable && toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
|
|
2054
|
-
children: [
|
|
2055
|
-
column.renderHeader ? column.renderHeader(column) : column.header,
|
|
2056
|
-
sortable && sortEntry?.dir === "asc" && /* @__PURE__ */ jsx19(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
|
|
2057
|
-
sortable && sortEntry?.dir === "desc" && /* @__PURE__ */ jsx19(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
|
|
2058
|
-
]
|
|
2059
|
-
}
|
|
2215
|
+
const cellProps = leafHeaderCellPropsByColumn.get(column.id);
|
|
2216
|
+
return /* @__PURE__ */ jsxs12("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
|
|
2217
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
|
|
2218
|
+
/* @__PURE__ */ jsxs12(
|
|
2219
|
+
"button",
|
|
2220
|
+
{
|
|
2221
|
+
type: "button",
|
|
2222
|
+
className: cn(
|
|
2223
|
+
"flex items-center gap-1 rounded-sm px-1",
|
|
2224
|
+
sortable && "hover:bg-accent hover:text-accent-foreground"
|
|
2060
2225
|
),
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2226
|
+
disabled: !sortable,
|
|
2227
|
+
onClick: (event) => sortable && toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
|
|
2228
|
+
children: [
|
|
2229
|
+
column.renderHeader ? column.renderHeader(column) : column.header,
|
|
2230
|
+
sortable && sortEntry?.dir === "asc" && /* @__PURE__ */ jsx19(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
|
|
2231
|
+
sortable && sortEntry?.dir === "desc" && /* @__PURE__ */ jsx19(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
|
|
2232
|
+
]
|
|
2233
|
+
}
|
|
2234
|
+
),
|
|
2235
|
+
filterable && /* @__PURE__ */ jsxs12(Popover, { children: [
|
|
2236
|
+
/* @__PURE__ */ jsx19(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx19(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx19(
|
|
2237
|
+
FunnelIcon7,
|
|
2068
2238
|
{
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2239
|
+
className: cn(
|
|
2240
|
+
"h-3 w-3",
|
|
2241
|
+
filtersByColumn.get(column.id) !== void 0 ? "opacity-100 text-primary" : "opacity-40"
|
|
2242
|
+
),
|
|
2243
|
+
"aria-hidden": true
|
|
2073
2244
|
}
|
|
2074
|
-
)
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2245
|
+
) }) }),
|
|
2246
|
+
/* @__PURE__ */ jsx19(PopoverContent, { children: renderFilterWidget(column) })
|
|
2247
|
+
] })
|
|
2248
|
+
] }),
|
|
2249
|
+
enableColumnResizing && /* @__PURE__ */ jsx19(
|
|
2250
|
+
"div",
|
|
2251
|
+
{
|
|
2252
|
+
"data-testid": `resize-handle-${column.id}`,
|
|
2253
|
+
onMouseDown: header.getResizeHandler(),
|
|
2254
|
+
onTouchStart: header.getResizeHandler(),
|
|
2255
|
+
className: "absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent"
|
|
2256
|
+
}
|
|
2257
|
+
)
|
|
2258
|
+
] }, header.id);
|
|
2079
2259
|
}
|
|
2080
2260
|
return /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
2081
|
-
hasHeaderGroups && /* @__PURE__ */ jsxs12("tr", { "data-testid": "header-group-row", children: [
|
|
2261
|
+
hasHeaderGroups && /* @__PURE__ */ jsxs12("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
|
|
2082
2262
|
showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2083
2263
|
"th",
|
|
2084
2264
|
{
|
|
2085
2265
|
rowSpan: 2,
|
|
2086
|
-
className:
|
|
2266
|
+
className: detailHeaderCellProps.className,
|
|
2087
2267
|
style: detailHeaderCellProps.style,
|
|
2088
2268
|
"aria-hidden": true
|
|
2089
2269
|
}
|
|
@@ -2092,7 +2272,7 @@ function DataGrid({
|
|
|
2092
2272
|
"th",
|
|
2093
2273
|
{
|
|
2094
2274
|
rowSpan: 2,
|
|
2095
|
-
className:
|
|
2275
|
+
className: selectionHeaderCellProps.className,
|
|
2096
2276
|
style: selectionHeaderCellProps.style,
|
|
2097
2277
|
children: /* @__PURE__ */ jsx19(
|
|
2098
2278
|
"input",
|
|
@@ -2110,7 +2290,7 @@ function DataGrid({
|
|
|
2110
2290
|
"th",
|
|
2111
2291
|
{
|
|
2112
2292
|
colSpan: run.columns.length,
|
|
2113
|
-
className: "border-b p-2 text-center font-medium",
|
|
2293
|
+
className: "border-b border-border p-2 text-center font-medium",
|
|
2114
2294
|
children: run.label
|
|
2115
2295
|
},
|
|
2116
2296
|
`group-${run.label}-${run.columns[0].id}`
|
|
@@ -2120,17 +2300,17 @@ function DataGrid({
|
|
|
2120
2300
|
"th",
|
|
2121
2301
|
{
|
|
2122
2302
|
rowSpan: 2,
|
|
2123
|
-
className:
|
|
2303
|
+
className: rowActionsHeaderCellProps.className,
|
|
2124
2304
|
style: rowActionsHeaderCellProps.style,
|
|
2125
2305
|
"aria-label": "Row actions"
|
|
2126
2306
|
}
|
|
2127
2307
|
)
|
|
2128
2308
|
] }, `${headerGroup.id}-groups`),
|
|
2129
|
-
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
2309
|
+
/* @__PURE__ */ jsxs12("tr", { className: "divide-x divide-border", children: [
|
|
2130
2310
|
!hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2131
2311
|
"th",
|
|
2132
2312
|
{
|
|
2133
|
-
className:
|
|
2313
|
+
className: detailHeaderCellProps.className,
|
|
2134
2314
|
style: detailHeaderCellProps.style,
|
|
2135
2315
|
"aria-hidden": true
|
|
2136
2316
|
}
|
|
@@ -2138,7 +2318,7 @@ function DataGrid({
|
|
|
2138
2318
|
!hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2139
2319
|
"th",
|
|
2140
2320
|
{
|
|
2141
|
-
className:
|
|
2321
|
+
className: selectionHeaderCellProps.className,
|
|
2142
2322
|
style: selectionHeaderCellProps.style,
|
|
2143
2323
|
children: /* @__PURE__ */ jsx19(
|
|
2144
2324
|
"input",
|
|
@@ -2159,7 +2339,7 @@ function DataGrid({
|
|
|
2159
2339
|
!hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx19(
|
|
2160
2340
|
"th",
|
|
2161
2341
|
{
|
|
2162
|
-
className:
|
|
2342
|
+
className: rowActionsHeaderCellProps.className,
|
|
2163
2343
|
style: rowActionsHeaderCellProps.style,
|
|
2164
2344
|
"aria-label": "Row actions"
|
|
2165
2345
|
}
|
|
@@ -2167,31 +2347,23 @@ function DataGrid({
|
|
|
2167
2347
|
] }, headerGroup.id)
|
|
2168
2348
|
] }, headerGroup.id);
|
|
2169
2349
|
}),
|
|
2170
|
-
hasFilterRow && /* @__PURE__ */ jsxs12("tr", { "data-testid": "filter-row", children: [
|
|
2171
|
-
showDetailColumn && /* @__PURE__ */ jsx19("th", { className:
|
|
2350
|
+
hasFilterRow && /* @__PURE__ */ jsxs12("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
|
|
2351
|
+
showDetailColumn && /* @__PURE__ */ jsx19("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
|
|
2172
2352
|
showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2173
2353
|
"th",
|
|
2174
2354
|
{
|
|
2175
|
-
className:
|
|
2355
|
+
className: selectionHeaderCellProps.className,
|
|
2176
2356
|
style: selectionHeaderCellProps.style
|
|
2177
2357
|
}
|
|
2178
2358
|
),
|
|
2179
2359
|
visibleColumns.map((column) => {
|
|
2180
|
-
const
|
|
2181
|
-
return /* @__PURE__ */ jsx19(
|
|
2182
|
-
"th",
|
|
2183
|
-
{
|
|
2184
|
-
style: pinnedProps.style,
|
|
2185
|
-
className: cn("border-b p-2 align-top font-normal", alignClassName(column), pinnedProps.className),
|
|
2186
|
-
children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null
|
|
2187
|
-
},
|
|
2188
|
-
column.id
|
|
2189
|
-
);
|
|
2360
|
+
const cellProps = filterHeaderCellPropsByColumn.get(column.id);
|
|
2361
|
+
return /* @__PURE__ */ jsx19("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
|
|
2190
2362
|
}),
|
|
2191
2363
|
showRowActionsColumn && /* @__PURE__ */ jsx19(
|
|
2192
2364
|
"th",
|
|
2193
2365
|
{
|
|
2194
|
-
className:
|
|
2366
|
+
className: rowActionsHeaderCellProps.className,
|
|
2195
2367
|
style: rowActionsHeaderCellProps.style
|
|
2196
2368
|
}
|
|
2197
2369
|
)
|
|
@@ -2203,7 +2375,7 @@ function DataGrid({
|
|
|
2203
2375
|
(virtualItem) => renderRow(tableRows[virtualItem.index], (el) => virtualizer.measureElement(el))
|
|
2204
2376
|
),
|
|
2205
2377
|
paddingBottom > 0 && /* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingBottom } }) }) })
|
|
2206
|
-
] }) : tableRows.map((row) => renderRow(row))
|
|
2378
|
+
] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
|
|
2207
2379
|
] })
|
|
2208
2380
|
}
|
|
2209
2381
|
)
|
|
@@ -2249,7 +2421,7 @@ function DataGrid({
|
|
|
2249
2421
|
// src/tree/TreeDataGrid.tsx
|
|
2250
2422
|
import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
|
|
2251
2423
|
import { AlertCircle, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2, Loader2 as Loader22 } from "lucide-react";
|
|
2252
|
-
import { useEffect as useEffect8, useMemo as
|
|
2424
|
+
import { useEffect as useEffect8, useMemo as useMemo8, useRef as useRef6 } from "react";
|
|
2253
2425
|
|
|
2254
2426
|
// src/tree/flattenTree.ts
|
|
2255
2427
|
function flattenTree(roots, accessors, expanded, childrenOverride) {
|
|
@@ -2268,8 +2440,33 @@ function flattenTree(roots, accessors, expanded, childrenOverride) {
|
|
|
2268
2440
|
return out;
|
|
2269
2441
|
}
|
|
2270
2442
|
|
|
2443
|
+
// src/tree/selectionState.ts
|
|
2444
|
+
function computeSelectionStates(roots, accessors, childrenOverride, selectedIds) {
|
|
2445
|
+
const { getRowId, getChildren } = accessors;
|
|
2446
|
+
const out = /* @__PURE__ */ new Map();
|
|
2447
|
+
function visit(row) {
|
|
2448
|
+
const id = getRowId(row);
|
|
2449
|
+
if (selectedIds.has(id)) {
|
|
2450
|
+
const state2 = { checked: true, indeterminate: false };
|
|
2451
|
+
out.set(id, state2);
|
|
2452
|
+
return state2;
|
|
2453
|
+
}
|
|
2454
|
+
const children = childrenOverride.get(id) ?? getChildren(row) ?? [];
|
|
2455
|
+
let anySelectedOrIndeterminate = false;
|
|
2456
|
+
for (const child of children) {
|
|
2457
|
+
const childState = visit(child);
|
|
2458
|
+
if (childState.checked || childState.indeterminate) anySelectedOrIndeterminate = true;
|
|
2459
|
+
}
|
|
2460
|
+
const state = { checked: false, indeterminate: anySelectedOrIndeterminate };
|
|
2461
|
+
out.set(id, state);
|
|
2462
|
+
return state;
|
|
2463
|
+
}
|
|
2464
|
+
for (const root of roots) visit(root);
|
|
2465
|
+
return out;
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2271
2468
|
// src/tree/useTreeState.ts
|
|
2272
|
-
import { useCallback as useCallback3, useMemo as
|
|
2469
|
+
import { useCallback as useCallback3, useMemo as useMemo7, useState as useState7 } from "react";
|
|
2273
2470
|
function useTreeState({
|
|
2274
2471
|
data,
|
|
2275
2472
|
getRowId,
|
|
@@ -2294,7 +2491,7 @@ function useTreeState({
|
|
|
2294
2491
|
const [childrenMap, setChildrenMap] = useState7(/* @__PURE__ */ new Map());
|
|
2295
2492
|
const [loadingIds, setLoadingIds] = useState7(/* @__PURE__ */ new Set());
|
|
2296
2493
|
const [errorIds, setErrorIds] = useState7(/* @__PURE__ */ new Map());
|
|
2297
|
-
const resolveHasChildren =
|
|
2494
|
+
const resolveHasChildren = useMemo7(
|
|
2298
2495
|
() => hasChildren ?? ((row) => (getChildren(row)?.length ?? 0) > 0),
|
|
2299
2496
|
[hasChildren, getChildren]
|
|
2300
2497
|
);
|
|
@@ -2374,6 +2571,8 @@ function useTreeState({
|
|
|
2374
2571
|
|
|
2375
2572
|
// src/tree/TreeDataGrid.tsx
|
|
2376
2573
|
import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2574
|
+
var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
|
|
2575
|
+
var UNCHECKED_STATE = { checked: false, indeterminate: false };
|
|
2377
2576
|
function renderCell(column, row) {
|
|
2378
2577
|
const value = getColumnValue(column, row);
|
|
2379
2578
|
return column.cell ? column.cell(value, row) : defaultFormat(column, value);
|
|
@@ -2428,9 +2627,13 @@ function TreeDataGrid({
|
|
|
2428
2627
|
rowActions,
|
|
2429
2628
|
virtualizeThreshold = 100,
|
|
2430
2629
|
estimatedRowHeight = 40,
|
|
2431
|
-
maxBodyHeight = 480
|
|
2630
|
+
maxBodyHeight = 480,
|
|
2631
|
+
selectedIds: controlledSelectedIds,
|
|
2632
|
+
onSelectedIdsChange,
|
|
2633
|
+
getRowSelectionState,
|
|
2634
|
+
isRowSelectionDisabled
|
|
2432
2635
|
}) {
|
|
2433
|
-
const accessors =
|
|
2636
|
+
const accessors = useMemo8(
|
|
2434
2637
|
() => ({ getRowId, getChildren, hasChildren }),
|
|
2435
2638
|
[getRowId, getChildren, hasChildren]
|
|
2436
2639
|
);
|
|
@@ -2449,13 +2652,49 @@ function TreeDataGrid({
|
|
|
2449
2652
|
didAutoExpand.current = true;
|
|
2450
2653
|
if (initialExpandedLevel > 0) void expandToLevel(initialExpandedLevel);
|
|
2451
2654
|
}, []);
|
|
2452
|
-
const flatRows =
|
|
2655
|
+
const flatRows = useMemo8(
|
|
2453
2656
|
() => flattenTree(data, accessors, expanded, childrenMap),
|
|
2454
2657
|
[data, accessors, expanded, childrenMap]
|
|
2455
2658
|
);
|
|
2456
2659
|
const treeColId = treeColumnId ?? columns[0]?.id;
|
|
2457
2660
|
const showRowActionsColumn = Boolean(rowActions?.length);
|
|
2458
|
-
const
|
|
2661
|
+
const showSelectionColumn = controlledSelectedIds !== void 0;
|
|
2662
|
+
const totalColumnCount = columns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);
|
|
2663
|
+
const selectionStates = useMemo8(
|
|
2664
|
+
() => showSelectionColumn ? computeSelectionStates(data, accessors, childrenMap, controlledSelectedIds ?? EMPTY_SELECTED_IDS) : void 0,
|
|
2665
|
+
[showSelectionColumn, data, accessors, childrenMap, controlledSelectedIds]
|
|
2666
|
+
);
|
|
2667
|
+
function resolveSelectionState(row, id) {
|
|
2668
|
+
const override = getRowSelectionState?.(row);
|
|
2669
|
+
if (override) return { checked: override.checked, indeterminate: override.indeterminate ?? false };
|
|
2670
|
+
return selectionStates?.get(id) ?? UNCHECKED_STATE;
|
|
2671
|
+
}
|
|
2672
|
+
function toggleRowSelected(id) {
|
|
2673
|
+
if (!controlledSelectedIds) return;
|
|
2674
|
+
const next = new Set(controlledSelectedIds);
|
|
2675
|
+
if (next.has(id)) next.delete(id);
|
|
2676
|
+
else next.add(id);
|
|
2677
|
+
onSelectedIdsChange?.(next);
|
|
2678
|
+
}
|
|
2679
|
+
const selectableFlatRows = useMemo8(
|
|
2680
|
+
() => flatRows.filter((flatRow) => !(isRowSelectionDisabled?.(flatRow.row) ?? false)),
|
|
2681
|
+
[flatRows, isRowSelectionDisabled]
|
|
2682
|
+
);
|
|
2683
|
+
const allVisibleSelected = showSelectionColumn && selectableFlatRows.length > 0 && selectableFlatRows.every((flatRow) => controlledSelectedIds.has(flatRow.id));
|
|
2684
|
+
function toggleAllVisibleSelected() {
|
|
2685
|
+
if (!controlledSelectedIds) return;
|
|
2686
|
+
const next = new Set(controlledSelectedIds);
|
|
2687
|
+
for (const flatRow of selectableFlatRows) {
|
|
2688
|
+
if (allVisibleSelected) next.delete(flatRow.id);
|
|
2689
|
+
else next.add(flatRow.id);
|
|
2690
|
+
}
|
|
2691
|
+
onSelectedIdsChange?.(next);
|
|
2692
|
+
}
|
|
2693
|
+
const bodyTdClassByColumn = useMemo8(() => {
|
|
2694
|
+
const map = /* @__PURE__ */ new Map();
|
|
2695
|
+
for (const column of columns) map.set(column.id, `p-2 ${alignClassName(column)}`);
|
|
2696
|
+
return map;
|
|
2697
|
+
}, [columns]);
|
|
2459
2698
|
const scrollRef = useRef6(null);
|
|
2460
2699
|
const shouldVirtualize = flatRows.length > virtualizeThreshold;
|
|
2461
2700
|
const virtualizer = useVirtualizer2({
|
|
@@ -2474,9 +2713,26 @@ function TreeDataGrid({
|
|
|
2474
2713
|
ref: measureRef,
|
|
2475
2714
|
...rowProps,
|
|
2476
2715
|
"data-testid": rowTestId,
|
|
2477
|
-
className:
|
|
2716
|
+
className: rowProps?.className ? cn(
|
|
2717
|
+
"border-b border-border divide-x divide-border",
|
|
2718
|
+
rowProps.className,
|
|
2719
|
+
index % 2 === 1 && "bg-foreground/5"
|
|
2720
|
+
) : `border-b border-border divide-x divide-border${index % 2 === 1 ? " bg-foreground/5" : ""}`,
|
|
2478
2721
|
children: [
|
|
2479
|
-
|
|
2722
|
+
showSelectionColumn && /* @__PURE__ */ jsx20("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
|
|
2723
|
+
const state = resolveSelectionState(flatRow.row, flatRow.id);
|
|
2724
|
+
return /* @__PURE__ */ jsx20(
|
|
2725
|
+
Checkbox,
|
|
2726
|
+
{
|
|
2727
|
+
"aria-label": `Select row ${flatRow.id}`,
|
|
2728
|
+
checked: state.indeterminate ? "indeterminate" : state.checked,
|
|
2729
|
+
disabled: isRowSelectionDisabled?.(flatRow.row) ?? false,
|
|
2730
|
+
onCheckedChange: () => toggleRowSelected(flatRow.id),
|
|
2731
|
+
onClick: stopRowClick
|
|
2732
|
+
}
|
|
2733
|
+
);
|
|
2734
|
+
})() }),
|
|
2735
|
+
columns.map((column) => /* @__PURE__ */ jsx20("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx20(
|
|
2480
2736
|
TreeCell,
|
|
2481
2737
|
{
|
|
2482
2738
|
flatRow,
|
|
@@ -2511,17 +2767,25 @@ function TreeDataGrid({
|
|
|
2511
2767
|
className: shouldVirtualize ? "overflow-y-auto" : void 0,
|
|
2512
2768
|
style: shouldVirtualize ? { maxHeight: maxBodyHeight } : void 0,
|
|
2513
2769
|
children: /* @__PURE__ */ jsxs13("table", { className: "w-full border-collapse text-sm", children: [
|
|
2514
|
-
/* @__PURE__ */ jsx20("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
|
|
2770
|
+
/* @__PURE__ */ jsx20("thead", { children: /* @__PURE__ */ jsxs13("tr", { className: "divide-x divide-border", children: [
|
|
2771
|
+
showSelectionColumn && /* @__PURE__ */ jsx20("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx20(
|
|
2772
|
+
Checkbox,
|
|
2773
|
+
{
|
|
2774
|
+
"aria-label": "Select all visible rows",
|
|
2775
|
+
checked: allVisibleSelected,
|
|
2776
|
+
onCheckedChange: toggleAllVisibleSelected
|
|
2777
|
+
}
|
|
2778
|
+
) }),
|
|
2515
2779
|
columns.map((column) => /* @__PURE__ */ jsx20(
|
|
2516
2780
|
"th",
|
|
2517
2781
|
{
|
|
2518
2782
|
style: column.width ? { width: column.width } : void 0,
|
|
2519
|
-
className:
|
|
2783
|
+
className: `border-b border-border p-2 font-medium ${alignClassName(column)}`,
|
|
2520
2784
|
children: column.header
|
|
2521
2785
|
},
|
|
2522
2786
|
column.id
|
|
2523
2787
|
)),
|
|
2524
|
-
showRowActionsColumn && /* @__PURE__ */ jsx20("th", { className: "border-b p-2", "aria-label": "Row actions" })
|
|
2788
|
+
showRowActionsColumn && /* @__PURE__ */ jsx20("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
|
|
2525
2789
|
] }) }),
|
|
2526
2790
|
/* @__PURE__ */ jsx20("tbody", { children: flatRows.length === 0 ? /* @__PURE__ */ jsx20("tr", { children: /* @__PURE__ */ jsx20("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: "No results." }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs13(Fragment4, { children: [
|
|
2527
2791
|
paddingTop > 0 && /* @__PURE__ */ jsx20("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx20("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
|