@bmsuisse/datagrid 0.23.1 → 0.23.4
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 +1 -1
- package/dist/index.js +253 -123
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -772,7 +772,7 @@ interface DataGridProps<TRow> {
|
|
|
772
772
|
* (`"server"`). Column filter UI defaults to the type-appropriate widget
|
|
773
773
|
* from `src/filter` unless a column supplies its own `cell` renderer.
|
|
774
774
|
*/
|
|
775
|
-
declare function DataGrid<TRow extends RowData>({ columns, dataSource, getRowId, rowActions, headerActions, enableMultiSort, initialState, columnVisibility, renderDetail, enableColumnResizing, columnResizeMode, columnSizing, onColumnSizingChange, virtualize, showPagination, getRowProps, getRowTestId, gridState, selectedIds: controlledSelectedIds, onSelectedIdsChange, testId, loading: loadingProp, }: DataGridProps<TRow>): ReactElement;
|
|
775
|
+
declare function DataGrid<TRow extends RowData>({ columns, dataSource, getRowId, rowActions, headerActions, enableMultiSort, initialState, columnVisibility, renderDetail, enableColumnResizing, columnResizeMode, columnSizing: controlledColumnSizing, onColumnSizingChange, virtualize, showPagination, getRowProps, getRowTestId, gridState, selectedIds: controlledSelectedIds, onSelectedIdsChange, testId, loading: loadingProp, }: DataGridProps<TRow>): ReactElement;
|
|
776
776
|
|
|
777
777
|
interface GridStateController {
|
|
778
778
|
state: GridState;
|
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";
|
|
@@ -648,6 +649,15 @@ function toNumber(value) {
|
|
|
648
649
|
}
|
|
649
650
|
return null;
|
|
650
651
|
}
|
|
652
|
+
var currencyFormatters = /* @__PURE__ */ new Map();
|
|
653
|
+
function currencyFormatter(currency) {
|
|
654
|
+
let formatter = currencyFormatters.get(currency);
|
|
655
|
+
if (!formatter) {
|
|
656
|
+
formatter = new Intl.NumberFormat(void 0, { style: "currency", currency });
|
|
657
|
+
currencyFormatters.set(currency, formatter);
|
|
658
|
+
}
|
|
659
|
+
return formatter;
|
|
660
|
+
}
|
|
651
661
|
function defaultFormat(column, value) {
|
|
652
662
|
if (value === null || value === void 0) return "";
|
|
653
663
|
switch (column.type) {
|
|
@@ -660,10 +670,7 @@ function defaultFormat(column, value) {
|
|
|
660
670
|
case "currency": {
|
|
661
671
|
const num = toNumber(value);
|
|
662
672
|
if (num === null) return String(value);
|
|
663
|
-
return
|
|
664
|
-
style: "currency",
|
|
665
|
-
currency: column.currency ?? "USD"
|
|
666
|
-
}).format(num);
|
|
673
|
+
return currencyFormatter(column.currency ?? "USD").format(num);
|
|
667
674
|
}
|
|
668
675
|
case "date": {
|
|
669
676
|
const date = toDate(value);
|
|
@@ -1508,6 +1515,7 @@ function resolveMenuItems(items, ctx) {
|
|
|
1508
1515
|
|
|
1509
1516
|
// src/menu/ActionsMenu.tsx
|
|
1510
1517
|
import { MoreVertical } from "lucide-react";
|
|
1518
|
+
import { useMemo as useMemo3 } from "react";
|
|
1511
1519
|
|
|
1512
1520
|
// src/components/ui/dropdown-menu.tsx
|
|
1513
1521
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
@@ -1563,7 +1571,10 @@ DropdownMenuLabel.displayName = "DropdownMenuLabel";
|
|
|
1563
1571
|
// src/menu/ActionsMenu.tsx
|
|
1564
1572
|
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1565
1573
|
function ActionsMenu({ items, ctx, triggerLabel }) {
|
|
1566
|
-
const resolved =
|
|
1574
|
+
const resolved = useMemo3(
|
|
1575
|
+
() => resolveMenuItems(items, ctx),
|
|
1576
|
+
[items, ctx.row, ctx.selectedRows]
|
|
1577
|
+
);
|
|
1567
1578
|
if (resolved.length === 0) return null;
|
|
1568
1579
|
return /* @__PURE__ */ jsxs11(DropdownMenu, { children: [
|
|
1569
1580
|
/* @__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 +1596,7 @@ import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon as FunnelIcon7 } from "@
|
|
|
1585
1596
|
import { columnResizingFeature, columnSizingFeature, flexRender, tableFeatures, useTable } from "@tanstack/react-table";
|
|
1586
1597
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
1587
1598
|
import { ChevronRight, Loader2 } from "lucide-react";
|
|
1588
|
-
import { Fragment as Fragment2, useEffect as useEffect7, useMemo as
|
|
1599
|
+
import { Fragment as Fragment2, useEffect as useEffect7, useMemo as useMemo6, useRef as useRef5, useState as useState6 } from "react";
|
|
1589
1600
|
|
|
1590
1601
|
// src/grid/processClientData.ts
|
|
1591
1602
|
function makeComparator(sort) {
|
|
@@ -1613,10 +1624,10 @@ function processClientData(data, state) {
|
|
|
1613
1624
|
}
|
|
1614
1625
|
|
|
1615
1626
|
// src/grid/useGridState.ts
|
|
1616
|
-
import { useCallback as useCallback2, useEffect as useEffect6, useMemo as
|
|
1627
|
+
import { useCallback as useCallback2, useEffect as useEffect6, useMemo as useMemo5, useState as useState5 } from "react";
|
|
1617
1628
|
|
|
1618
1629
|
// src/hooks/useDebouncedCallback.ts
|
|
1619
|
-
import { useEffect as useEffect5, useMemo as
|
|
1630
|
+
import { useEffect as useEffect5, useMemo as useMemo4, useRef as useRef4 } from "react";
|
|
1620
1631
|
function useDebouncedCallback(callback, delayMs) {
|
|
1621
1632
|
const callbackRef = useRef4(callback);
|
|
1622
1633
|
callbackRef.current = callback;
|
|
@@ -1626,7 +1637,7 @@ function useDebouncedCallback(callback, delayMs) {
|
|
|
1626
1637
|
clearTimeout(timeoutRef.current);
|
|
1627
1638
|
};
|
|
1628
1639
|
}, []);
|
|
1629
|
-
return
|
|
1640
|
+
return useMemo4(() => {
|
|
1630
1641
|
return {
|
|
1631
1642
|
run: (...args) => {
|
|
1632
1643
|
clearTimeout(timeoutRef.current);
|
|
@@ -1676,7 +1687,7 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
1676
1687
|
cancelDebouncedNotify();
|
|
1677
1688
|
setState(externalState);
|
|
1678
1689
|
}, [externalState, cancelDebouncedNotify]);
|
|
1679
|
-
const filtersByColumn =
|
|
1690
|
+
const filtersByColumn = useMemo5(() => {
|
|
1680
1691
|
const map = /* @__PURE__ */ new Map();
|
|
1681
1692
|
for (const child of state.filter?.filters ?? []) {
|
|
1682
1693
|
if ("operator" in child) map.set(fieldKey(child.field), child);
|
|
@@ -1743,9 +1754,12 @@ function useGridState(dataSource, initialState, externalState) {
|
|
|
1743
1754
|
import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1744
1755
|
var gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });
|
|
1745
1756
|
var DEFAULT_COLUMN_SIZE = 150;
|
|
1746
|
-
|
|
1757
|
+
var DETAIL_COLUMN_WIDTH = 44;
|
|
1758
|
+
var SELECTION_COLUMN_WIDTH = 44;
|
|
1759
|
+
var ROW_ACTIONS_COLUMN_WIDTH = 44;
|
|
1760
|
+
function pinnedOffsets(visibleColumns, side, getSize, leadingOffset) {
|
|
1747
1761
|
const offsets = /* @__PURE__ */ new Map();
|
|
1748
|
-
let cursor =
|
|
1762
|
+
let cursor = leadingOffset;
|
|
1749
1763
|
const ordered = side === "left" ? visibleColumns : [...visibleColumns].reverse();
|
|
1750
1764
|
for (const column of ordered) {
|
|
1751
1765
|
if (column.pinned !== side) continue;
|
|
@@ -1789,7 +1803,7 @@ function DataGrid({
|
|
|
1789
1803
|
renderDetail,
|
|
1790
1804
|
enableColumnResizing = false,
|
|
1791
1805
|
columnResizeMode = "onChange",
|
|
1792
|
-
columnSizing,
|
|
1806
|
+
columnSizing: controlledColumnSizing,
|
|
1793
1807
|
onColumnSizingChange,
|
|
1794
1808
|
virtualize,
|
|
1795
1809
|
showPagination = true,
|
|
@@ -1812,6 +1826,12 @@ function DataGrid({
|
|
|
1812
1826
|
setInternalSelectedIds(next);
|
|
1813
1827
|
onSelectedIdsChange?.(next);
|
|
1814
1828
|
}
|
|
1829
|
+
const [internalColumnSizing, setInternalColumnSizing] = useState6({});
|
|
1830
|
+
const columnSizing = controlledColumnSizing ?? internalColumnSizing;
|
|
1831
|
+
function updateColumnSizing(next) {
|
|
1832
|
+
setInternalColumnSizing(next);
|
|
1833
|
+
onColumnSizingChange?.(next);
|
|
1834
|
+
}
|
|
1815
1835
|
const [expandedIds, setExpandedIds] = useState6(/* @__PURE__ */ new Set());
|
|
1816
1836
|
function toggleRowExpanded(id) {
|
|
1817
1837
|
setExpandedIds((prev) => {
|
|
@@ -1821,16 +1841,16 @@ function DataGrid({
|
|
|
1821
1841
|
return next;
|
|
1822
1842
|
});
|
|
1823
1843
|
}
|
|
1824
|
-
const visibleColumns =
|
|
1844
|
+
const visibleColumns = useMemo6(
|
|
1825
1845
|
() => columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,
|
|
1826
1846
|
[columns, columnVisibility]
|
|
1827
1847
|
);
|
|
1828
1848
|
const serverRowCount = dataSource.mode === "server" ? dataSource.rowCount : void 0;
|
|
1829
|
-
const { rows, rowCount } =
|
|
1849
|
+
const { rows, rowCount } = useMemo6(() => {
|
|
1830
1850
|
if (dataSource.mode === "client") return processClientData(dataSource.data, state);
|
|
1831
1851
|
return { rows: dataSource.data, rowCount: serverRowCount ?? 0 };
|
|
1832
1852
|
}, [dataSource.mode, dataSource.data, serverRowCount, state]);
|
|
1833
|
-
const selectedRows =
|
|
1853
|
+
const selectedRows = useMemo6(
|
|
1834
1854
|
() => rows.filter((row) => selectedIds.has(getRowId(row))),
|
|
1835
1855
|
[rows, selectedIds, getRowId]
|
|
1836
1856
|
);
|
|
@@ -1849,8 +1869,8 @@ function DataGrid({
|
|
|
1849
1869
|
}
|
|
1850
1870
|
updateSelectedIds(next);
|
|
1851
1871
|
}
|
|
1852
|
-
const tanstackColumns =
|
|
1853
|
-
const columnById =
|
|
1872
|
+
const tanstackColumns = useMemo6(() => toTanstackColumns(visibleColumns), [visibleColumns]);
|
|
1873
|
+
const columnById = useMemo6(() => {
|
|
1854
1874
|
const map = /* @__PURE__ */ new Map();
|
|
1855
1875
|
for (const column of visibleColumns) map.set(column.id, column);
|
|
1856
1876
|
return map;
|
|
@@ -1862,13 +1882,11 @@ function DataGrid({
|
|
|
1862
1882
|
getRowId,
|
|
1863
1883
|
enableColumnResizing,
|
|
1864
1884
|
columnResizeMode,
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
}
|
|
1871
|
-
} : {}
|
|
1885
|
+
state: { columnSizing },
|
|
1886
|
+
onColumnSizingChange: (updater) => {
|
|
1887
|
+
const next = typeof updater === "function" ? updater(columnSizing) : updater;
|
|
1888
|
+
updateColumnSizing(next);
|
|
1889
|
+
}
|
|
1872
1890
|
});
|
|
1873
1891
|
const pageCount = Math.max(1, Math.ceil(rowCount / state.pageSize));
|
|
1874
1892
|
const loading = loadingProp || (dataSource.mode === "server" ? Boolean(dataSource.loading) : false);
|
|
@@ -1877,7 +1895,7 @@ function DataGrid({
|
|
|
1877
1895
|
const showDetailColumn = Boolean(renderDetail);
|
|
1878
1896
|
const totalColumnCount = visibleColumns.length + (showDetailColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0) + (showRowActionsColumn ? 1 : 0);
|
|
1879
1897
|
const hasFilterRow = visibleColumns.some((column) => isFilterable(column) && column.filterDisplay === "row");
|
|
1880
|
-
const headerRuns =
|
|
1898
|
+
const headerRuns = useMemo6(() => computeHeaderRuns(visibleColumns), [visibleColumns]);
|
|
1881
1899
|
const hasHeaderGroups = visibleColumns.some((column) => column.headerGroup);
|
|
1882
1900
|
function renderFilterWidget(column) {
|
|
1883
1901
|
const value = filtersByColumn.get(column.id);
|
|
@@ -1885,19 +1903,79 @@ function DataGrid({
|
|
|
1885
1903
|
return column.renderFilter ? column.renderFilter(value, onChange, state.filter) : renderDefaultFilterWidget(column, value, onChange);
|
|
1886
1904
|
}
|
|
1887
1905
|
const columnSize = (columnId) => table.getColumn(columnId)?.getSize() ?? DEFAULT_COLUMN_SIZE;
|
|
1888
|
-
const
|
|
1889
|
-
const
|
|
1906
|
+
const leadingStructuralWidth = (showDetailColumn ? DETAIL_COLUMN_WIDTH : 0) + (showSelectionColumn ? SELECTION_COLUMN_WIDTH : 0);
|
|
1907
|
+
const trailingStructuralWidth = showRowActionsColumn ? ROW_ACTIONS_COLUMN_WIDTH : 0;
|
|
1908
|
+
const leftPinnedOffsets = useMemo6(
|
|
1909
|
+
() => pinnedOffsets(visibleColumns, "left", columnSize, leadingStructuralWidth),
|
|
1910
|
+
[visibleColumns, columnSizing, leadingStructuralWidth]
|
|
1911
|
+
);
|
|
1912
|
+
const rightPinnedOffsets = useMemo6(
|
|
1913
|
+
() => pinnedOffsets(visibleColumns, "right", columnSize, trailingStructuralWidth),
|
|
1914
|
+
[visibleColumns, columnSizing, trailingStructuralWidth]
|
|
1915
|
+
);
|
|
1890
1916
|
function pinnedCellProps(column, area = "body") {
|
|
1891
1917
|
const bg = area === "header" ? "bg-muted" : "bg-background";
|
|
1892
1918
|
if (column.pinned === "left") {
|
|
1893
|
-
return { className:
|
|
1919
|
+
return { className: `sticky z-10 ${bg}`, style: { left: leftPinnedOffsets.get(column.id), width: columnSize(column.id) } };
|
|
1894
1920
|
}
|
|
1895
1921
|
if (column.pinned === "right") {
|
|
1896
|
-
return { className:
|
|
1922
|
+
return { className: `sticky z-10 ${bg}`, style: { right: rightPinnedOffsets.get(column.id), width: columnSize(column.id) } };
|
|
1897
1923
|
}
|
|
1898
1924
|
if (enableColumnResizing) return { style: { width: columnSize(column.id) } };
|
|
1899
1925
|
return { style: column.width ? { width: column.width } : void 0 };
|
|
1900
1926
|
}
|
|
1927
|
+
function structuralCellProps(side, offset, width, area = "body") {
|
|
1928
|
+
const bg = area === "header" ? "bg-muted" : "bg-background";
|
|
1929
|
+
return { className: `border-b p-1 sticky z-10 ${bg}`, style: { [side]: offset, width } };
|
|
1930
|
+
}
|
|
1931
|
+
const detailCellProps = structuralCellProps("left", 0, DETAIL_COLUMN_WIDTH);
|
|
1932
|
+
const selectionCellProps = structuralCellProps(
|
|
1933
|
+
"left",
|
|
1934
|
+
showDetailColumn ? DETAIL_COLUMN_WIDTH : 0,
|
|
1935
|
+
SELECTION_COLUMN_WIDTH
|
|
1936
|
+
);
|
|
1937
|
+
const rowActionsCellProps = structuralCellProps("right", 0, ROW_ACTIONS_COLUMN_WIDTH);
|
|
1938
|
+
const detailHeaderCellProps = structuralCellProps("left", 0, DETAIL_COLUMN_WIDTH, "header");
|
|
1939
|
+
const selectionHeaderCellProps = structuralCellProps(
|
|
1940
|
+
"left",
|
|
1941
|
+
showDetailColumn ? DETAIL_COLUMN_WIDTH : 0,
|
|
1942
|
+
SELECTION_COLUMN_WIDTH,
|
|
1943
|
+
"header"
|
|
1944
|
+
);
|
|
1945
|
+
const rowActionsHeaderCellProps = structuralCellProps("right", 0, ROW_ACTIONS_COLUMN_WIDTH, "header");
|
|
1946
|
+
const BODY_TD_BASE_CLASS = "border-b p-2";
|
|
1947
|
+
function bodyCellClassAndStyle(column) {
|
|
1948
|
+
const pinnedProps = column.pinned || enableColumnResizing ? pinnedCellProps(column) : void 0;
|
|
1949
|
+
return {
|
|
1950
|
+
className: pinnedProps?.className ? `${BODY_TD_BASE_CLASS} ${pinnedProps.className}` : BODY_TD_BASE_CLASS,
|
|
1951
|
+
style: pinnedProps?.style
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
const bodyCellPropsByColumn = useMemo6(() => {
|
|
1955
|
+
const map = /* @__PURE__ */ new Map();
|
|
1956
|
+
for (const column of visibleColumns) map.set(column.id, bodyCellClassAndStyle(column));
|
|
1957
|
+
return map;
|
|
1958
|
+
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
1959
|
+
function headerCellClassAndStyle(column, base) {
|
|
1960
|
+
const pinnedProps = pinnedCellProps(column, "header");
|
|
1961
|
+
const align = alignClassName(column);
|
|
1962
|
+
const className = pinnedProps.className ? `${base} ${align} ${pinnedProps.className}` : `${base} ${align}`;
|
|
1963
|
+
return { className, style: pinnedProps.style };
|
|
1964
|
+
}
|
|
1965
|
+
const leafHeaderCellPropsByColumn = useMemo6(() => {
|
|
1966
|
+
const map = /* @__PURE__ */ new Map();
|
|
1967
|
+
for (const column of visibleColumns) {
|
|
1968
|
+
map.set(column.id, headerCellClassAndStyle(column, "relative border-b p-2 font-medium"));
|
|
1969
|
+
}
|
|
1970
|
+
return map;
|
|
1971
|
+
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
1972
|
+
const filterHeaderCellPropsByColumn = useMemo6(() => {
|
|
1973
|
+
const map = /* @__PURE__ */ new Map();
|
|
1974
|
+
for (const column of visibleColumns) {
|
|
1975
|
+
map.set(column.id, headerCellClassAndStyle(column, "border-b p-2 align-top font-normal"));
|
|
1976
|
+
}
|
|
1977
|
+
return map;
|
|
1978
|
+
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
1901
1979
|
const tableRows = table.getRowModel().rows;
|
|
1902
1980
|
const shouldVirtualize = Boolean(virtualize) && tableRows.length > (virtualize?.threshold ?? 100);
|
|
1903
1981
|
const scrollRef = useRef5(null);
|
|
@@ -1931,22 +2009,30 @@ function DataGrid({
|
|
|
1931
2009
|
{
|
|
1932
2010
|
...rowProps,
|
|
1933
2011
|
"data-testid": rowTestId,
|
|
1934
|
-
className: cn(rowProps
|
|
2012
|
+
className: rowProps?.className ? cn(rowProps.className, row.index % 2 === 1 && "bg-foreground/5") : row.index % 2 === 1 ? "bg-foreground/5" : void 0,
|
|
1935
2013
|
children: [
|
|
1936
|
-
showDetailColumn && /* @__PURE__ */ jsx19("td", { className:
|
|
1937
|
-
|
|
2014
|
+
showDetailColumn && /* @__PURE__ */ jsx19("td", { className: detailCellProps.className, style: detailCellProps.style, children: /* @__PURE__ */ jsx19(
|
|
2015
|
+
Button,
|
|
1938
2016
|
{
|
|
1939
2017
|
type: "button",
|
|
2018
|
+
variant: "ghost",
|
|
2019
|
+
size: "icon",
|
|
1940
2020
|
"aria-label": `${isExpanded ? "Collapse" : "Expand"} row ${row.id}`,
|
|
1941
2021
|
"aria-expanded": isExpanded,
|
|
1942
2022
|
onClick: (event) => {
|
|
1943
2023
|
stopRowClick(event);
|
|
1944
2024
|
toggleRowExpanded(row.id);
|
|
1945
2025
|
},
|
|
1946
|
-
children: /* @__PURE__ */ jsx19(
|
|
2026
|
+
children: /* @__PURE__ */ jsx19(
|
|
2027
|
+
ChevronRight,
|
|
2028
|
+
{
|
|
2029
|
+
className: `h-4 w-4 transition-transform${isExpanded ? " rotate-90" : ""}`,
|
|
2030
|
+
"aria-hidden": true
|
|
2031
|
+
}
|
|
2032
|
+
)
|
|
1947
2033
|
}
|
|
1948
2034
|
) }),
|
|
1949
|
-
showSelectionColumn && /* @__PURE__ */ jsx19("td", { className:
|
|
2035
|
+
showSelectionColumn && /* @__PURE__ */ jsx19("td", { className: selectionCellProps.className, style: selectionCellProps.style, children: /* @__PURE__ */ jsx19(
|
|
1950
2036
|
"input",
|
|
1951
2037
|
{
|
|
1952
2038
|
type: "checkbox",
|
|
@@ -1957,11 +2043,10 @@ function DataGrid({
|
|
|
1957
2043
|
}
|
|
1958
2044
|
) }),
|
|
1959
2045
|
row.getAllCells().map((cell) => {
|
|
1960
|
-
const
|
|
1961
|
-
|
|
1962
|
-
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);
|
|
2046
|
+
const cellProps = bodyCellPropsByColumn.get(cell.column.id);
|
|
2047
|
+
return /* @__PURE__ */ jsx19("td", { style: cellProps?.style, className: cellProps?.className ?? BODY_TD_BASE_CLASS, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id);
|
|
1963
2048
|
}),
|
|
1964
|
-
showRowActionsColumn && rowActions && /* @__PURE__ */ jsx19("td", { className:
|
|
2049
|
+
showRowActionsColumn && rowActions && /* @__PURE__ */ jsx19("td", { className: rowActionsCellProps.className, style: rowActionsCellProps.style, children: /* @__PURE__ */ jsx19(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` }) })
|
|
1965
2050
|
]
|
|
1966
2051
|
}
|
|
1967
2052
|
),
|
|
@@ -1992,7 +2077,7 @@ function DataGrid({
|
|
|
1992
2077
|
"data-testid": testId,
|
|
1993
2078
|
className: cn("overflow-x-auto rounded-md border", shouldVirtualize && "overflow-y-auto"),
|
|
1994
2079
|
style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : void 0,
|
|
1995
|
-
children: /* @__PURE__ */ jsxs12("table", { className: "w-full border-collapse text-sm", children: [
|
|
2080
|
+
children: /* @__PURE__ */ jsxs12("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
|
|
1996
2081
|
/* @__PURE__ */ jsxs12("thead", { className: "bg-muted", children: [
|
|
1997
2082
|
table.getHeaderGroups().map((headerGroup) => {
|
|
1998
2083
|
const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));
|
|
@@ -2002,63 +2087,70 @@ function DataGrid({
|
|
|
2002
2087
|
const sortEntry = state.sort.find((s) => s.field === column.id);
|
|
2003
2088
|
const sortable = isSortable(column);
|
|
2004
2089
|
const filterable = isFilterable(column) && column.filterDisplay !== "row";
|
|
2005
|
-
const
|
|
2006
|
-
return /* @__PURE__ */ jsxs12(
|
|
2007
|
-
"
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
"button",
|
|
2016
|
-
{
|
|
2017
|
-
type: "button",
|
|
2018
|
-
className: cn(
|
|
2019
|
-
"flex items-center gap-1 rounded-sm px-1",
|
|
2020
|
-
sortable && "hover:bg-accent hover:text-accent-foreground"
|
|
2021
|
-
),
|
|
2022
|
-
disabled: !sortable,
|
|
2023
|
-
onClick: (event) => sortable && toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
|
|
2024
|
-
children: [
|
|
2025
|
-
column.renderHeader ? column.renderHeader(column) : column.header,
|
|
2026
|
-
sortable && sortEntry?.dir === "asc" && /* @__PURE__ */ jsx19(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
|
|
2027
|
-
sortable && sortEntry?.dir === "desc" && /* @__PURE__ */ jsx19(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
|
|
2028
|
-
]
|
|
2029
|
-
}
|
|
2090
|
+
const cellProps = leafHeaderCellPropsByColumn.get(column.id);
|
|
2091
|
+
return /* @__PURE__ */ jsxs12("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
|
|
2092
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
|
|
2093
|
+
/* @__PURE__ */ jsxs12(
|
|
2094
|
+
"button",
|
|
2095
|
+
{
|
|
2096
|
+
type: "button",
|
|
2097
|
+
className: cn(
|
|
2098
|
+
"flex items-center gap-1 rounded-sm px-1",
|
|
2099
|
+
sortable && "hover:bg-accent hover:text-accent-foreground"
|
|
2030
2100
|
),
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2101
|
+
disabled: !sortable,
|
|
2102
|
+
onClick: (event) => sortable && toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
|
|
2103
|
+
children: [
|
|
2104
|
+
column.renderHeader ? column.renderHeader(column) : column.header,
|
|
2105
|
+
sortable && sortEntry?.dir === "asc" && /* @__PURE__ */ jsx19(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
|
|
2106
|
+
sortable && sortEntry?.dir === "desc" && /* @__PURE__ */ jsx19(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
|
|
2107
|
+
]
|
|
2108
|
+
}
|
|
2109
|
+
),
|
|
2110
|
+
filterable && /* @__PURE__ */ jsxs12(Popover, { children: [
|
|
2111
|
+
/* @__PURE__ */ jsx19(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx19(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx19(FunnelIcon7, { className: "h-3 w-3", "aria-hidden": true }) }) }),
|
|
2112
|
+
/* @__PURE__ */ jsx19(PopoverContent, { children: renderFilterWidget(column) })
|
|
2113
|
+
] })
|
|
2114
|
+
] }),
|
|
2115
|
+
enableColumnResizing && /* @__PURE__ */ jsx19(
|
|
2116
|
+
"div",
|
|
2117
|
+
{
|
|
2118
|
+
"data-testid": `resize-handle-${column.id}`,
|
|
2119
|
+
onMouseDown: header.getResizeHandler(),
|
|
2120
|
+
onTouchStart: header.getResizeHandler(),
|
|
2121
|
+
className: "absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent"
|
|
2122
|
+
}
|
|
2123
|
+
)
|
|
2124
|
+
] }, header.id);
|
|
2049
2125
|
}
|
|
2050
2126
|
return /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
2051
2127
|
hasHeaderGroups && /* @__PURE__ */ jsxs12("tr", { "data-testid": "header-group-row", children: [
|
|
2052
|
-
showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2053
|
-
|
|
2054
|
-
"input",
|
|
2128
|
+
showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2129
|
+
"th",
|
|
2055
2130
|
{
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2131
|
+
rowSpan: 2,
|
|
2132
|
+
className: detailHeaderCellProps.className,
|
|
2133
|
+
style: detailHeaderCellProps.style,
|
|
2134
|
+
"aria-hidden": true
|
|
2135
|
+
}
|
|
2136
|
+
),
|
|
2137
|
+
showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2138
|
+
"th",
|
|
2139
|
+
{
|
|
2140
|
+
rowSpan: 2,
|
|
2141
|
+
className: selectionHeaderCellProps.className,
|
|
2142
|
+
style: selectionHeaderCellProps.style,
|
|
2143
|
+
children: /* @__PURE__ */ jsx19(
|
|
2144
|
+
"input",
|
|
2145
|
+
{
|
|
2146
|
+
type: "checkbox",
|
|
2147
|
+
"aria-label": "Select all rows on this page",
|
|
2148
|
+
checked: allOnPageSelected,
|
|
2149
|
+
onChange: toggleAllOnPageSelected
|
|
2150
|
+
}
|
|
2151
|
+
)
|
|
2060
2152
|
}
|
|
2061
|
-
)
|
|
2153
|
+
),
|
|
2062
2154
|
headerRuns.map(
|
|
2063
2155
|
(run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx19(
|
|
2064
2156
|
"th",
|
|
@@ -2070,44 +2162,77 @@ function DataGrid({
|
|
|
2070
2162
|
`group-${run.label}-${run.columns[0].id}`
|
|
2071
2163
|
)
|
|
2072
2164
|
),
|
|
2073
|
-
showRowActionsColumn && /* @__PURE__ */ jsx19(
|
|
2165
|
+
showRowActionsColumn && /* @__PURE__ */ jsx19(
|
|
2166
|
+
"th",
|
|
2167
|
+
{
|
|
2168
|
+
rowSpan: 2,
|
|
2169
|
+
className: rowActionsHeaderCellProps.className,
|
|
2170
|
+
style: rowActionsHeaderCellProps.style,
|
|
2171
|
+
"aria-label": "Row actions"
|
|
2172
|
+
}
|
|
2173
|
+
)
|
|
2074
2174
|
] }, `${headerGroup.id}-groups`),
|
|
2075
2175
|
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
2076
|
-
!hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2077
|
-
|
|
2078
|
-
|
|
2176
|
+
!hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2177
|
+
"th",
|
|
2178
|
+
{
|
|
2179
|
+
className: detailHeaderCellProps.className,
|
|
2180
|
+
style: detailHeaderCellProps.style,
|
|
2181
|
+
"aria-hidden": true
|
|
2182
|
+
}
|
|
2183
|
+
),
|
|
2184
|
+
!hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2185
|
+
"th",
|
|
2079
2186
|
{
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2187
|
+
className: selectionHeaderCellProps.className,
|
|
2188
|
+
style: selectionHeaderCellProps.style,
|
|
2189
|
+
children: /* @__PURE__ */ jsx19(
|
|
2190
|
+
"input",
|
|
2191
|
+
{
|
|
2192
|
+
type: "checkbox",
|
|
2193
|
+
"aria-label": "Select all rows on this page",
|
|
2194
|
+
checked: allOnPageSelected,
|
|
2195
|
+
onChange: toggleAllOnPageSelected
|
|
2196
|
+
}
|
|
2197
|
+
)
|
|
2084
2198
|
}
|
|
2085
|
-
)
|
|
2199
|
+
),
|
|
2086
2200
|
headerGroup.headers.map((header) => {
|
|
2087
2201
|
const column = columnById.get(header.id);
|
|
2088
2202
|
if (!column || hasHeaderGroups && !column.headerGroup) return null;
|
|
2089
2203
|
return renderLeafHeaderCell(column);
|
|
2090
2204
|
}),
|
|
2091
|
-
!hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx19(
|
|
2205
|
+
!hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx19(
|
|
2206
|
+
"th",
|
|
2207
|
+
{
|
|
2208
|
+
className: rowActionsHeaderCellProps.className,
|
|
2209
|
+
style: rowActionsHeaderCellProps.style,
|
|
2210
|
+
"aria-label": "Row actions"
|
|
2211
|
+
}
|
|
2212
|
+
)
|
|
2092
2213
|
] }, headerGroup.id)
|
|
2093
2214
|
] }, headerGroup.id);
|
|
2094
2215
|
}),
|
|
2095
2216
|
hasFilterRow && /* @__PURE__ */ jsxs12("tr", { "data-testid": "filter-row", children: [
|
|
2096
|
-
showDetailColumn && /* @__PURE__ */ jsx19("th", { className:
|
|
2097
|
-
showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2217
|
+
showDetailColumn && /* @__PURE__ */ jsx19("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
|
|
2218
|
+
showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2219
|
+
"th",
|
|
2220
|
+
{
|
|
2221
|
+
className: selectionHeaderCellProps.className,
|
|
2222
|
+
style: selectionHeaderCellProps.style
|
|
2223
|
+
}
|
|
2224
|
+
),
|
|
2098
2225
|
visibleColumns.map((column) => {
|
|
2099
|
-
const
|
|
2100
|
-
return /* @__PURE__ */ jsx19(
|
|
2101
|
-
"th",
|
|
2102
|
-
{
|
|
2103
|
-
style: pinnedProps.style,
|
|
2104
|
-
className: cn("border-b p-2 align-top font-normal", alignClassName(column), pinnedProps.className),
|
|
2105
|
-
children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null
|
|
2106
|
-
},
|
|
2107
|
-
column.id
|
|
2108
|
-
);
|
|
2226
|
+
const cellProps = filterHeaderCellPropsByColumn.get(column.id);
|
|
2227
|
+
return /* @__PURE__ */ jsx19("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
|
|
2109
2228
|
}),
|
|
2110
|
-
showRowActionsColumn && /* @__PURE__ */ jsx19(
|
|
2229
|
+
showRowActionsColumn && /* @__PURE__ */ jsx19(
|
|
2230
|
+
"th",
|
|
2231
|
+
{
|
|
2232
|
+
className: rowActionsHeaderCellProps.className,
|
|
2233
|
+
style: rowActionsHeaderCellProps.style
|
|
2234
|
+
}
|
|
2235
|
+
)
|
|
2111
2236
|
] })
|
|
2112
2237
|
] }),
|
|
2113
2238
|
rows.length === 0 ? /* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? "Loading..." : "No results." }) }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
@@ -2162,7 +2287,7 @@ function DataGrid({
|
|
|
2162
2287
|
// src/tree/TreeDataGrid.tsx
|
|
2163
2288
|
import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
|
|
2164
2289
|
import { AlertCircle, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2, Loader2 as Loader22 } from "lucide-react";
|
|
2165
|
-
import { useEffect as useEffect8, useMemo as
|
|
2290
|
+
import { useEffect as useEffect8, useMemo as useMemo8, useRef as useRef6 } from "react";
|
|
2166
2291
|
|
|
2167
2292
|
// src/tree/flattenTree.ts
|
|
2168
2293
|
function flattenTree(roots, accessors, expanded, childrenOverride) {
|
|
@@ -2182,7 +2307,7 @@ function flattenTree(roots, accessors, expanded, childrenOverride) {
|
|
|
2182
2307
|
}
|
|
2183
2308
|
|
|
2184
2309
|
// src/tree/useTreeState.ts
|
|
2185
|
-
import { useCallback as useCallback3, useMemo as
|
|
2310
|
+
import { useCallback as useCallback3, useMemo as useMemo7, useState as useState7 } from "react";
|
|
2186
2311
|
function useTreeState({
|
|
2187
2312
|
data,
|
|
2188
2313
|
getRowId,
|
|
@@ -2207,7 +2332,7 @@ function useTreeState({
|
|
|
2207
2332
|
const [childrenMap, setChildrenMap] = useState7(/* @__PURE__ */ new Map());
|
|
2208
2333
|
const [loadingIds, setLoadingIds] = useState7(/* @__PURE__ */ new Set());
|
|
2209
2334
|
const [errorIds, setErrorIds] = useState7(/* @__PURE__ */ new Map());
|
|
2210
|
-
const resolveHasChildren =
|
|
2335
|
+
const resolveHasChildren = useMemo7(
|
|
2211
2336
|
() => hasChildren ?? ((row) => (getChildren(row)?.length ?? 0) > 0),
|
|
2212
2337
|
[hasChildren, getChildren]
|
|
2213
2338
|
);
|
|
@@ -2343,7 +2468,7 @@ function TreeDataGrid({
|
|
|
2343
2468
|
estimatedRowHeight = 40,
|
|
2344
2469
|
maxBodyHeight = 480
|
|
2345
2470
|
}) {
|
|
2346
|
-
const accessors =
|
|
2471
|
+
const accessors = useMemo8(
|
|
2347
2472
|
() => ({ getRowId, getChildren, hasChildren }),
|
|
2348
2473
|
[getRowId, getChildren, hasChildren]
|
|
2349
2474
|
);
|
|
@@ -2362,13 +2487,18 @@ function TreeDataGrid({
|
|
|
2362
2487
|
didAutoExpand.current = true;
|
|
2363
2488
|
if (initialExpandedLevel > 0) void expandToLevel(initialExpandedLevel);
|
|
2364
2489
|
}, []);
|
|
2365
|
-
const flatRows =
|
|
2490
|
+
const flatRows = useMemo8(
|
|
2366
2491
|
() => flattenTree(data, accessors, expanded, childrenMap),
|
|
2367
2492
|
[data, accessors, expanded, childrenMap]
|
|
2368
2493
|
);
|
|
2369
2494
|
const treeColId = treeColumnId ?? columns[0]?.id;
|
|
2370
2495
|
const showRowActionsColumn = Boolean(rowActions?.length);
|
|
2371
2496
|
const totalColumnCount = columns.length + (showRowActionsColumn ? 1 : 0);
|
|
2497
|
+
const bodyTdClassByColumn = useMemo8(() => {
|
|
2498
|
+
const map = /* @__PURE__ */ new Map();
|
|
2499
|
+
for (const column of columns) map.set(column.id, `p-2 ${alignClassName(column)}`);
|
|
2500
|
+
return map;
|
|
2501
|
+
}, [columns]);
|
|
2372
2502
|
const scrollRef = useRef6(null);
|
|
2373
2503
|
const shouldVirtualize = flatRows.length > virtualizeThreshold;
|
|
2374
2504
|
const virtualizer = useVirtualizer2({
|
|
@@ -2387,9 +2517,9 @@ function TreeDataGrid({
|
|
|
2387
2517
|
ref: measureRef,
|
|
2388
2518
|
...rowProps,
|
|
2389
2519
|
"data-testid": rowTestId,
|
|
2390
|
-
className: cn("border-b", rowProps
|
|
2520
|
+
className: rowProps?.className ? cn("border-b", rowProps.className, index % 2 === 1 && "bg-foreground/5") : `border-b${index % 2 === 1 ? " bg-foreground/5" : ""}`,
|
|
2391
2521
|
children: [
|
|
2392
|
-
columns.map((column) => /* @__PURE__ */ jsx20("td", { className:
|
|
2522
|
+
columns.map((column) => /* @__PURE__ */ jsx20("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx20(
|
|
2393
2523
|
TreeCell,
|
|
2394
2524
|
{
|
|
2395
2525
|
flatRow,
|
|
@@ -2429,7 +2559,7 @@ function TreeDataGrid({
|
|
|
2429
2559
|
"th",
|
|
2430
2560
|
{
|
|
2431
2561
|
style: column.width ? { width: column.width } : void 0,
|
|
2432
|
-
className:
|
|
2562
|
+
className: `border-b p-2 font-medium ${alignClassName(column)}`,
|
|
2433
2563
|
children: column.header
|
|
2434
2564
|
},
|
|
2435
2565
|
column.id
|