@bmsuisse/datagrid 0.39.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2235,6 +2235,20 @@ interface TreeDataGridProps<TRow> extends TreeAccessors<TRow> {
2235
2235
  * bottom) but neither requires the other. Defaults to false.
2236
2236
  */
2237
2237
  showTotals?: boolean;
2238
+ /**
2239
+ * Opt-in: defaults to false. Gives every header a drag handle (and
2240
+ * ArrowLeft/ArrowRight keyboard resize) on its trailing edge, and switches
2241
+ * the table to `table-layout: fixed` so a dragged width is exactly the
2242
+ * rendered width. A column without an explicit `width` stays flexible and
2243
+ * absorbs the leftover space until it is resized itself.
2244
+ */
2245
+ enableColumnResizing?: boolean;
2246
+ /**
2247
+ * Controlled column widths in px, keyed by column id. Both must be supplied
2248
+ * together to control it — omit both for `<TreeDataGrid>` to own the state.
2249
+ */
2250
+ columnSizing?: ColumnSizingState;
2251
+ onColumnSizingChange?: (sizing: ColumnSizingState) => void;
2238
2252
  /**
2239
2253
  * Enables the same built-in inline-editing workflow `<DataGrid>` has:
2240
2254
  * any column with `editable` set renders as an interactive editor
@@ -2263,7 +2277,7 @@ interface TreeDataGridProps<TRow> extends TreeAccessors<TRow> {
2263
2277
  * column's cell matches `treeColumnId` (defaults to `columns[0]`); every
2264
2278
  * other column renders exactly like it would in `<DataGrid>`.
2265
2279
  */
2266
- declare function TreeDataGrid<TRow>({ columns, data, loading, error, onRetry, statusLabels, treeColumnId, columnVisibility, getRowId, getChildren, hasChildren, onLoadChildren, initialExpandedLevel, expanded: controlledExpanded, onExpandedChange, childrenMap: controlledChildrenMap, onChildrenMapChange, indentSize, getRowProps, getRowTestId, rowActions, virtualizeThreshold, estimatedRowHeight, maxBodyHeight, testId, selectedIds: controlledSelectedIds, onSelectedIdsChange, getRowSelectionState, isRowSelectionDisabled, groupBy, renderGroupHeader, defaultGroupsExpanded, expandedGroups: controlledExpandedGroups, onExpandedGroupsChange, zebra, showTotals, editing, }: TreeDataGridProps<TRow>): ReactElement;
2280
+ declare function TreeDataGrid<TRow>({ columns, data, loading, error, onRetry, statusLabels, treeColumnId, columnVisibility, getRowId, getChildren, hasChildren, onLoadChildren, initialExpandedLevel, expanded: controlledExpanded, onExpandedChange, childrenMap: controlledChildrenMap, onChildrenMapChange, indentSize, getRowProps, getRowTestId, rowActions, virtualizeThreshold, estimatedRowHeight, maxBodyHeight, testId, selectedIds: controlledSelectedIds, onSelectedIdsChange, getRowSelectionState, isRowSelectionDisabled, groupBy, renderGroupHeader, defaultGroupsExpanded, expandedGroups: controlledExpandedGroups, onExpandedGroupsChange, zebra, showTotals, editing, enableColumnResizing, columnSizing: controlledColumnSizing, onColumnSizingChange, }: TreeDataGridProps<TRow>): ReactElement;
2267
2281
 
2268
2282
  interface FlatTreeRow<TRow> {
2269
2283
  row: TRow;
package/dist/index.js CHANGED
@@ -4718,7 +4718,73 @@ import {
4718
4718
  ChevronDownIcon as ChevronDownIcon3,
4719
4719
  ChevronRightIcon as ChevronRightIcon3
4720
4720
  } from "@heroicons/react/24/outline";
4721
- import { Fragment as Fragment5, useEffect as useEffect13, useMemo as useMemo11, useRef as useRef14 } from "react";
4721
+ import { Fragment as Fragment5, useEffect as useEffect13, useMemo as useMemo11, useRef as useRef15 } from "react";
4722
+
4723
+ // src/hooks/useColumnSizing.ts
4724
+ import { useCallback as useCallback5, useState as useState17 } from "react";
4725
+ var MIN_COLUMN_SIZE2 = 40;
4726
+ var KEYBOARD_RESIZE_STEP = 10;
4727
+ function useColumnSizing(controlled, onChange) {
4728
+ const [internal, setInternal] = useState17({});
4729
+ const columnSizing = controlled ?? internal;
4730
+ const setColumnSize = useCallback5(
4731
+ (columnId, width) => {
4732
+ const next = { ...columnSizing, [columnId]: Math.max(MIN_COLUMN_SIZE2, Math.round(width)) };
4733
+ setInternal(next);
4734
+ onChange?.(next);
4735
+ },
4736
+ [columnSizing, onChange]
4737
+ );
4738
+ return { columnSizing, setColumnSize };
4739
+ }
4740
+
4741
+ // src/tree/ColumnResizeHandle.tsx
4742
+ import { useRef as useRef14 } from "react";
4743
+ import { jsx as jsx36 } from "react/jsx-runtime";
4744
+ function ColumnResizeHandle({ columnId, label, width, onResize }) {
4745
+ const drag = useRef14(null);
4746
+ const cellWidth = (handle) => handle.parentElement?.getBoundingClientRect().width ?? 0;
4747
+ const onPointerDown = (event) => {
4748
+ event.preventDefault();
4749
+ event.stopPropagation();
4750
+ event.currentTarget.setPointerCapture(event.pointerId);
4751
+ drag.current = { startX: event.clientX, startWidth: cellWidth(event.currentTarget) };
4752
+ };
4753
+ const onPointerMove = (event) => {
4754
+ if (!drag.current) return;
4755
+ onResize(columnId, drag.current.startWidth + event.clientX - drag.current.startX);
4756
+ };
4757
+ const endDrag = (event) => {
4758
+ if (!drag.current) return;
4759
+ drag.current = null;
4760
+ event.currentTarget.releasePointerCapture(event.pointerId);
4761
+ };
4762
+ const onKeyDown = (event) => {
4763
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
4764
+ event.preventDefault();
4765
+ const delta = event.key === "ArrowRight" ? KEYBOARD_RESIZE_STEP : -KEYBOARD_RESIZE_STEP;
4766
+ onResize(columnId, cellWidth(event.currentTarget) + delta);
4767
+ };
4768
+ return /* @__PURE__ */ jsx36(
4769
+ "div",
4770
+ {
4771
+ "data-testid": `resize-handle-${columnId}`,
4772
+ role: "separator",
4773
+ "aria-orientation": "vertical",
4774
+ "aria-label": `Resize ${label} column`,
4775
+ "aria-valuenow": width,
4776
+ "aria-valuemin": MIN_COLUMN_SIZE2,
4777
+ "aria-valuetext": width === void 0 ? void 0 : `${width} pixels`,
4778
+ tabIndex: 0,
4779
+ onPointerDown,
4780
+ onPointerMove,
4781
+ onPointerUp: endDrag,
4782
+ onPointerCancel: endDrag,
4783
+ onKeyDown,
4784
+ className: "absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none hover:bg-accent focus-visible:bg-accent focus-visible:outline-none"
4785
+ }
4786
+ );
4787
+ }
4722
4788
 
4723
4789
  // src/tree/flattenTree.ts
4724
4790
  function flattenTree(roots, accessors, expanded, childrenOverride) {
@@ -4763,7 +4829,7 @@ function computeSelectionStates(roots, accessors, childrenOverride, selectedIds)
4763
4829
  }
4764
4830
 
4765
4831
  // src/tree/useTreeState.ts
4766
- import { useCallback as useCallback5, useMemo as useMemo10, useState as useState17 } from "react";
4832
+ import { useCallback as useCallback6, useMemo as useMemo10, useState as useState18 } from "react";
4767
4833
  function useTreeState({
4768
4834
  data,
4769
4835
  getRowId,
@@ -4775,9 +4841,9 @@ function useTreeState({
4775
4841
  childrenMap: controlledChildrenMap,
4776
4842
  onChildrenMapChange
4777
4843
  }) {
4778
- const [internalExpanded, setInternalExpanded] = useState17({});
4844
+ const [internalExpanded, setInternalExpanded] = useState18({});
4779
4845
  const expanded = controlledExpanded ?? internalExpanded;
4780
- const setExpanded = useCallback5(
4846
+ const setExpanded = useCallback6(
4781
4847
  (updater) => {
4782
4848
  if (controlledExpanded) {
4783
4849
  onExpandedChange?.(updater(controlledExpanded));
@@ -4787,9 +4853,9 @@ function useTreeState({
4787
4853
  },
4788
4854
  [controlledExpanded, onExpandedChange]
4789
4855
  );
4790
- const [internalChildrenMap, setInternalChildrenMap] = useState17(/* @__PURE__ */ new Map());
4856
+ const [internalChildrenMap, setInternalChildrenMap] = useState18(/* @__PURE__ */ new Map());
4791
4857
  const childrenMap = controlledChildrenMap ?? internalChildrenMap;
4792
- const setChildrenMap = useCallback5(
4858
+ const setChildrenMap = useCallback6(
4793
4859
  (updater) => {
4794
4860
  if (controlledChildrenMap) {
4795
4861
  onChildrenMapChange?.(updater(controlledChildrenMap));
@@ -4799,13 +4865,13 @@ function useTreeState({
4799
4865
  },
4800
4866
  [controlledChildrenMap, onChildrenMapChange]
4801
4867
  );
4802
- const [loadingIds, setLoadingIds] = useState17(/* @__PURE__ */ new Set());
4803
- const [errorIds, setErrorIds] = useState17(/* @__PURE__ */ new Map());
4868
+ const [loadingIds, setLoadingIds] = useState18(/* @__PURE__ */ new Set());
4869
+ const [errorIds, setErrorIds] = useState18(/* @__PURE__ */ new Map());
4804
4870
  const resolveHasChildren = useMemo10(
4805
4871
  () => hasChildren ?? ((row) => (getChildren(row)?.length ?? 0) > 0),
4806
4872
  [hasChildren, getChildren]
4807
4873
  );
4808
- const loadChildrenFor = useCallback5(
4874
+ const loadChildrenFor = useCallback6(
4809
4875
  async (row) => {
4810
4876
  const id = getRowId(row);
4811
4877
  const cached = childrenMap.get(id);
@@ -4838,7 +4904,7 @@ function useTreeState({
4838
4904
  },
4839
4905
  [childrenMap, getChildren, getRowId, onLoadChildren, setChildrenMap]
4840
4906
  );
4841
- const toggleExpand = useCallback5(
4907
+ const toggleExpand = useCallback6(
4842
4908
  (row) => {
4843
4909
  const id = getRowId(row);
4844
4910
  const willExpand = !expanded[id];
@@ -4850,14 +4916,14 @@ function useTreeState({
4850
4916
  },
4851
4917
  [expanded, getRowId, loadChildrenFor, resolveHasChildren, setExpanded]
4852
4918
  );
4853
- const retry = useCallback5(
4919
+ const retry = useCallback6(
4854
4920
  (row) => {
4855
4921
  loadChildrenFor(row).catch(() => {
4856
4922
  });
4857
4923
  },
4858
4924
  [loadChildrenFor]
4859
4925
  );
4860
- const expandToLevel = useCallback5(
4926
+ const expandToLevel = useCallback6(
4861
4927
  async (level) => {
4862
4928
  const nextExpanded = { ...expanded };
4863
4929
  async function visit(row, depth) {
@@ -4880,7 +4946,7 @@ function useTreeState({
4880
4946
  }
4881
4947
 
4882
4948
  // src/tree/TreeDataGrid.tsx
4883
- import { Fragment as Fragment6, jsx as jsx36, jsxs as jsxs22 } from "react/jsx-runtime";
4949
+ import { Fragment as Fragment6, jsx as jsx37, jsxs as jsxs22 } from "react/jsx-runtime";
4884
4950
  var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
4885
4951
  var UNCHECKED_STATE = { checked: false, indeterminate: false };
4886
4952
  function renderCell(column, row, editingCtx) {
@@ -4898,7 +4964,7 @@ function TreeCell({
4898
4964
  editingCtx
4899
4965
  }) {
4900
4966
  return /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
4901
- flatRow.hasChildren ? /* @__PURE__ */ jsx36(
4967
+ flatRow.hasChildren ? /* @__PURE__ */ jsx37(
4902
4968
  "button",
4903
4969
  {
4904
4970
  type: "button",
@@ -4910,11 +4976,11 @@ function TreeCell({
4910
4976
  "aria-label": flatRow.isExpanded ? "Collapse" : "Expand",
4911
4977
  "aria-expanded": flatRow.isExpanded,
4912
4978
  className: "flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50",
4913
- children: isLoading ? /* @__PURE__ */ jsx36(ArrowPathIcon2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx36(ChevronDownIcon3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx36(ChevronRightIcon3, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
4979
+ children: isLoading ? /* @__PURE__ */ jsx37(ArrowPathIcon2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx37(ChevronDownIcon3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx37(ChevronRightIcon3, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
4914
4980
  }
4915
- ) : /* @__PURE__ */ jsx36("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
4916
- /* @__PURE__ */ jsx36("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
4917
- flatRow.isExpanded && error ? /* @__PURE__ */ jsx36(
4981
+ ) : /* @__PURE__ */ jsx37("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
4982
+ /* @__PURE__ */ jsx37("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
4983
+ flatRow.isExpanded && error ? /* @__PURE__ */ jsx37(
4918
4984
  GridInlineError,
4919
4985
  {
4920
4986
  error: { message: labels.nodeLoadFailed, details: error },
@@ -4962,7 +5028,10 @@ function TreeDataGrid({
4962
5028
  onExpandedGroupsChange,
4963
5029
  zebra = true,
4964
5030
  showTotals = false,
4965
- editing
5031
+ editing,
5032
+ enableColumnResizing = false,
5033
+ columnSizing: controlledColumnSizing,
5034
+ onColumnSizingChange
4966
5035
  }) {
4967
5036
  const labels = resolveStatusLabels(statusLabels);
4968
5037
  const hasError = toErrorInfo(error) !== void 0;
@@ -4982,7 +5051,7 @@ function TreeDataGrid({
4982
5051
  childrenMap: controlledChildrenMap,
4983
5052
  onChildrenMapChange
4984
5053
  });
4985
- const didAutoExpand = useRef14(false);
5054
+ const didAutoExpand = useRef15(false);
4986
5055
  useEffect13(() => {
4987
5056
  if (didAutoExpand.current) return;
4988
5057
  didAutoExpand.current = true;
@@ -5022,6 +5091,12 @@ function TreeDataGrid({
5022
5091
  const showRowActionsColumn = Boolean(rowActions?.length);
5023
5092
  const showSelectionColumn = controlledSelectedIds !== void 0;
5024
5093
  const totalColumnCount = visibleColumns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);
5094
+ const { columnSizing, setColumnSize } = useColumnSizing(controlledColumnSizing, onColumnSizingChange);
5095
+ const columnWidthStyle = (column) => {
5096
+ const width = enableColumnResizing ? columnSizing[column.id] ?? column.width : column.width;
5097
+ return width ? { width } : void 0;
5098
+ };
5099
+ const fixedTableWidth = enableColumnResizing && !showRowActionsColumn && visibleColumns.every((c) => columnWidthStyle(c)) ? visibleColumns.reduce((sum, c) => sum + Number(columnWidthStyle(c)?.width), showSelectionColumn ? SELECTION_COLUMN_WIDTH : 0) : void 0;
5025
5100
  const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));
5026
5101
  const selectionStates = useMemo11(
5027
5102
  () => showSelectionColumn ? computeSelectionStates(data, accessors, childrenMap, controlledSelectedIds ?? EMPTY_SELECTED_IDS) : void 0,
@@ -5055,14 +5130,15 @@ function TreeDataGrid({
5055
5130
  }
5056
5131
  const bodyTdClassByColumn = useMemo11(() => {
5057
5132
  const map = /* @__PURE__ */ new Map();
5058
- for (const column of visibleColumns) map.set(column.id, `p-2 ${alignClassName(column)}`);
5133
+ const clip = enableColumnResizing ? "overflow-hidden " : "";
5134
+ for (const column of visibleColumns) map.set(column.id, `${clip}p-2 ${alignClassName(column)}`);
5059
5135
  return map;
5060
- }, [visibleColumns]);
5136
+ }, [visibleColumns, enableColumnResizing]);
5061
5137
  const hasColumnSummary = visibleColumns.some((column) => column.summary);
5062
5138
  function renderGroupSummaryCells(rows) {
5063
- return visibleColumns.map((column) => /* @__PURE__ */ jsx36("td", { className: cn(bodyTdClassByColumn.get(column.id), "font-medium"), children: column.summary?.(rows) }, column.id));
5139
+ return visibleColumns.map((column) => /* @__PURE__ */ jsx37("td", { className: cn(bodyTdClassByColumn.get(column.id), "font-medium"), children: column.summary?.(rows) }, column.id));
5064
5140
  }
5065
- const scrollRef = useRef14(null);
5141
+ const scrollRef = useRef15(null);
5066
5142
  const shouldVirtualize = !groupBy && flatRows.length > virtualizeThreshold;
5067
5143
  const virtualizer = useVirtualizer2({
5068
5144
  count: flatRows.length,
@@ -5088,9 +5164,9 @@ function TreeDataGrid({
5088
5164
  isOddRow && "bg-foreground/5"
5089
5165
  ) : `border-b border-border divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
5090
5166
  children: [
5091
- showSelectionColumn && /* @__PURE__ */ jsx36("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
5167
+ showSelectionColumn && /* @__PURE__ */ jsx37("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
5092
5168
  const state = resolveSelectionState(flatRow.row, flatRow.id);
5093
- return /* @__PURE__ */ jsx36(
5169
+ return /* @__PURE__ */ jsx37(
5094
5170
  Checkbox,
5095
5171
  {
5096
5172
  "aria-label": `Select row ${flatRow.id}`,
@@ -5101,7 +5177,7 @@ function TreeDataGrid({
5101
5177
  }
5102
5178
  );
5103
5179
  })() }),
5104
- visibleColumns.map((column) => /* @__PURE__ */ jsx36("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx36(
5180
+ visibleColumns.map((column) => /* @__PURE__ */ jsx37("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx37(
5105
5181
  TreeCell,
5106
5182
  {
5107
5183
  flatRow,
@@ -5115,7 +5191,7 @@ function TreeDataGrid({
5115
5191
  editingCtx: editingState.ctx
5116
5192
  }
5117
5193
  ) : renderCell(column, flatRow.row, editingState.ctx) }, column.id)),
5118
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx36("td", { className: "p-2", children: /* @__PURE__ */ jsx36(
5194
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx37("td", { className: "p-2", children: /* @__PURE__ */ jsx37(
5119
5195
  ActionsMenu,
5120
5196
  {
5121
5197
  items: rowActions,
@@ -5132,8 +5208,8 @@ function TreeDataGrid({
5132
5208
  const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
5133
5209
  const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
5134
5210
  return /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: [
5135
- hasError && data.length > 0 && /* @__PURE__ */ jsx36("div", { className: "flex shrink-0 items-center rounded-md border border-destructive/40 bg-destructive/10 px-2 py-1", children: /* @__PURE__ */ jsx36(GridInlineError, { error, onRetry, labels }) }),
5136
- editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx36(
5211
+ hasError && data.length > 0 && /* @__PURE__ */ jsx37("div", { className: "flex shrink-0 items-center rounded-md border border-destructive/40 bg-destructive/10 px-2 py-1", children: /* @__PURE__ */ jsx37(GridInlineError, { error, onRetry, labels }) }),
5212
+ editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx37(
5137
5213
  EditingBar,
5138
5214
  {
5139
5215
  editing,
@@ -5144,143 +5220,167 @@ function TreeDataGrid({
5144
5220
  testIdPrefix: "tree-datagrid"
5145
5221
  }
5146
5222
  ),
5147
- /* @__PURE__ */ jsx36(
5223
+ /* @__PURE__ */ jsx37(
5148
5224
  "div",
5149
5225
  {
5150
5226
  ref: scrollRef,
5151
5227
  "data-testid": testId,
5152
5228
  className: "overflow-y-auto",
5153
5229
  style: { maxHeight: maxBodyHeight },
5154
- children: /* @__PURE__ */ jsxs22("table", { className: "w-full border-collapse text-sm", children: [
5155
- /* @__PURE__ */ jsx36("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs22("tr", { className: "divide-x divide-border", children: [
5156
- showSelectionColumn && /* @__PURE__ */ jsx36(
5157
- "th",
5158
- {
5159
- className: "sticky top-0 z-20 border-b border-border bg-muted p-1",
5160
- style: { width: SELECTION_COLUMN_WIDTH },
5161
- children: /* @__PURE__ */ jsx36(
5162
- Checkbox,
5230
+ children: /* @__PURE__ */ jsxs22(
5231
+ "table",
5232
+ {
5233
+ className: cn(
5234
+ "border-collapse text-sm",
5235
+ fixedTableWidth === void 0 && "w-full",
5236
+ enableColumnResizing && "table-fixed"
5237
+ ),
5238
+ style: fixedTableWidth === void 0 ? void 0 : { width: fixedTableWidth },
5239
+ children: [
5240
+ /* @__PURE__ */ jsx37("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs22("tr", { className: "divide-x divide-border", children: [
5241
+ showSelectionColumn && /* @__PURE__ */ jsx37(
5242
+ "th",
5163
5243
  {
5164
- "aria-label": "Select all visible rows",
5165
- checked: allVisibleSelected,
5166
- onCheckedChange: toggleAllVisibleSelected
5244
+ className: "sticky top-0 z-20 border-b border-border bg-muted p-1",
5245
+ style: { width: SELECTION_COLUMN_WIDTH },
5246
+ children: /* @__PURE__ */ jsx37(
5247
+ Checkbox,
5248
+ {
5249
+ "aria-label": "Select all visible rows",
5250
+ checked: allVisibleSelected,
5251
+ onCheckedChange: toggleAllVisibleSelected
5252
+ }
5253
+ )
5167
5254
  }
5168
- )
5169
- }
5170
- ),
5171
- visibleColumns.map((column) => /* @__PURE__ */ jsx36(
5172
- "th",
5173
- {
5174
- style: column.width ? { width: column.width } : void 0,
5175
- className: cn(
5176
- "sticky top-0 z-20 border-b border-border bg-muted p-2 font-medium",
5177
- alignClassName(column)
5178
5255
  ),
5179
- children: column.header
5180
- },
5181
- column.id
5182
- )),
5183
- showRowActionsColumn && /* @__PURE__ */ jsx36("th", { className: "sticky top-0 z-20 border-b border-border bg-muted p-2", "aria-label": "Row actions" })
5184
- ] }) }),
5185
- /* @__PURE__ */ jsx36("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx36("tr", { children: /* @__PURE__ */ jsx36("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? /* @__PURE__ */ jsx36(LoadingRows, { label: labels.loading }) : hasError ? /* @__PURE__ */ jsx36(GridErrorState, { error, onRetry, labels }) : labels.noResults }) }) : groupedBuckets ? groupedBuckets.map((bucket) => {
5186
- const groupExpanded = isGroupExpanded(bucket.key);
5187
- return /* @__PURE__ */ jsxs22(Fragment5, { children: [
5188
- /* @__PURE__ */ jsx36("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx36(
5189
- "td",
5190
- {
5191
- colSpan: totalColumnCount,
5192
- className: cn(
5193
- "sticky z-20 border-b p-2 font-medium",
5194
- zebra ? "bg-muted" : "bg-background"
5195
- ),
5196
- style: { top: groupHeaderTop },
5197
- children: /* @__PURE__ */ jsxs22(
5198
- "button",
5256
+ visibleColumns.map((column) => /* @__PURE__ */ jsxs22(
5257
+ "th",
5258
+ {
5259
+ style: columnWidthStyle(column),
5260
+ className: cn(
5261
+ "sticky top-0 z-20 border-b border-border bg-muted p-2 font-medium",
5262
+ enableColumnResizing && "overflow-hidden",
5263
+ alignClassName(column)
5264
+ ),
5265
+ children: [
5266
+ column.header,
5267
+ enableColumnResizing && /* @__PURE__ */ jsx37(
5268
+ ColumnResizeHandle,
5269
+ {
5270
+ columnId: column.id,
5271
+ label: String(column.header),
5272
+ width: columnSizing[column.id] ?? column.width,
5273
+ onResize: setColumnSize
5274
+ }
5275
+ )
5276
+ ]
5277
+ },
5278
+ column.id
5279
+ )),
5280
+ showRowActionsColumn && /* @__PURE__ */ jsx37("th", { className: "sticky top-0 z-20 border-b border-border bg-muted p-2", "aria-label": "Row actions" })
5281
+ ] }) }),
5282
+ /* @__PURE__ */ jsx37("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx37("tr", { children: /* @__PURE__ */ jsx37("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? /* @__PURE__ */ jsx37(LoadingRows, { label: labels.loading }) : hasError ? /* @__PURE__ */ jsx37(GridErrorState, { error, onRetry, labels }) : labels.noResults }) }) : groupedBuckets ? groupedBuckets.map((bucket) => {
5283
+ const groupExpanded = isGroupExpanded(bucket.key);
5284
+ return /* @__PURE__ */ jsxs22(Fragment5, { children: [
5285
+ /* @__PURE__ */ jsx37("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx37(
5286
+ "td",
5287
+ {
5288
+ colSpan: totalColumnCount,
5289
+ className: cn(
5290
+ "sticky z-20 border-b p-2 font-medium",
5291
+ zebra ? "bg-muted" : "bg-background"
5292
+ ),
5293
+ style: { top: groupHeaderTop },
5294
+ children: /* @__PURE__ */ jsxs22(
5295
+ "button",
5296
+ {
5297
+ type: "button",
5298
+ className: "flex w-full items-center gap-1 text-left",
5299
+ onClick: () => toggleGroupExpanded(bucket.key),
5300
+ "aria-expanded": groupExpanded,
5301
+ children: [
5302
+ /* @__PURE__ */ jsx37(
5303
+ ChevronRightIcon3,
5304
+ {
5305
+ className: `h-4 w-4 shrink-0 transition-transform${groupExpanded ? " rotate-90" : ""}`,
5306
+ "aria-hidden": true
5307
+ }
5308
+ ),
5309
+ renderGroupHeader ? renderGroupHeader(bucket.key, bucket.roots, groupExpanded) : `${bucket.key} (${bucket.roots.length})`
5310
+ ]
5311
+ }
5312
+ )
5313
+ }
5314
+ ) }),
5315
+ hasColumnSummary && /* @__PURE__ */ jsxs22(
5316
+ "tr",
5199
5317
  {
5200
- type: "button",
5201
- className: "flex w-full items-center gap-1 text-left",
5202
- onClick: () => toggleGroupExpanded(bucket.key),
5203
- "aria-expanded": groupExpanded,
5318
+ "data-testid": `group-summary-${bucket.key}`,
5319
+ className: "border-b border-border divide-x divide-border",
5204
5320
  children: [
5205
- /* @__PURE__ */ jsx36(
5206
- ChevronRightIcon3,
5321
+ showSelectionColumn && /* @__PURE__ */ jsx37(
5322
+ "td",
5207
5323
  {
5208
- className: `h-4 w-4 shrink-0 transition-transform${groupExpanded ? " rotate-90" : ""}`,
5209
- "aria-hidden": true
5324
+ className: cn("p-2", zebra ? "bg-muted" : "bg-background"),
5325
+ style: { width: SELECTION_COLUMN_WIDTH }
5210
5326
  }
5211
5327
  ),
5212
- renderGroupHeader ? renderGroupHeader(bucket.key, bucket.roots, groupExpanded) : `${bucket.key} (${bucket.roots.length})`
5328
+ renderGroupSummaryCells(bucket.roots),
5329
+ showRowActionsColumn && /* @__PURE__ */ jsx37("td", { className: cn("p-2", zebra ? "bg-muted" : "bg-background"), "aria-hidden": true })
5213
5330
  ]
5214
5331
  }
5215
- )
5216
- }
5217
- ) }),
5218
- hasColumnSummary && /* @__PURE__ */ jsxs22(
5219
- "tr",
5220
- {
5221
- "data-testid": `group-summary-${bucket.key}`,
5222
- className: "border-b border-border divide-x divide-border",
5223
- children: [
5224
- showSelectionColumn && /* @__PURE__ */ jsx36(
5225
- "td",
5226
- {
5227
- className: cn("p-2", zebra ? "bg-muted" : "bg-background"),
5228
- style: { width: SELECTION_COLUMN_WIDTH }
5229
- }
5230
- ),
5231
- renderGroupSummaryCells(bucket.roots),
5232
- showRowActionsColumn && /* @__PURE__ */ jsx36("td", { className: cn("p-2", zebra ? "bg-muted" : "bg-background"), "aria-hidden": true })
5233
- ]
5234
- }
5235
- ),
5236
- groupExpanded && (() => {
5237
- const startIndex = groupedRender?.startIndexByBucket.get(bucket.key) ?? 0;
5238
- return bucket.flatRows.map((flatRow, i) => renderRow(flatRow, startIndex + i));
5239
- })()
5240
- ] }, `group-${bucket.key}`);
5241
- }) : shouldVirtualize ? /* @__PURE__ */ jsxs22(Fragment6, { children: [
5242
- paddingTop > 0 && /* @__PURE__ */ jsx36("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx36("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
5243
- virtualItems.map(
5244
- (virtualItem) => renderRow(flatRows[virtualItem.index], virtualItem.index, (el) => virtualizer.measureElement(el))
5245
- ),
5246
- paddingBottom > 0 && /* @__PURE__ */ jsx36("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx36(
5247
- "td",
5248
- {
5249
- colSpan: totalColumnCount,
5250
- style: { padding: 0, border: "none", height: paddingBottom }
5251
- }
5252
- ) })
5253
- ] }) : flatRows.map((flatRow, index) => renderRow(flatRow, index)) }),
5254
- showTotals && hasColumnSummary && // Sticky to the BOTTOM of the scroll container, mirroring the
5255
- // `<thead>`'s own `top-0` stickiness — see `<DataGrid>`'s own
5256
- // `renderTotalsFooter` for the full reasoning. Summarizes `data`
5257
- // (the root-level rows), not `flatRows`/`allFlatRows` — same
5258
- // "roots only" scope `groupBy` itself uses, and for the same
5259
- // reason (a child's value is usually already folded into its
5260
- // parent's).
5261
- /* @__PURE__ */ jsx36("tfoot", { children: /* @__PURE__ */ jsxs22("tr", { "data-testid": "totals-row", className: "divide-x divide-border", children: [
5262
- showSelectionColumn && /* @__PURE__ */ jsx36(
5263
- "td",
5264
- {
5265
- className: "sticky bottom-0 z-20 border-t border-border bg-muted p-2",
5266
- style: { width: SELECTION_COLUMN_WIDTH }
5267
- }
5268
- ),
5269
- visibleColumns.map((column) => /* @__PURE__ */ jsx36(
5270
- "td",
5271
- {
5272
- style: column.width ? { width: column.width } : void 0,
5273
- className: cn(
5274
- "sticky bottom-0 z-20 border-t border-border bg-muted p-2 font-medium",
5275
- alignClassName(column)
5332
+ ),
5333
+ groupExpanded && (() => {
5334
+ const startIndex = groupedRender?.startIndexByBucket.get(bucket.key) ?? 0;
5335
+ return bucket.flatRows.map((flatRow, i) => renderRow(flatRow, startIndex + i));
5336
+ })()
5337
+ ] }, `group-${bucket.key}`);
5338
+ }) : shouldVirtualize ? /* @__PURE__ */ jsxs22(Fragment6, { children: [
5339
+ paddingTop > 0 && /* @__PURE__ */ jsx37("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx37("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
5340
+ virtualItems.map(
5341
+ (virtualItem) => renderRow(flatRows[virtualItem.index], virtualItem.index, (el) => virtualizer.measureElement(el))
5276
5342
  ),
5277
- children: column.summary?.(data)
5278
- },
5279
- column.id
5280
- )),
5281
- showRowActionsColumn && /* @__PURE__ */ jsx36("td", { className: "sticky bottom-0 z-20 border-t border-border bg-muted p-2", "aria-hidden": true })
5282
- ] }) })
5283
- ] })
5343
+ paddingBottom > 0 && /* @__PURE__ */ jsx37("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx37(
5344
+ "td",
5345
+ {
5346
+ colSpan: totalColumnCount,
5347
+ style: { padding: 0, border: "none", height: paddingBottom }
5348
+ }
5349
+ ) })
5350
+ ] }) : flatRows.map((flatRow, index) => renderRow(flatRow, index)) }),
5351
+ showTotals && hasColumnSummary && // Sticky to the BOTTOM of the scroll container, mirroring the
5352
+ // `<thead>`'s own `top-0` stickiness — see `<DataGrid>`'s own
5353
+ // `renderTotalsFooter` for the full reasoning. Summarizes `data`
5354
+ // (the root-level rows), not `flatRows`/`allFlatRows` — same
5355
+ // "roots only" scope `groupBy` itself uses, and for the same
5356
+ // reason (a child's value is usually already folded into its
5357
+ // parent's).
5358
+ /* @__PURE__ */ jsx37("tfoot", { children: /* @__PURE__ */ jsxs22("tr", { "data-testid": "totals-row", className: "divide-x divide-border", children: [
5359
+ showSelectionColumn && /* @__PURE__ */ jsx37(
5360
+ "td",
5361
+ {
5362
+ className: "sticky bottom-0 z-20 border-t border-border bg-muted p-2",
5363
+ style: { width: SELECTION_COLUMN_WIDTH }
5364
+ }
5365
+ ),
5366
+ visibleColumns.map((column) => /* @__PURE__ */ jsx37(
5367
+ "td",
5368
+ {
5369
+ style: columnWidthStyle(column),
5370
+ className: cn(
5371
+ "sticky bottom-0 z-20 border-t border-border bg-muted p-2 font-medium",
5372
+ enableColumnResizing && "overflow-hidden",
5373
+ alignClassName(column)
5374
+ ),
5375
+ children: column.summary?.(data)
5376
+ },
5377
+ column.id
5378
+ )),
5379
+ showRowActionsColumn && /* @__PURE__ */ jsx37("td", { className: "sticky bottom-0 z-20 border-t border-border bg-muted p-2", "aria-hidden": true })
5380
+ ] }) })
5381
+ ]
5382
+ }
5383
+ )
5284
5384
  }
5285
5385
  )
5286
5386
  ] });