@bmsuisse/datagrid 0.25.1 → 0.25.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1632,7 +1632,7 @@ import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon as FunnelIcon7 } from "@
1632
1632
  import { columnResizingFeature, columnSizingFeature, flexRender, tableFeatures, useTable } from "@tanstack/react-table";
1633
1633
  import { useVirtualizer } from "@tanstack/react-virtual";
1634
1634
  import { ChevronRight, Loader2 } from "lucide-react";
1635
- import { Fragment as Fragment2, useEffect as useEffect7, useMemo as useMemo6, useRef as useRef5, useState as useState6 } from "react";
1635
+ import { Fragment as Fragment2, useEffect as useEffect7, useLayoutEffect, useMemo as useMemo6, useRef as useRef5, useState as useState6 } from "react";
1636
1636
 
1637
1637
  // src/lib/structuralColumns.ts
1638
1638
  var SELECTION_COLUMN_WIDTH = 44;
@@ -1994,7 +1994,7 @@ function DataGrid({
1994
1994
  return { style: column.width ? { width: column.width } : void 0 };
1995
1995
  }
1996
1996
  function structuralCellProps(side, offset, width, area = "body") {
1997
- const base = "border-b border-border p-1 sticky z-10";
1997
+ const base = area === "header" ? "border-b border-border p-1 sticky top-0 z-20" : "border-b border-border p-1 sticky z-10";
1998
1998
  const bg = area === "header" ? "bg-muted" : "bg-background";
1999
1999
  const bgOdd = area === "header" ? "bg-muted" : STRUCTURAL_ZEBRA_BG_CLASS;
2000
2000
  return {
@@ -2031,16 +2031,24 @@ function DataGrid({
2031
2031
  for (const column of visibleColumns) map.set(column.id, bodyCellClassAndStyle(column));
2032
2032
  return map;
2033
2033
  }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
2034
- function headerCellClassAndStyle(column, base) {
2034
+ function headerCellClassAndStyle(column, base, options = {}) {
2035
2035
  const pinnedProps = pinnedCellProps(column, "header");
2036
2036
  const align = alignClassName(column);
2037
- const className = pinnedProps.className ? `${base} ${align} ${pinnedProps.className}` : `${base} ${align}`;
2037
+ const className = cn(
2038
+ base,
2039
+ align,
2040
+ pinnedProps.className,
2041
+ options.sticky && "sticky top-0 z-20 bg-muted"
2042
+ );
2038
2043
  return { className, style: pinnedProps.style };
2039
2044
  }
2040
2045
  const leafHeaderCellPropsByColumn = useMemo6(() => {
2041
2046
  const map = /* @__PURE__ */ new Map();
2042
2047
  for (const column of visibleColumns) {
2043
- map.set(column.id, headerCellClassAndStyle(column, "relative border-b border-border p-2 font-medium"));
2048
+ map.set(
2049
+ column.id,
2050
+ headerCellClassAndStyle(column, "relative border-b border-border p-2 font-medium", { sticky: true })
2051
+ );
2044
2052
  }
2045
2053
  return map;
2046
2054
  }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
@@ -2057,6 +2065,18 @@ function DataGrid({
2057
2065
  () => groupBy ? groupRows(tableRows, (row) => groupBy(row.original)) : void 0,
2058
2066
  [tableRows, groupBy]
2059
2067
  );
2068
+ const theadRef = useRef5(null);
2069
+ const [groupHeaderTop, setGroupHeaderTop] = useState6(0);
2070
+ useLayoutEffect(() => {
2071
+ if (!groupBy) return;
2072
+ const theadEl = theadRef.current;
2073
+ if (!theadEl) return;
2074
+ const updateHeight = () => setGroupHeaderTop(theadEl.getBoundingClientRect().height);
2075
+ updateHeight();
2076
+ const observer = new ResizeObserver(updateHeight);
2077
+ observer.observe(theadEl);
2078
+ return () => observer.disconnect();
2079
+ }, [groupBy]);
2060
2080
  const scrollRef = useRef5(null);
2061
2081
  const virtualizer = useVirtualizer({
2062
2082
  count: tableRows.length,
@@ -2158,270 +2178,295 @@ function DataGrid({
2158
2178
  const expanded = isGroupExpanded(bucket.key);
2159
2179
  const originalRows = bucket.items.map((row) => row.original);
2160
2180
  return /* @__PURE__ */ jsxs12(Fragment2, { children: [
2161
- /* @__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(
2162
- "button",
2181
+ /* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx19(
2182
+ "td",
2163
2183
  {
2164
- type: "button",
2165
- className: "flex w-full items-center gap-1 text-left",
2166
- onClick: () => toggleGroupExpanded(bucket.key),
2167
- "aria-expanded": expanded,
2168
- children: [
2169
- /* @__PURE__ */ jsx19(
2170
- ChevronRight,
2171
- {
2172
- className: `h-4 w-4 shrink-0 transition-transform${expanded ? " rotate-90" : ""}`,
2173
- "aria-hidden": true
2174
- }
2175
- ),
2176
- renderGroupHeader ? renderGroupHeader(bucket.key, originalRows, expanded) : `${bucket.key} (${originalRows.length})`
2177
- ]
2184
+ colSpan: totalColumnCount,
2185
+ className: cn(
2186
+ "sticky z-20 border-b p-2 font-medium",
2187
+ zebra ? "bg-muted" : "bg-background"
2188
+ ),
2189
+ style: { top: groupHeaderTop },
2190
+ children: /* @__PURE__ */ jsxs12(
2191
+ "button",
2192
+ {
2193
+ type: "button",
2194
+ className: "flex w-full items-center gap-1 text-left",
2195
+ onClick: () => toggleGroupExpanded(bucket.key),
2196
+ "aria-expanded": expanded,
2197
+ children: [
2198
+ /* @__PURE__ */ jsx19(
2199
+ ChevronRight,
2200
+ {
2201
+ className: `h-4 w-4 shrink-0 transition-transform${expanded ? " rotate-90" : ""}`,
2202
+ "aria-hidden": true
2203
+ }
2204
+ ),
2205
+ renderGroupHeader ? renderGroupHeader(bucket.key, originalRows, expanded) : `${bucket.key} (${originalRows.length})`
2206
+ ]
2207
+ }
2208
+ )
2178
2209
  }
2179
- ) }) }) }),
2210
+ ) }) }),
2180
2211
  expanded && bucket.items.map((row) => renderRow(row))
2181
2212
  ] }, `group-${bucket.key}`);
2182
2213
  }
2183
- return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-2", "data-testid": "datagrid", children: [
2184
- showSelectionColumn && headerActions && /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between text-sm", children: [
2185
- /* @__PURE__ */ jsxs12("span", { children: [
2186
- selectedRows.length,
2187
- " selected"
2214
+ return (
2215
+ // `h-full`/`min-h-0` here and on the two wrappers below are no-ops unless
2216
+ // a consumer itself gives <DataGrid> a bounded height (e.g. wraps it in
2217
+ // its own `flex-1 min-h-0` container) -- percentage heights fall back to
2218
+ // `auto` with no definite ancestor height, so every existing natural-
2219
+ // height usage renders unchanged. Wrap it in a bounded container, though,
2220
+ // and this cascades down to the actual scrolling div below instead of
2221
+ // silently rendering at full unbounded content height, which used to
2222
+ // force any consumer wanting a bounded, page-filling grid to add its own
2223
+ // *second* `overflow-auto` wrapper around <DataGrid> -- two nested scroll
2224
+ // containers (that one scrolling vertically, this component's own
2225
+ // `overflow-x-auto` div scrolling horizontally) fighting over which one
2226
+ // a sticky header/pinned column is actually stuck relative to.
2227
+ /* @__PURE__ */ jsxs12("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
2228
+ showSelectionColumn && headerActions && /* @__PURE__ */ jsxs12("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
2229
+ /* @__PURE__ */ jsxs12("span", { children: [
2230
+ selectedRows.length,
2231
+ " selected"
2232
+ ] }),
2233
+ /* @__PURE__ */ jsx19(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
2188
2234
  ] }),
2189
- /* @__PURE__ */ jsx19(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
2190
- ] }),
2191
- /* @__PURE__ */ jsxs12("div", { className: "relative", children: [
2192
- loading && /* @__PURE__ */ jsx19(
2193
- "div",
2194
- {
2195
- "data-testid": "datagrid-loading-overlay",
2196
- className: "absolute inset-0 z-10 flex items-center justify-center bg-background/60",
2197
- children: /* @__PURE__ */ jsx19(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
2198
- }
2199
- ),
2200
- /* @__PURE__ */ jsx19(
2201
- "div",
2202
- {
2203
- ref: scrollRef,
2204
- "data-testid": testId,
2205
- className: cn("overflow-x-auto rounded-md border", shouldVirtualize && "overflow-y-auto"),
2206
- style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : void 0,
2207
- children: /* @__PURE__ */ jsxs12("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
2208
- /* @__PURE__ */ jsxs12("thead", { className: "bg-muted", children: [
2209
- table.getHeaderGroups().map((headerGroup) => {
2210
- const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));
2211
- function renderLeafHeaderCell(column, rowSpan) {
2212
- const header = headerById.get(column.id);
2213
- if (!header) return null;
2214
- const sortEntry = state.sort.find((s) => s.field === column.id);
2215
- const sortable = isSortable(column);
2216
- const filterable = isFilterable(column) && column.filterDisplay !== "row";
2217
- const cellProps = leafHeaderCellPropsByColumn.get(column.id);
2218
- const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;
2219
- return /* @__PURE__ */ jsxs12("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
2220
- /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
2221
- sortable ? /* @__PURE__ */ jsxs12(
2222
- "button",
2235
+ /* @__PURE__ */ jsxs12("div", { className: "relative min-h-0 flex-1", children: [
2236
+ loading && /* @__PURE__ */ jsx19(
2237
+ "div",
2238
+ {
2239
+ "data-testid": "datagrid-loading-overlay",
2240
+ className: "absolute inset-0 z-30 flex items-center justify-center bg-background/60",
2241
+ children: /* @__PURE__ */ jsx19(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
2242
+ }
2243
+ ),
2244
+ /* @__PURE__ */ jsx19(
2245
+ "div",
2246
+ {
2247
+ ref: scrollRef,
2248
+ "data-testid": testId,
2249
+ className: "h-full overflow-auto rounded-md border",
2250
+ style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : void 0,
2251
+ children: /* @__PURE__ */ jsxs12("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
2252
+ /* @__PURE__ */ jsxs12("thead", { ref: theadRef, className: "bg-muted", children: [
2253
+ table.getHeaderGroups().map((headerGroup) => {
2254
+ const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));
2255
+ function renderLeafHeaderCell(column, rowSpan) {
2256
+ const header = headerById.get(column.id);
2257
+ if (!header) return null;
2258
+ const sortEntry = state.sort.find((s) => s.field === column.id);
2259
+ const sortable = isSortable(column);
2260
+ const filterable = isFilterable(column) && column.filterDisplay !== "row";
2261
+ const cellProps = leafHeaderCellPropsByColumn.get(column.id);
2262
+ const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;
2263
+ return /* @__PURE__ */ jsxs12("th", { rowSpan, style: cellProps?.style, className: cellProps?.className, children: [
2264
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
2265
+ sortable ? /* @__PURE__ */ jsxs12(
2266
+ "button",
2267
+ {
2268
+ type: "button",
2269
+ className: "flex items-center gap-1 rounded-sm px-1 hover:bg-accent hover:text-accent-foreground",
2270
+ onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
2271
+ children: [
2272
+ headerContent,
2273
+ sortEntry?.dir === "asc" && /* @__PURE__ */ jsx19(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
2274
+ sortEntry?.dir === "desc" && /* @__PURE__ */ jsx19(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
2275
+ ]
2276
+ }
2277
+ ) : (
2278
+ // Not wrapped in a `<button>` (unlike the sortable branch above): a
2279
+ // disabled `<button>` swallows pointer events for its entire subtree,
2280
+ // silently breaking any interactive content a column's own `renderHeader`
2281
+ // embeds directly (bare filter inputs, "select/reject all" buttons) —
2282
+ // clicks never reach them at all, even though nothing looks wrong visually.
2283
+ /* @__PURE__ */ jsx19("div", { className: "flex items-center gap-1 px-1", children: headerContent })
2284
+ ),
2285
+ filterable && /* @__PURE__ */ jsxs12(Popover, { children: [
2286
+ /* @__PURE__ */ jsx19(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx19(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx19(
2287
+ FunnelIcon7,
2288
+ {
2289
+ className: cn(
2290
+ "h-3 w-3",
2291
+ filtersByColumn.get(column.id) !== void 0 ? "opacity-100 text-primary" : "opacity-40"
2292
+ ),
2293
+ "aria-hidden": true
2294
+ }
2295
+ ) }) }),
2296
+ /* @__PURE__ */ jsx19(PopoverContent, { children: renderFilterWidget(column) })
2297
+ ] })
2298
+ ] }),
2299
+ enableColumnResizing && /* @__PURE__ */ jsx19(
2300
+ "div",
2223
2301
  {
2224
- type: "button",
2225
- className: "flex items-center gap-1 rounded-sm px-1 hover:bg-accent hover:text-accent-foreground",
2226
- onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
2227
- children: [
2228
- headerContent,
2229
- sortEntry?.dir === "asc" && /* @__PURE__ */ jsx19(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
2230
- sortEntry?.dir === "desc" && /* @__PURE__ */ jsx19(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
2231
- ]
2302
+ "data-testid": `resize-handle-${column.id}`,
2303
+ onMouseDown: header.getResizeHandler(),
2304
+ onTouchStart: header.getResizeHandler(),
2305
+ className: "absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent"
2306
+ }
2307
+ )
2308
+ ] }, header.id);
2309
+ }
2310
+ return /* @__PURE__ */ jsxs12(Fragment2, { children: [
2311
+ hasHeaderGroups && /* @__PURE__ */ jsxs12("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
2312
+ showDetailColumn && /* @__PURE__ */ jsx19(
2313
+ "th",
2314
+ {
2315
+ rowSpan: 2,
2316
+ className: detailHeaderCellProps.className,
2317
+ style: detailHeaderCellProps.style,
2318
+ "aria-hidden": true
2232
2319
  }
2233
- ) : (
2234
- // Not wrapped in a `<button>` (unlike the sortable branch above): a
2235
- // disabled `<button>` swallows pointer events for its entire subtree,
2236
- // silently breaking any interactive content a column's own `renderHeader`
2237
- // embeds directly (bare filter inputs, "select/reject all" buttons) —
2238
- // clicks never reach them at all, even though nothing looks wrong visually.
2239
- /* @__PURE__ */ jsx19("div", { className: "flex items-center gap-1 px-1", children: headerContent })
2240
2320
  ),
2241
- filterable && /* @__PURE__ */ jsxs12(Popover, { children: [
2242
- /* @__PURE__ */ jsx19(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx19(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx19(
2243
- FunnelIcon7,
2244
- {
2245
- className: cn(
2246
- "h-3 w-3",
2247
- filtersByColumn.get(column.id) !== void 0 ? "opacity-100 text-primary" : "opacity-40"
2248
- ),
2249
- "aria-hidden": true
2250
- }
2251
- ) }) }),
2252
- /* @__PURE__ */ jsx19(PopoverContent, { children: renderFilterWidget(column) })
2253
- ] })
2254
- ] }),
2255
- enableColumnResizing && /* @__PURE__ */ jsx19(
2256
- "div",
2257
- {
2258
- "data-testid": `resize-handle-${column.id}`,
2259
- onMouseDown: header.getResizeHandler(),
2260
- onTouchStart: header.getResizeHandler(),
2261
- className: "absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent"
2262
- }
2263
- )
2264
- ] }, header.id);
2265
- }
2266
- return /* @__PURE__ */ jsxs12(Fragment2, { children: [
2267
- hasHeaderGroups && /* @__PURE__ */ jsxs12("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
2268
- showDetailColumn && /* @__PURE__ */ jsx19(
2269
- "th",
2270
- {
2271
- rowSpan: 2,
2272
- className: detailHeaderCellProps.className,
2273
- style: detailHeaderCellProps.style,
2274
- "aria-hidden": true
2275
- }
2276
- ),
2277
- showSelectionColumn && /* @__PURE__ */ jsx19(
2278
- "th",
2279
- {
2280
- rowSpan: 2,
2281
- className: selectionHeaderCellProps.className,
2282
- style: selectionHeaderCellProps.style,
2283
- children: /* @__PURE__ */ jsx19(
2284
- "input",
2321
+ showSelectionColumn && /* @__PURE__ */ jsx19(
2322
+ "th",
2323
+ {
2324
+ rowSpan: 2,
2325
+ className: selectionHeaderCellProps.className,
2326
+ style: selectionHeaderCellProps.style,
2327
+ children: /* @__PURE__ */ jsx19(
2328
+ "input",
2329
+ {
2330
+ type: "checkbox",
2331
+ "aria-label": "Select all rows on this page",
2332
+ checked: allOnPageSelected,
2333
+ onChange: toggleAllOnPageSelected
2334
+ }
2335
+ )
2336
+ }
2337
+ ),
2338
+ headerRuns.map(
2339
+ (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx19(
2340
+ "th",
2285
2341
  {
2286
- type: "checkbox",
2287
- "aria-label": "Select all rows on this page",
2288
- checked: allOnPageSelected,
2289
- onChange: toggleAllOnPageSelected
2290
- }
2342
+ colSpan: run.columns.length,
2343
+ className: "border-b border-border p-2 text-center font-medium",
2344
+ children: run.label
2345
+ },
2346
+ `group-${run.label}-${run.columns[0].id}`
2291
2347
  )
2292
- }
2293
- ),
2294
- headerRuns.map(
2295
- (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx19(
2348
+ ),
2349
+ showRowActionsColumn && /* @__PURE__ */ jsx19(
2296
2350
  "th",
2297
2351
  {
2298
- colSpan: run.columns.length,
2299
- className: "border-b border-border p-2 text-center font-medium",
2300
- children: run.label
2301
- },
2302
- `group-${run.label}-${run.columns[0].id}`
2352
+ rowSpan: 2,
2353
+ className: rowActionsHeaderCellProps.className,
2354
+ style: rowActionsHeaderCellProps.style,
2355
+ "aria-label": "Row actions"
2356
+ }
2303
2357
  )
2304
- ),
2305
- showRowActionsColumn && /* @__PURE__ */ jsx19(
2306
- "th",
2307
- {
2308
- rowSpan: 2,
2309
- className: rowActionsHeaderCellProps.className,
2310
- style: rowActionsHeaderCellProps.style,
2311
- "aria-label": "Row actions"
2312
- }
2313
- )
2314
- ] }, `${headerGroup.id}-groups`),
2315
- /* @__PURE__ */ jsxs12("tr", { className: "divide-x divide-border", children: [
2316
- !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx19(
2317
- "th",
2318
- {
2319
- className: detailHeaderCellProps.className,
2320
- style: detailHeaderCellProps.style,
2321
- "aria-hidden": true
2322
- }
2323
- ),
2324
- !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx19(
2325
- "th",
2326
- {
2327
- className: selectionHeaderCellProps.className,
2328
- style: selectionHeaderCellProps.style,
2329
- children: /* @__PURE__ */ jsx19(
2330
- "input",
2331
- {
2332
- type: "checkbox",
2333
- "aria-label": "Select all rows on this page",
2334
- checked: allOnPageSelected,
2335
- onChange: toggleAllOnPageSelected
2336
- }
2337
- )
2338
- }
2339
- ),
2340
- headerGroup.headers.map((header) => {
2341
- const column = columnById.get(header.id);
2342
- if (!column || hasHeaderGroups && !column.headerGroup) return null;
2343
- return renderLeafHeaderCell(column);
2344
- }),
2345
- !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx19(
2346
- "th",
2347
- {
2348
- className: rowActionsHeaderCellProps.className,
2349
- style: rowActionsHeaderCellProps.style,
2350
- "aria-label": "Row actions"
2351
- }
2352
- )
2353
- ] }, headerGroup.id)
2354
- ] }, headerGroup.id);
2355
- }),
2356
- hasFilterRow && /* @__PURE__ */ jsxs12("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
2357
- showDetailColumn && /* @__PURE__ */ jsx19("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
2358
- showSelectionColumn && /* @__PURE__ */ jsx19(
2359
- "th",
2360
- {
2361
- className: selectionHeaderCellProps.className,
2362
- style: selectionHeaderCellProps.style
2363
- }
2364
- ),
2365
- visibleColumns.map((column) => {
2366
- const cellProps = filterHeaderCellPropsByColumn.get(column.id);
2367
- return /* @__PURE__ */ jsx19("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
2358
+ ] }, `${headerGroup.id}-groups`),
2359
+ /* @__PURE__ */ jsxs12("tr", { className: "divide-x divide-border", children: [
2360
+ !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx19(
2361
+ "th",
2362
+ {
2363
+ className: detailHeaderCellProps.className,
2364
+ style: detailHeaderCellProps.style,
2365
+ "aria-hidden": true
2366
+ }
2367
+ ),
2368
+ !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx19(
2369
+ "th",
2370
+ {
2371
+ className: selectionHeaderCellProps.className,
2372
+ style: selectionHeaderCellProps.style,
2373
+ children: /* @__PURE__ */ jsx19(
2374
+ "input",
2375
+ {
2376
+ type: "checkbox",
2377
+ "aria-label": "Select all rows on this page",
2378
+ checked: allOnPageSelected,
2379
+ onChange: toggleAllOnPageSelected
2380
+ }
2381
+ )
2382
+ }
2383
+ ),
2384
+ headerGroup.headers.map((header) => {
2385
+ const column = columnById.get(header.id);
2386
+ if (!column || hasHeaderGroups && !column.headerGroup) return null;
2387
+ return renderLeafHeaderCell(column);
2388
+ }),
2389
+ !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx19(
2390
+ "th",
2391
+ {
2392
+ className: rowActionsHeaderCellProps.className,
2393
+ style: rowActionsHeaderCellProps.style,
2394
+ "aria-label": "Row actions"
2395
+ }
2396
+ )
2397
+ ] }, headerGroup.id)
2398
+ ] }, headerGroup.id);
2368
2399
  }),
2369
- showRowActionsColumn && /* @__PURE__ */ jsx19(
2370
- "th",
2371
- {
2372
- className: rowActionsHeaderCellProps.className,
2373
- style: rowActionsHeaderCellProps.style
2374
- }
2375
- )
2376
- ] })
2377
- ] }),
2378
- 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: [
2379
- paddingTop > 0 && /* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
2380
- virtualItems.map(
2381
- (virtualItem) => renderRow(tableRows[virtualItem.index], (el) => virtualizer.measureElement(el))
2382
- ),
2383
- 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 } }) }) })
2384
- ] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
2385
- ] })
2386
- }
2387
- )
2388
- ] }),
2389
- showPagination && /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between text-sm", children: [
2390
- /* @__PURE__ */ jsxs12("span", { children: [
2391
- "Page ",
2392
- state.page + 1,
2393
- " of ",
2394
- pageCount,
2395
- " (",
2396
- rowCount,
2397
- " row",
2398
- rowCount === 1 ? "" : "s",
2399
- ")"
2400
- ] }),
2401
- /* @__PURE__ */ jsxs12("div", { className: "flex gap-2", children: [
2402
- /* @__PURE__ */ jsx19(
2403
- Button,
2404
- {
2405
- variant: "outline",
2406
- size: "sm",
2407
- disabled: state.page <= 0,
2408
- onClick: () => setPage(state.page - 1),
2409
- children: "Previous"
2410
- }
2411
- ),
2412
- /* @__PURE__ */ jsx19(
2413
- Button,
2414
- {
2415
- variant: "outline",
2416
- size: "sm",
2417
- disabled: state.page >= pageCount - 1,
2418
- onClick: () => setPage(state.page + 1),
2419
- children: "Next"
2400
+ hasFilterRow && /* @__PURE__ */ jsxs12("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
2401
+ showDetailColumn && /* @__PURE__ */ jsx19("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
2402
+ showSelectionColumn && /* @__PURE__ */ jsx19(
2403
+ "th",
2404
+ {
2405
+ className: selectionHeaderCellProps.className,
2406
+ style: selectionHeaderCellProps.style
2407
+ }
2408
+ ),
2409
+ visibleColumns.map((column) => {
2410
+ const cellProps = filterHeaderCellPropsByColumn.get(column.id);
2411
+ return /* @__PURE__ */ jsx19("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
2412
+ }),
2413
+ showRowActionsColumn && /* @__PURE__ */ jsx19(
2414
+ "th",
2415
+ {
2416
+ className: rowActionsHeaderCellProps.className,
2417
+ style: rowActionsHeaderCellProps.style
2418
+ }
2419
+ )
2420
+ ] })
2421
+ ] }),
2422
+ 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: [
2423
+ paddingTop > 0 && /* @__PURE__ */ jsx19("tbody", { children: /* @__PURE__ */ jsx19("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx19("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
2424
+ virtualItems.map(
2425
+ (virtualItem) => renderRow(tableRows[virtualItem.index], (el) => virtualizer.measureElement(el))
2426
+ ),
2427
+ 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 } }) }) })
2428
+ ] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
2429
+ ] })
2420
2430
  }
2421
2431
  )
2432
+ ] }),
2433
+ showPagination && /* @__PURE__ */ jsxs12("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
2434
+ /* @__PURE__ */ jsxs12("span", { children: [
2435
+ "Page ",
2436
+ state.page + 1,
2437
+ " of ",
2438
+ pageCount,
2439
+ " (",
2440
+ rowCount,
2441
+ " row",
2442
+ rowCount === 1 ? "" : "s",
2443
+ ")"
2444
+ ] }),
2445
+ /* @__PURE__ */ jsxs12("div", { className: "flex gap-2", children: [
2446
+ /* @__PURE__ */ jsx19(
2447
+ Button,
2448
+ {
2449
+ variant: "outline",
2450
+ size: "sm",
2451
+ disabled: state.page <= 0,
2452
+ onClick: () => setPage(state.page - 1),
2453
+ children: "Previous"
2454
+ }
2455
+ ),
2456
+ /* @__PURE__ */ jsx19(
2457
+ Button,
2458
+ {
2459
+ variant: "outline",
2460
+ size: "sm",
2461
+ disabled: state.page >= pageCount - 1,
2462
+ onClick: () => setPage(state.page + 1),
2463
+ children: "Next"
2464
+ }
2465
+ )
2466
+ ] })
2422
2467
  ] })
2423
2468
  ] })
2424
- ] });
2469
+ );
2425
2470
  }
2426
2471
 
2427
2472
  // src/tree/TreeDataGrid.tsx