@bmsuisse/datagrid 0.23.4 → 0.24.1
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 +286 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -338,7 +338,13 @@ function BooleanFilter({
|
|
|
338
338
|
"aria-label": `Filter ${column.header}`,
|
|
339
339
|
"data-testid": `filter-${column.id}`,
|
|
340
340
|
children: [
|
|
341
|
-
/* @__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
|
+
),
|
|
342
348
|
isFiltered && /* @__PURE__ */ jsx4("span", { className: "truncate", children: summary })
|
|
343
349
|
]
|
|
344
350
|
}
|
|
@@ -592,7 +598,13 @@ function EnumFilter({
|
|
|
592
598
|
"aria-label": `Filter ${column.header}`,
|
|
593
599
|
"data-testid": `filter-${column.id}`,
|
|
594
600
|
children: [
|
|
595
|
-
/* @__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
|
+
),
|
|
596
608
|
isFiltered && /* @__PURE__ */ jsx9("span", { className: "truncate", children: summary })
|
|
597
609
|
]
|
|
598
610
|
}
|
|
@@ -877,7 +889,13 @@ function NumberComparisonFilter({
|
|
|
877
889
|
"aria-label": `Filter ${column.header}`,
|
|
878
890
|
"data-testid": `filter-${column.id}`,
|
|
879
891
|
children: [
|
|
880
|
-
/* @__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
|
+
),
|
|
881
899
|
isFiltered && /* @__PURE__ */ jsx10("span", { className: "truncate", children: summary })
|
|
882
900
|
]
|
|
883
901
|
}
|
|
@@ -971,7 +989,13 @@ function NumberRangeFilter({
|
|
|
971
989
|
"aria-label": `Filter ${column.header}`,
|
|
972
990
|
"data-testid": `filter-${column.id}`,
|
|
973
991
|
children: [
|
|
974
|
-
/* @__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
|
+
),
|
|
975
999
|
isFiltered && /* @__PURE__ */ jsx11("span", { className: "truncate", children: summary })
|
|
976
1000
|
]
|
|
977
1001
|
}
|
|
@@ -1184,7 +1208,13 @@ function NumberHistogramFilter({
|
|
|
1184
1208
|
"aria-label": `Filter ${column.header}`,
|
|
1185
1209
|
"data-testid": `filter-${column.id}`,
|
|
1186
1210
|
children: [
|
|
1187
|
-
/* @__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
|
+
),
|
|
1188
1218
|
isFiltered && /* @__PURE__ */ jsx12("span", { className: "truncate", children: summary })
|
|
1189
1219
|
]
|
|
1190
1220
|
}
|
|
@@ -1300,7 +1330,13 @@ function StringFilter({
|
|
|
1300
1330
|
"aria-label": `Filter ${column.header}`,
|
|
1301
1331
|
"data-testid": `filter-${column.id}`,
|
|
1302
1332
|
children: [
|
|
1303
|
-
/* @__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
|
+
),
|
|
1304
1340
|
isFiltered && /* @__PURE__ */ jsx13("span", { className: "truncate", children: text })
|
|
1305
1341
|
]
|
|
1306
1342
|
}
|
|
@@ -1598,6 +1634,21 @@ import { useVirtualizer } from "@tanstack/react-virtual";
|
|
|
1598
1634
|
import { ChevronRight, Loader2 } from "lucide-react";
|
|
1599
1635
|
import { Fragment as Fragment2, useEffect as useEffect7, useMemo as useMemo6, useRef as useRef5, useState as useState6 } from "react";
|
|
1600
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
|
+
}
|
|
1651
|
+
|
|
1601
1652
|
// src/grid/processClientData.ts
|
|
1602
1653
|
function makeComparator(sort) {
|
|
1603
1654
|
return (a, b) => {
|
|
@@ -1755,8 +1806,8 @@ import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-r
|
|
|
1755
1806
|
var gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });
|
|
1756
1807
|
var DEFAULT_COLUMN_SIZE = 150;
|
|
1757
1808
|
var DETAIL_COLUMN_WIDTH = 44;
|
|
1758
|
-
var SELECTION_COLUMN_WIDTH = 44;
|
|
1759
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))]";
|
|
1760
1811
|
function pinnedOffsets(visibleColumns, side, getSize, leadingOffset) {
|
|
1761
1812
|
const offsets = /* @__PURE__ */ new Map();
|
|
1762
1813
|
let cursor = leadingOffset;
|
|
@@ -1813,7 +1864,12 @@ function DataGrid({
|
|
|
1813
1864
|
selectedIds: controlledSelectedIds,
|
|
1814
1865
|
onSelectedIdsChange,
|
|
1815
1866
|
testId,
|
|
1816
|
-
loading: loadingProp = false
|
|
1867
|
+
loading: loadingProp = false,
|
|
1868
|
+
groupBy,
|
|
1869
|
+
renderGroupHeader,
|
|
1870
|
+
defaultGroupsExpanded = true,
|
|
1871
|
+
expandedGroups: controlledExpandedGroups,
|
|
1872
|
+
onExpandedGroupsChange
|
|
1817
1873
|
}) {
|
|
1818
1874
|
const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(
|
|
1819
1875
|
dataSource,
|
|
@@ -1841,6 +1897,18 @@ function DataGrid({
|
|
|
1841
1897
|
return next;
|
|
1842
1898
|
});
|
|
1843
1899
|
}
|
|
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
|
+
}
|
|
1844
1912
|
const visibleColumns = useMemo6(
|
|
1845
1913
|
() => columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,
|
|
1846
1914
|
[columns, columnVisibility]
|
|
@@ -1925,8 +1993,14 @@ function DataGrid({
|
|
|
1925
1993
|
return { style: column.width ? { width: column.width } : void 0 };
|
|
1926
1994
|
}
|
|
1927
1995
|
function structuralCellProps(side, offset, width, area = "body") {
|
|
1996
|
+
const base = "border-b border-border p-1 sticky z-10";
|
|
1928
1997
|
const bg = area === "header" ? "bg-muted" : "bg-background";
|
|
1929
|
-
|
|
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
|
+
};
|
|
1930
2004
|
}
|
|
1931
2005
|
const detailCellProps = structuralCellProps("left", 0, DETAIL_COLUMN_WIDTH);
|
|
1932
2006
|
const selectionCellProps = structuralCellProps(
|
|
@@ -1943,7 +2017,7 @@ function DataGrid({
|
|
|
1943
2017
|
"header"
|
|
1944
2018
|
);
|
|
1945
2019
|
const rowActionsHeaderCellProps = structuralCellProps("right", 0, ROW_ACTIONS_COLUMN_WIDTH, "header");
|
|
1946
|
-
const BODY_TD_BASE_CLASS = "border-b p-2";
|
|
2020
|
+
const BODY_TD_BASE_CLASS = "border-b border-border p-2";
|
|
1947
2021
|
function bodyCellClassAndStyle(column) {
|
|
1948
2022
|
const pinnedProps = column.pinned || enableColumnResizing ? pinnedCellProps(column) : void 0;
|
|
1949
2023
|
return {
|
|
@@ -1965,19 +2039,23 @@ function DataGrid({
|
|
|
1965
2039
|
const leafHeaderCellPropsByColumn = useMemo6(() => {
|
|
1966
2040
|
const map = /* @__PURE__ */ new Map();
|
|
1967
2041
|
for (const column of visibleColumns) {
|
|
1968
|
-
map.set(column.id, headerCellClassAndStyle(column, "relative border-b p-2 font-medium"));
|
|
2042
|
+
map.set(column.id, headerCellClassAndStyle(column, "relative border-b border-border p-2 font-medium"));
|
|
1969
2043
|
}
|
|
1970
2044
|
return map;
|
|
1971
2045
|
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
1972
2046
|
const filterHeaderCellPropsByColumn = useMemo6(() => {
|
|
1973
2047
|
const map = /* @__PURE__ */ new Map();
|
|
1974
2048
|
for (const column of visibleColumns) {
|
|
1975
|
-
map.set(column.id, headerCellClassAndStyle(column, "border-b p-2 align-top font-normal"));
|
|
2049
|
+
map.set(column.id, headerCellClassAndStyle(column, "border-b border-border p-2 align-top font-normal"));
|
|
1976
2050
|
}
|
|
1977
2051
|
return map;
|
|
1978
2052
|
}, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
|
|
1979
2053
|
const tableRows = table.getRowModel().rows;
|
|
1980
|
-
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
|
+
);
|
|
1981
2059
|
const scrollRef = useRef5(null);
|
|
1982
2060
|
const virtualizer = useVirtualizer({
|
|
1983
2061
|
count: tableRows.length,
|
|
@@ -2009,50 +2087,97 @@ function DataGrid({
|
|
|
2009
2087
|
{
|
|
2010
2088
|
...rowProps,
|
|
2011
2089
|
"data-testid": rowTestId,
|
|
2012
|
-
className: rowProps?.className ? cn(rowProps.className, row.index % 2 === 1 && "bg-foreground/5") : row.index % 2 === 1 ? "bg-foreground/5" :
|
|
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" : ""}`,
|
|
2013
2091
|
children: [
|
|
2014
|
-
showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2015
|
-
|
|
2092
|
+
showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2093
|
+
"td",
|
|
2016
2094
|
{
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
size: "icon",
|
|
2020
|
-
"aria-label": `${isExpanded ? "Collapse" : "Expand"} row ${row.id}`,
|
|
2021
|
-
"aria-expanded": isExpanded,
|
|
2022
|
-
onClick: (event) => {
|
|
2023
|
-
stopRowClick(event);
|
|
2024
|
-
toggleRowExpanded(row.id);
|
|
2025
|
-
},
|
|
2095
|
+
className: row.index % 2 === 1 ? detailCellProps.classNameOdd : detailCellProps.className,
|
|
2096
|
+
style: detailCellProps.style,
|
|
2026
2097
|
children: /* @__PURE__ */ jsx19(
|
|
2027
|
-
|
|
2098
|
+
Button,
|
|
2028
2099
|
{
|
|
2029
|
-
|
|
2030
|
-
"
|
|
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
|
+
)
|
|
2031
2116
|
}
|
|
2032
2117
|
)
|
|
2033
2118
|
}
|
|
2034
|
-
)
|
|
2035
|
-
showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2036
|
-
"
|
|
2119
|
+
),
|
|
2120
|
+
showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2121
|
+
"td",
|
|
2037
2122
|
{
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
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
|
+
)
|
|
2043
2135
|
}
|
|
2044
|
-
)
|
|
2136
|
+
),
|
|
2045
2137
|
row.getAllCells().map((cell) => {
|
|
2046
2138
|
const cellProps = bodyCellPropsByColumn.get(cell.column.id);
|
|
2047
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);
|
|
2048
2140
|
}),
|
|
2049
|
-
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
|
+
)
|
|
2050
2149
|
]
|
|
2051
2150
|
}
|
|
2052
2151
|
),
|
|
2053
|
-
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) }) })
|
|
2054
2153
|
] }, row.id);
|
|
2055
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
|
+
}
|
|
2056
2181
|
return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-2", "data-testid": "datagrid", children: [
|
|
2057
2182
|
showSelectionColumn && headerActions && /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between text-sm", children: [
|
|
2058
2183
|
/* @__PURE__ */ jsxs12("span", { children: [
|
|
@@ -2088,27 +2213,40 @@ function DataGrid({
|
|
|
2088
2213
|
const sortable = isSortable(column);
|
|
2089
2214
|
const filterable = isFilterable(column) && column.filterDisplay !== "row";
|
|
2090
2215
|
const cellProps = leafHeaderCellPropsByColumn.get(column.id);
|
|
2216
|
+
const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;
|
|
2091
2217
|
return /* @__PURE__ */ jsxs12("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
|
|
2092
2218
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
|
|
2093
|
-
/* @__PURE__ */ jsxs12(
|
|
2219
|
+
sortable ? /* @__PURE__ */ jsxs12(
|
|
2094
2220
|
"button",
|
|
2095
2221
|
{
|
|
2096
2222
|
type: "button",
|
|
2097
|
-
className:
|
|
2098
|
-
|
|
2099
|
-
sortable && "hover:bg-accent hover:text-accent-foreground"
|
|
2100
|
-
),
|
|
2101
|
-
disabled: !sortable,
|
|
2102
|
-
onClick: (event) => sortable && toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
|
|
2223
|
+
className: "flex items-center gap-1 rounded-sm px-1 hover:bg-accent hover:text-accent-foreground",
|
|
2224
|
+
onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
|
|
2103
2225
|
children: [
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2226
|
+
headerContent,
|
|
2227
|
+
sortEntry?.dir === "asc" && /* @__PURE__ */ jsx19(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
|
|
2228
|
+
sortEntry?.dir === "desc" && /* @__PURE__ */ jsx19(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
|
|
2107
2229
|
]
|
|
2108
2230
|
}
|
|
2231
|
+
) : (
|
|
2232
|
+
// Not wrapped in a `<button>` (unlike the sortable branch above): a
|
|
2233
|
+
// disabled `<button>` swallows pointer events for its entire subtree,
|
|
2234
|
+
// silently breaking any interactive content a column's own `renderHeader`
|
|
2235
|
+
// embeds directly (bare filter inputs, "select/reject all" buttons) —
|
|
2236
|
+
// clicks never reach them at all, even though nothing looks wrong visually.
|
|
2237
|
+
/* @__PURE__ */ jsx19("div", { className: "flex items-center gap-1 px-1", children: headerContent })
|
|
2109
2238
|
),
|
|
2110
2239
|
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(
|
|
2240
|
+
/* @__PURE__ */ jsx19(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx19(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx19(
|
|
2241
|
+
FunnelIcon7,
|
|
2242
|
+
{
|
|
2243
|
+
className: cn(
|
|
2244
|
+
"h-3 w-3",
|
|
2245
|
+
filtersByColumn.get(column.id) !== void 0 ? "opacity-100 text-primary" : "opacity-40"
|
|
2246
|
+
),
|
|
2247
|
+
"aria-hidden": true
|
|
2248
|
+
}
|
|
2249
|
+
) }) }),
|
|
2112
2250
|
/* @__PURE__ */ jsx19(PopoverContent, { children: renderFilterWidget(column) })
|
|
2113
2251
|
] })
|
|
2114
2252
|
] }),
|
|
@@ -2124,7 +2262,7 @@ function DataGrid({
|
|
|
2124
2262
|
] }, header.id);
|
|
2125
2263
|
}
|
|
2126
2264
|
return /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
2127
|
-
hasHeaderGroups && /* @__PURE__ */ jsxs12("tr", { "data-testid": "header-group-row", children: [
|
|
2265
|
+
hasHeaderGroups && /* @__PURE__ */ jsxs12("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
|
|
2128
2266
|
showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2129
2267
|
"th",
|
|
2130
2268
|
{
|
|
@@ -2156,7 +2294,7 @@ function DataGrid({
|
|
|
2156
2294
|
"th",
|
|
2157
2295
|
{
|
|
2158
2296
|
colSpan: run.columns.length,
|
|
2159
|
-
className: "border-b p-2 text-center font-medium",
|
|
2297
|
+
className: "border-b border-border p-2 text-center font-medium",
|
|
2160
2298
|
children: run.label
|
|
2161
2299
|
},
|
|
2162
2300
|
`group-${run.label}-${run.columns[0].id}`
|
|
@@ -2172,7 +2310,7 @@ function DataGrid({
|
|
|
2172
2310
|
}
|
|
2173
2311
|
)
|
|
2174
2312
|
] }, `${headerGroup.id}-groups`),
|
|
2175
|
-
/* @__PURE__ */ jsxs12("tr", { children: [
|
|
2313
|
+
/* @__PURE__ */ jsxs12("tr", { className: "divide-x divide-border", children: [
|
|
2176
2314
|
!hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx19(
|
|
2177
2315
|
"th",
|
|
2178
2316
|
{
|
|
@@ -2213,7 +2351,7 @@ function DataGrid({
|
|
|
2213
2351
|
] }, headerGroup.id)
|
|
2214
2352
|
] }, headerGroup.id);
|
|
2215
2353
|
}),
|
|
2216
|
-
hasFilterRow && /* @__PURE__ */ jsxs12("tr", { "data-testid": "filter-row", children: [
|
|
2354
|
+
hasFilterRow && /* @__PURE__ */ jsxs12("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
|
|
2217
2355
|
showDetailColumn && /* @__PURE__ */ jsx19("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
|
|
2218
2356
|
showSelectionColumn && /* @__PURE__ */ jsx19(
|
|
2219
2357
|
"th",
|
|
@@ -2241,7 +2379,7 @@ function DataGrid({
|
|
|
2241
2379
|
(virtualItem) => renderRow(tableRows[virtualItem.index], (el) => virtualizer.measureElement(el))
|
|
2242
2380
|
),
|
|
2243
2381
|
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 } }) }) })
|
|
2244
|
-
] }) : tableRows.map((row) => renderRow(row))
|
|
2382
|
+
] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
|
|
2245
2383
|
] })
|
|
2246
2384
|
}
|
|
2247
2385
|
)
|
|
@@ -2306,6 +2444,31 @@ function flattenTree(roots, accessors, expanded, childrenOverride) {
|
|
|
2306
2444
|
return out;
|
|
2307
2445
|
}
|
|
2308
2446
|
|
|
2447
|
+
// src/tree/selectionState.ts
|
|
2448
|
+
function computeSelectionStates(roots, accessors, childrenOverride, selectedIds) {
|
|
2449
|
+
const { getRowId, getChildren } = accessors;
|
|
2450
|
+
const out = /* @__PURE__ */ new Map();
|
|
2451
|
+
function visit(row) {
|
|
2452
|
+
const id = getRowId(row);
|
|
2453
|
+
if (selectedIds.has(id)) {
|
|
2454
|
+
const state2 = { checked: true, indeterminate: false };
|
|
2455
|
+
out.set(id, state2);
|
|
2456
|
+
return state2;
|
|
2457
|
+
}
|
|
2458
|
+
const children = childrenOverride.get(id) ?? getChildren(row) ?? [];
|
|
2459
|
+
let anySelectedOrIndeterminate = false;
|
|
2460
|
+
for (const child of children) {
|
|
2461
|
+
const childState = visit(child);
|
|
2462
|
+
if (childState.checked || childState.indeterminate) anySelectedOrIndeterminate = true;
|
|
2463
|
+
}
|
|
2464
|
+
const state = { checked: false, indeterminate: anySelectedOrIndeterminate };
|
|
2465
|
+
out.set(id, state);
|
|
2466
|
+
return state;
|
|
2467
|
+
}
|
|
2468
|
+
for (const root of roots) visit(root);
|
|
2469
|
+
return out;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2309
2472
|
// src/tree/useTreeState.ts
|
|
2310
2473
|
import { useCallback as useCallback3, useMemo as useMemo7, useState as useState7 } from "react";
|
|
2311
2474
|
function useTreeState({
|
|
@@ -2412,6 +2575,8 @@ function useTreeState({
|
|
|
2412
2575
|
|
|
2413
2576
|
// src/tree/TreeDataGrid.tsx
|
|
2414
2577
|
import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2578
|
+
var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
|
|
2579
|
+
var UNCHECKED_STATE = { checked: false, indeterminate: false };
|
|
2415
2580
|
function renderCell(column, row) {
|
|
2416
2581
|
const value = getColumnValue(column, row);
|
|
2417
2582
|
return column.cell ? column.cell(value, row) : defaultFormat(column, value);
|
|
@@ -2466,7 +2631,11 @@ function TreeDataGrid({
|
|
|
2466
2631
|
rowActions,
|
|
2467
2632
|
virtualizeThreshold = 100,
|
|
2468
2633
|
estimatedRowHeight = 40,
|
|
2469
|
-
maxBodyHeight = 480
|
|
2634
|
+
maxBodyHeight = 480,
|
|
2635
|
+
selectedIds: controlledSelectedIds,
|
|
2636
|
+
onSelectedIdsChange,
|
|
2637
|
+
getRowSelectionState,
|
|
2638
|
+
isRowSelectionDisabled
|
|
2470
2639
|
}) {
|
|
2471
2640
|
const accessors = useMemo8(
|
|
2472
2641
|
() => ({ getRowId, getChildren, hasChildren }),
|
|
@@ -2493,7 +2662,38 @@ function TreeDataGrid({
|
|
|
2493
2662
|
);
|
|
2494
2663
|
const treeColId = treeColumnId ?? columns[0]?.id;
|
|
2495
2664
|
const showRowActionsColumn = Boolean(rowActions?.length);
|
|
2496
|
-
const
|
|
2665
|
+
const showSelectionColumn = controlledSelectedIds !== void 0;
|
|
2666
|
+
const totalColumnCount = columns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);
|
|
2667
|
+
const selectionStates = useMemo8(
|
|
2668
|
+
() => showSelectionColumn ? computeSelectionStates(data, accessors, childrenMap, controlledSelectedIds ?? EMPTY_SELECTED_IDS) : void 0,
|
|
2669
|
+
[showSelectionColumn, data, accessors, childrenMap, controlledSelectedIds]
|
|
2670
|
+
);
|
|
2671
|
+
function resolveSelectionState(row, id) {
|
|
2672
|
+
const override = getRowSelectionState?.(row);
|
|
2673
|
+
if (override) return { checked: override.checked, indeterminate: override.indeterminate ?? false };
|
|
2674
|
+
return selectionStates?.get(id) ?? UNCHECKED_STATE;
|
|
2675
|
+
}
|
|
2676
|
+
function toggleRowSelected(id) {
|
|
2677
|
+
if (!controlledSelectedIds) return;
|
|
2678
|
+
const next = new Set(controlledSelectedIds);
|
|
2679
|
+
if (next.has(id)) next.delete(id);
|
|
2680
|
+
else next.add(id);
|
|
2681
|
+
onSelectedIdsChange?.(next);
|
|
2682
|
+
}
|
|
2683
|
+
const selectableFlatRows = useMemo8(
|
|
2684
|
+
() => flatRows.filter((flatRow) => !(isRowSelectionDisabled?.(flatRow.row) ?? false)),
|
|
2685
|
+
[flatRows, isRowSelectionDisabled]
|
|
2686
|
+
);
|
|
2687
|
+
const allVisibleSelected = showSelectionColumn && selectableFlatRows.length > 0 && selectableFlatRows.every((flatRow) => controlledSelectedIds.has(flatRow.id));
|
|
2688
|
+
function toggleAllVisibleSelected() {
|
|
2689
|
+
if (!controlledSelectedIds) return;
|
|
2690
|
+
const next = new Set(controlledSelectedIds);
|
|
2691
|
+
for (const flatRow of selectableFlatRows) {
|
|
2692
|
+
if (allVisibleSelected) next.delete(flatRow.id);
|
|
2693
|
+
else next.add(flatRow.id);
|
|
2694
|
+
}
|
|
2695
|
+
onSelectedIdsChange?.(next);
|
|
2696
|
+
}
|
|
2497
2697
|
const bodyTdClassByColumn = useMemo8(() => {
|
|
2498
2698
|
const map = /* @__PURE__ */ new Map();
|
|
2499
2699
|
for (const column of columns) map.set(column.id, `p-2 ${alignClassName(column)}`);
|
|
@@ -2517,8 +2717,25 @@ function TreeDataGrid({
|
|
|
2517
2717
|
ref: measureRef,
|
|
2518
2718
|
...rowProps,
|
|
2519
2719
|
"data-testid": rowTestId,
|
|
2520
|
-
className: rowProps?.className ? cn(
|
|
2720
|
+
className: rowProps?.className ? cn(
|
|
2721
|
+
"border-b border-border divide-x divide-border",
|
|
2722
|
+
rowProps.className,
|
|
2723
|
+
index % 2 === 1 && "bg-foreground/5"
|
|
2724
|
+
) : `border-b border-border divide-x divide-border${index % 2 === 1 ? " bg-foreground/5" : ""}`,
|
|
2521
2725
|
children: [
|
|
2726
|
+
showSelectionColumn && /* @__PURE__ */ jsx20("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
|
|
2727
|
+
const state = resolveSelectionState(flatRow.row, flatRow.id);
|
|
2728
|
+
return /* @__PURE__ */ jsx20(
|
|
2729
|
+
Checkbox,
|
|
2730
|
+
{
|
|
2731
|
+
"aria-label": `Select row ${flatRow.id}`,
|
|
2732
|
+
checked: state.indeterminate ? "indeterminate" : state.checked,
|
|
2733
|
+
disabled: isRowSelectionDisabled?.(flatRow.row) ?? false,
|
|
2734
|
+
onCheckedChange: () => toggleRowSelected(flatRow.id),
|
|
2735
|
+
onClick: stopRowClick
|
|
2736
|
+
}
|
|
2737
|
+
);
|
|
2738
|
+
})() }),
|
|
2522
2739
|
columns.map((column) => /* @__PURE__ */ jsx20("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx20(
|
|
2523
2740
|
TreeCell,
|
|
2524
2741
|
{
|
|
@@ -2554,17 +2771,25 @@ function TreeDataGrid({
|
|
|
2554
2771
|
className: shouldVirtualize ? "overflow-y-auto" : void 0,
|
|
2555
2772
|
style: shouldVirtualize ? { maxHeight: maxBodyHeight } : void 0,
|
|
2556
2773
|
children: /* @__PURE__ */ jsxs13("table", { className: "w-full border-collapse text-sm", children: [
|
|
2557
|
-
/* @__PURE__ */ jsx20("thead", { children: /* @__PURE__ */ jsxs13("tr", { children: [
|
|
2774
|
+
/* @__PURE__ */ jsx20("thead", { children: /* @__PURE__ */ jsxs13("tr", { className: "divide-x divide-border", children: [
|
|
2775
|
+
showSelectionColumn && /* @__PURE__ */ jsx20("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx20(
|
|
2776
|
+
Checkbox,
|
|
2777
|
+
{
|
|
2778
|
+
"aria-label": "Select all visible rows",
|
|
2779
|
+
checked: allVisibleSelected,
|
|
2780
|
+
onCheckedChange: toggleAllVisibleSelected
|
|
2781
|
+
}
|
|
2782
|
+
) }),
|
|
2558
2783
|
columns.map((column) => /* @__PURE__ */ jsx20(
|
|
2559
2784
|
"th",
|
|
2560
2785
|
{
|
|
2561
2786
|
style: column.width ? { width: column.width } : void 0,
|
|
2562
|
-
className: `border-b p-2 font-medium ${alignClassName(column)}`,
|
|
2787
|
+
className: `border-b border-border p-2 font-medium ${alignClassName(column)}`,
|
|
2563
2788
|
children: column.header
|
|
2564
2789
|
},
|
|
2565
2790
|
column.id
|
|
2566
2791
|
)),
|
|
2567
|
-
showRowActionsColumn && /* @__PURE__ */ jsx20("th", { className: "border-b p-2", "aria-label": "Row actions" })
|
|
2792
|
+
showRowActionsColumn && /* @__PURE__ */ jsx20("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
|
|
2568
2793
|
] }) }),
|
|
2569
2794
|
/* @__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: [
|
|
2570
2795
|
paddingTop > 0 && /* @__PURE__ */ jsx20("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx20("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
|