@bmsuisse/datagrid 0.39.0 → 0.41.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.js CHANGED
@@ -3267,7 +3267,7 @@ function GridInlineError({
3267
3267
  }
3268
3268
 
3269
3269
  // src/grid/DataGrid.tsx
3270
- import { Fragment as Fragment3, useEffect as useEffect12, useMemo as useMemo9, useRef as useRef13, useState as useState16 } from "react";
3270
+ import { Fragment as Fragment3, useEffect as useEffect12, useId as useId4, useMemo as useMemo9, useRef as useRef13, useState as useState16 } from "react";
3271
3271
 
3272
3272
  // src/cell-editing/renderAlwaysEditCell.tsx
3273
3273
  import {
@@ -3689,6 +3689,8 @@ function DataGrid({
3689
3689
  onGridStateChange,
3690
3690
  selectedIds: controlledSelectedIds,
3691
3691
  onSelectedIdsChange,
3692
+ selectionMode = "multiple",
3693
+ maxHeight,
3692
3694
  testId,
3693
3695
  loading: loadingProp = false,
3694
3696
  error,
@@ -3715,11 +3717,18 @@ function DataGrid({
3715
3717
  onGridStateChange
3716
3718
  );
3717
3719
  const [internalSelectedIds, setInternalSelectedIds] = useState16(/* @__PURE__ */ new Set());
3718
- const selectedIds = controlledSelectedIds ?? internalSelectedIds;
3720
+ const rawSelectedIds = controlledSelectedIds ?? internalSelectedIds;
3721
+ const selectedIds = useMemo9(
3722
+ () => selectionMode === "single" && rawSelectedIds.size > 1 ? new Set([...rawSelectedIds].slice(0, 1)) : rawSelectedIds,
3723
+ [selectionMode, rawSelectedIds]
3724
+ );
3719
3725
  function updateSelectedIds(next) {
3720
3726
  setInternalSelectedIds(next);
3721
3727
  onSelectedIdsChange?.(next);
3722
3728
  }
3729
+ useEffect12(() => {
3730
+ if (selectedIds !== rawSelectedIds) updateSelectedIds(selectedIds);
3731
+ }, [selectedIds, rawSelectedIds]);
3723
3732
  const [internalColumnSizing, setInternalColumnSizing] = useState16({});
3724
3733
  const columnSizing = controlledColumnSizing ?? internalColumnSizing;
3725
3734
  function updateColumnSizing(next) {
@@ -3760,7 +3769,13 @@ function DataGrid({
3760
3769
  [rows, selectedIds, getRowId]
3761
3770
  );
3762
3771
  const allOnPageSelected = rows.length > 0 && rows.every((row) => selectedIds.has(getRowId(row)));
3772
+ const singleSelect = selectionMode === "single";
3773
+ const selectionGroupName = useId4();
3763
3774
  function toggleRowSelected(id) {
3775
+ if (singleSelect) {
3776
+ updateSelectedIds(/* @__PURE__ */ new Set([id]));
3777
+ return;
3778
+ }
3764
3779
  const next = new Set(selectedIds);
3765
3780
  if (next.has(id)) next.delete(id);
3766
3781
  else next.add(id);
@@ -3803,7 +3818,7 @@ function DataGrid({
3803
3818
  const labels = resolveStatusLabels(statusLabels);
3804
3819
  const hasError = toErrorInfo(error) !== void 0;
3805
3820
  const loading = loadingProp || (dataSource.mode === "server" ? Boolean(dataSource.loading) : false);
3806
- const showSelectionColumn = Boolean(headerActions?.length) || controlledSelectedIds !== void 0;
3821
+ const showSelectionColumn = Boolean(headerActions?.length) || controlledSelectedIds !== void 0 || selectionMode === "single";
3807
3822
  const showRowActionsColumn = Boolean(rowActions?.length);
3808
3823
  const showDetailColumn = Boolean(renderDetail);
3809
3824
  const totalColumnCount = visibleColumns.length + (showDetailColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0) + (showRowActionsColumn ? 1 : 0);
@@ -4124,6 +4139,8 @@ function DataGrid({
4124
4139
  }
4125
4140
  function handleCellKeyDown(event) {
4126
4141
  if (!hasCellEditing || !cellEditingCtx) return;
4142
+ if (event.target instanceof HTMLInputElement && (event.target.type === "radio" || event.target.type === "checkbox") && !event.target.closest("[data-cell-row]"))
4143
+ return;
4127
4144
  if (cellEditingCtx.alwaysEdit) return;
4128
4145
  if (cellEditingCtx.editingCell && !cellEditingCtx.consumeJustCommitted()) return;
4129
4146
  const direction = CELL_NAV_KEYS[event.key];
@@ -4253,7 +4270,8 @@ function DataGrid({
4253
4270
  children: /* @__PURE__ */ jsx35(
4254
4271
  "input",
4255
4272
  {
4256
- type: "checkbox",
4273
+ type: singleSelect ? "radio" : "checkbox",
4274
+ name: singleSelect ? selectionGroupName : void 0,
4257
4275
  "aria-label": `Select row ${row.id}`,
4258
4276
  checked: selectedIds.has(row.id),
4259
4277
  onClick: stopRowClick,
@@ -4432,7 +4450,7 @@ function DataGrid({
4432
4450
  "h-full overflow-auto rounded-md border",
4433
4451
  hasCellEditing && "relative"
4434
4452
  ),
4435
- style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : void 0,
4453
+ style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : maxHeight !== void 0 ? { maxHeight } : void 0,
4436
4454
  tabIndex: hasCellEditing ? 0 : void 0,
4437
4455
  onMouseDown: hasCellEditing ? handleCellMouseDown : void 0,
4438
4456
  onDoubleClick: hasCellEditing ? handleCellDoubleClick : void 0,
@@ -4550,7 +4568,7 @@ function DataGrid({
4550
4568
  rowSpan: 2,
4551
4569
  className: selectionHeaderCellProps.className,
4552
4570
  style: selectionHeaderCellProps.style,
4553
- children: /* @__PURE__ */ jsx35(
4571
+ children: !singleSelect && /* @__PURE__ */ jsx35(
4554
4572
  "input",
4555
4573
  {
4556
4574
  type: "checkbox",
@@ -4596,7 +4614,7 @@ function DataGrid({
4596
4614
  {
4597
4615
  className: selectionHeaderCellProps.className,
4598
4616
  style: selectionHeaderCellProps.style,
4599
- children: /* @__PURE__ */ jsx35(
4617
+ children: !singleSelect && /* @__PURE__ */ jsx35(
4600
4618
  "input",
4601
4619
  {
4602
4620
  type: "checkbox",
@@ -4718,7 +4736,73 @@ import {
4718
4736
  ChevronDownIcon as ChevronDownIcon3,
4719
4737
  ChevronRightIcon as ChevronRightIcon3
4720
4738
  } from "@heroicons/react/24/outline";
4721
- import { Fragment as Fragment5, useEffect as useEffect13, useMemo as useMemo11, useRef as useRef14 } from "react";
4739
+ import { Fragment as Fragment5, useEffect as useEffect13, useMemo as useMemo11, useRef as useRef15 } from "react";
4740
+
4741
+ // src/hooks/useColumnSizing.ts
4742
+ import { useCallback as useCallback5, useState as useState17 } from "react";
4743
+ var MIN_COLUMN_SIZE2 = 40;
4744
+ var KEYBOARD_RESIZE_STEP = 10;
4745
+ function useColumnSizing(controlled, onChange) {
4746
+ const [internal, setInternal] = useState17({});
4747
+ const columnSizing = controlled ?? internal;
4748
+ const setColumnSize = useCallback5(
4749
+ (columnId, width) => {
4750
+ const next = { ...columnSizing, [columnId]: Math.max(MIN_COLUMN_SIZE2, Math.round(width)) };
4751
+ setInternal(next);
4752
+ onChange?.(next);
4753
+ },
4754
+ [columnSizing, onChange]
4755
+ );
4756
+ return { columnSizing, setColumnSize };
4757
+ }
4758
+
4759
+ // src/tree/ColumnResizeHandle.tsx
4760
+ import { useRef as useRef14 } from "react";
4761
+ import { jsx as jsx36 } from "react/jsx-runtime";
4762
+ function ColumnResizeHandle({ columnId, label, width, onResize }) {
4763
+ const drag = useRef14(null);
4764
+ const cellWidth = (handle) => handle.parentElement?.getBoundingClientRect().width ?? 0;
4765
+ const onPointerDown = (event) => {
4766
+ event.preventDefault();
4767
+ event.stopPropagation();
4768
+ event.currentTarget.setPointerCapture(event.pointerId);
4769
+ drag.current = { startX: event.clientX, startWidth: cellWidth(event.currentTarget) };
4770
+ };
4771
+ const onPointerMove = (event) => {
4772
+ if (!drag.current) return;
4773
+ onResize(columnId, drag.current.startWidth + event.clientX - drag.current.startX);
4774
+ };
4775
+ const endDrag = (event) => {
4776
+ if (!drag.current) return;
4777
+ drag.current = null;
4778
+ event.currentTarget.releasePointerCapture(event.pointerId);
4779
+ };
4780
+ const onKeyDown = (event) => {
4781
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
4782
+ event.preventDefault();
4783
+ const delta = event.key === "ArrowRight" ? KEYBOARD_RESIZE_STEP : -KEYBOARD_RESIZE_STEP;
4784
+ onResize(columnId, cellWidth(event.currentTarget) + delta);
4785
+ };
4786
+ return /* @__PURE__ */ jsx36(
4787
+ "div",
4788
+ {
4789
+ "data-testid": `resize-handle-${columnId}`,
4790
+ role: "separator",
4791
+ "aria-orientation": "vertical",
4792
+ "aria-label": `Resize ${label} column`,
4793
+ "aria-valuenow": width,
4794
+ "aria-valuemin": MIN_COLUMN_SIZE2,
4795
+ "aria-valuetext": width === void 0 ? void 0 : `${width} pixels`,
4796
+ tabIndex: 0,
4797
+ onPointerDown,
4798
+ onPointerMove,
4799
+ onPointerUp: endDrag,
4800
+ onPointerCancel: endDrag,
4801
+ onKeyDown,
4802
+ 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"
4803
+ }
4804
+ );
4805
+ }
4722
4806
 
4723
4807
  // src/tree/flattenTree.ts
4724
4808
  function flattenTree(roots, accessors, expanded, childrenOverride) {
@@ -4763,7 +4847,7 @@ function computeSelectionStates(roots, accessors, childrenOverride, selectedIds)
4763
4847
  }
4764
4848
 
4765
4849
  // src/tree/useTreeState.ts
4766
- import { useCallback as useCallback5, useMemo as useMemo10, useState as useState17 } from "react";
4850
+ import { useCallback as useCallback6, useMemo as useMemo10, useState as useState18 } from "react";
4767
4851
  function useTreeState({
4768
4852
  data,
4769
4853
  getRowId,
@@ -4775,9 +4859,9 @@ function useTreeState({
4775
4859
  childrenMap: controlledChildrenMap,
4776
4860
  onChildrenMapChange
4777
4861
  }) {
4778
- const [internalExpanded, setInternalExpanded] = useState17({});
4862
+ const [internalExpanded, setInternalExpanded] = useState18({});
4779
4863
  const expanded = controlledExpanded ?? internalExpanded;
4780
- const setExpanded = useCallback5(
4864
+ const setExpanded = useCallback6(
4781
4865
  (updater) => {
4782
4866
  if (controlledExpanded) {
4783
4867
  onExpandedChange?.(updater(controlledExpanded));
@@ -4787,9 +4871,9 @@ function useTreeState({
4787
4871
  },
4788
4872
  [controlledExpanded, onExpandedChange]
4789
4873
  );
4790
- const [internalChildrenMap, setInternalChildrenMap] = useState17(/* @__PURE__ */ new Map());
4874
+ const [internalChildrenMap, setInternalChildrenMap] = useState18(/* @__PURE__ */ new Map());
4791
4875
  const childrenMap = controlledChildrenMap ?? internalChildrenMap;
4792
- const setChildrenMap = useCallback5(
4876
+ const setChildrenMap = useCallback6(
4793
4877
  (updater) => {
4794
4878
  if (controlledChildrenMap) {
4795
4879
  onChildrenMapChange?.(updater(controlledChildrenMap));
@@ -4799,13 +4883,13 @@ function useTreeState({
4799
4883
  },
4800
4884
  [controlledChildrenMap, onChildrenMapChange]
4801
4885
  );
4802
- const [loadingIds, setLoadingIds] = useState17(/* @__PURE__ */ new Set());
4803
- const [errorIds, setErrorIds] = useState17(/* @__PURE__ */ new Map());
4886
+ const [loadingIds, setLoadingIds] = useState18(/* @__PURE__ */ new Set());
4887
+ const [errorIds, setErrorIds] = useState18(/* @__PURE__ */ new Map());
4804
4888
  const resolveHasChildren = useMemo10(
4805
4889
  () => hasChildren ?? ((row) => (getChildren(row)?.length ?? 0) > 0),
4806
4890
  [hasChildren, getChildren]
4807
4891
  );
4808
- const loadChildrenFor = useCallback5(
4892
+ const loadChildrenFor = useCallback6(
4809
4893
  async (row) => {
4810
4894
  const id = getRowId(row);
4811
4895
  const cached = childrenMap.get(id);
@@ -4838,7 +4922,7 @@ function useTreeState({
4838
4922
  },
4839
4923
  [childrenMap, getChildren, getRowId, onLoadChildren, setChildrenMap]
4840
4924
  );
4841
- const toggleExpand = useCallback5(
4925
+ const toggleExpand = useCallback6(
4842
4926
  (row) => {
4843
4927
  const id = getRowId(row);
4844
4928
  const willExpand = !expanded[id];
@@ -4850,14 +4934,14 @@ function useTreeState({
4850
4934
  },
4851
4935
  [expanded, getRowId, loadChildrenFor, resolveHasChildren, setExpanded]
4852
4936
  );
4853
- const retry = useCallback5(
4937
+ const retry = useCallback6(
4854
4938
  (row) => {
4855
4939
  loadChildrenFor(row).catch(() => {
4856
4940
  });
4857
4941
  },
4858
4942
  [loadChildrenFor]
4859
4943
  );
4860
- const expandToLevel = useCallback5(
4944
+ const expandToLevel = useCallback6(
4861
4945
  async (level) => {
4862
4946
  const nextExpanded = { ...expanded };
4863
4947
  async function visit(row, depth) {
@@ -4880,7 +4964,7 @@ function useTreeState({
4880
4964
  }
4881
4965
 
4882
4966
  // src/tree/TreeDataGrid.tsx
4883
- import { Fragment as Fragment6, jsx as jsx36, jsxs as jsxs22 } from "react/jsx-runtime";
4967
+ import { Fragment as Fragment6, jsx as jsx37, jsxs as jsxs22 } from "react/jsx-runtime";
4884
4968
  var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
4885
4969
  var UNCHECKED_STATE = { checked: false, indeterminate: false };
4886
4970
  function renderCell(column, row, editingCtx) {
@@ -4898,7 +4982,7 @@ function TreeCell({
4898
4982
  editingCtx
4899
4983
  }) {
4900
4984
  return /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
4901
- flatRow.hasChildren ? /* @__PURE__ */ jsx36(
4985
+ flatRow.hasChildren ? /* @__PURE__ */ jsx37(
4902
4986
  "button",
4903
4987
  {
4904
4988
  type: "button",
@@ -4910,11 +4994,11 @@ function TreeCell({
4910
4994
  "aria-label": flatRow.isExpanded ? "Collapse" : "Expand",
4911
4995
  "aria-expanded": flatRow.isExpanded,
4912
4996
  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" })
4997
+ 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
4998
  }
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(
4999
+ ) : /* @__PURE__ */ jsx37("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
5000
+ /* @__PURE__ */ jsx37("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
5001
+ flatRow.isExpanded && error ? /* @__PURE__ */ jsx37(
4918
5002
  GridInlineError,
4919
5003
  {
4920
5004
  error: { message: labels.nodeLoadFailed, details: error },
@@ -4962,7 +5046,10 @@ function TreeDataGrid({
4962
5046
  onExpandedGroupsChange,
4963
5047
  zebra = true,
4964
5048
  showTotals = false,
4965
- editing
5049
+ editing,
5050
+ enableColumnResizing = false,
5051
+ columnSizing: controlledColumnSizing,
5052
+ onColumnSizingChange
4966
5053
  }) {
4967
5054
  const labels = resolveStatusLabels(statusLabels);
4968
5055
  const hasError = toErrorInfo(error) !== void 0;
@@ -4982,7 +5069,7 @@ function TreeDataGrid({
4982
5069
  childrenMap: controlledChildrenMap,
4983
5070
  onChildrenMapChange
4984
5071
  });
4985
- const didAutoExpand = useRef14(false);
5072
+ const didAutoExpand = useRef15(false);
4986
5073
  useEffect13(() => {
4987
5074
  if (didAutoExpand.current) return;
4988
5075
  didAutoExpand.current = true;
@@ -5022,6 +5109,12 @@ function TreeDataGrid({
5022
5109
  const showRowActionsColumn = Boolean(rowActions?.length);
5023
5110
  const showSelectionColumn = controlledSelectedIds !== void 0;
5024
5111
  const totalColumnCount = visibleColumns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);
5112
+ const { columnSizing, setColumnSize } = useColumnSizing(controlledColumnSizing, onColumnSizingChange);
5113
+ const columnWidthStyle = (column) => {
5114
+ const width = enableColumnResizing ? columnSizing[column.id] ?? column.width : column.width;
5115
+ return width ? { width } : void 0;
5116
+ };
5117
+ 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
5118
  const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));
5026
5119
  const selectionStates = useMemo11(
5027
5120
  () => showSelectionColumn ? computeSelectionStates(data, accessors, childrenMap, controlledSelectedIds ?? EMPTY_SELECTED_IDS) : void 0,
@@ -5055,14 +5148,15 @@ function TreeDataGrid({
5055
5148
  }
5056
5149
  const bodyTdClassByColumn = useMemo11(() => {
5057
5150
  const map = /* @__PURE__ */ new Map();
5058
- for (const column of visibleColumns) map.set(column.id, `p-2 ${alignClassName(column)}`);
5151
+ const clip = enableColumnResizing ? "overflow-hidden " : "";
5152
+ for (const column of visibleColumns) map.set(column.id, `${clip}p-2 ${alignClassName(column)}`);
5059
5153
  return map;
5060
- }, [visibleColumns]);
5154
+ }, [visibleColumns, enableColumnResizing]);
5061
5155
  const hasColumnSummary = visibleColumns.some((column) => column.summary);
5062
5156
  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));
5157
+ return visibleColumns.map((column) => /* @__PURE__ */ jsx37("td", { className: cn(bodyTdClassByColumn.get(column.id), "font-medium"), children: column.summary?.(rows) }, column.id));
5064
5158
  }
5065
- const scrollRef = useRef14(null);
5159
+ const scrollRef = useRef15(null);
5066
5160
  const shouldVirtualize = !groupBy && flatRows.length > virtualizeThreshold;
5067
5161
  const virtualizer = useVirtualizer2({
5068
5162
  count: flatRows.length,
@@ -5088,9 +5182,9 @@ function TreeDataGrid({
5088
5182
  isOddRow && "bg-foreground/5"
5089
5183
  ) : `border-b border-border divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
5090
5184
  children: [
5091
- showSelectionColumn && /* @__PURE__ */ jsx36("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
5185
+ showSelectionColumn && /* @__PURE__ */ jsx37("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
5092
5186
  const state = resolveSelectionState(flatRow.row, flatRow.id);
5093
- return /* @__PURE__ */ jsx36(
5187
+ return /* @__PURE__ */ jsx37(
5094
5188
  Checkbox,
5095
5189
  {
5096
5190
  "aria-label": `Select row ${flatRow.id}`,
@@ -5101,7 +5195,7 @@ function TreeDataGrid({
5101
5195
  }
5102
5196
  );
5103
5197
  })() }),
5104
- visibleColumns.map((column) => /* @__PURE__ */ jsx36("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx36(
5198
+ visibleColumns.map((column) => /* @__PURE__ */ jsx37("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx37(
5105
5199
  TreeCell,
5106
5200
  {
5107
5201
  flatRow,
@@ -5115,7 +5209,7 @@ function TreeDataGrid({
5115
5209
  editingCtx: editingState.ctx
5116
5210
  }
5117
5211
  ) : renderCell(column, flatRow.row, editingState.ctx) }, column.id)),
5118
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx36("td", { className: "p-2", children: /* @__PURE__ */ jsx36(
5212
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx37("td", { className: "p-2", children: /* @__PURE__ */ jsx37(
5119
5213
  ActionsMenu,
5120
5214
  {
5121
5215
  items: rowActions,
@@ -5132,8 +5226,8 @@ function TreeDataGrid({
5132
5226
  const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
5133
5227
  const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
5134
5228
  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(
5229
+ 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 }) }),
5230
+ editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx37(
5137
5231
  EditingBar,
5138
5232
  {
5139
5233
  editing,
@@ -5144,143 +5238,167 @@ function TreeDataGrid({
5144
5238
  testIdPrefix: "tree-datagrid"
5145
5239
  }
5146
5240
  ),
5147
- /* @__PURE__ */ jsx36(
5241
+ /* @__PURE__ */ jsx37(
5148
5242
  "div",
5149
5243
  {
5150
5244
  ref: scrollRef,
5151
5245
  "data-testid": testId,
5152
5246
  className: "overflow-y-auto",
5153
5247
  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,
5248
+ children: /* @__PURE__ */ jsxs22(
5249
+ "table",
5250
+ {
5251
+ className: cn(
5252
+ "border-collapse text-sm",
5253
+ fixedTableWidth === void 0 && "w-full",
5254
+ enableColumnResizing && "table-fixed"
5255
+ ),
5256
+ style: fixedTableWidth === void 0 ? void 0 : { width: fixedTableWidth },
5257
+ children: [
5258
+ /* @__PURE__ */ jsx37("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs22("tr", { className: "divide-x divide-border", children: [
5259
+ showSelectionColumn && /* @__PURE__ */ jsx37(
5260
+ "th",
5163
5261
  {
5164
- "aria-label": "Select all visible rows",
5165
- checked: allVisibleSelected,
5166
- onCheckedChange: toggleAllVisibleSelected
5262
+ className: "sticky top-0 z-20 border-b border-border bg-muted p-1",
5263
+ style: { width: SELECTION_COLUMN_WIDTH },
5264
+ children: /* @__PURE__ */ jsx37(
5265
+ Checkbox,
5266
+ {
5267
+ "aria-label": "Select all visible rows",
5268
+ checked: allVisibleSelected,
5269
+ onCheckedChange: toggleAllVisibleSelected
5270
+ }
5271
+ )
5167
5272
  }
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
5273
  ),
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",
5274
+ visibleColumns.map((column) => /* @__PURE__ */ jsxs22(
5275
+ "th",
5276
+ {
5277
+ style: columnWidthStyle(column),
5278
+ className: cn(
5279
+ "sticky top-0 z-20 border-b border-border bg-muted p-2 font-medium",
5280
+ enableColumnResizing && "overflow-hidden",
5281
+ alignClassName(column)
5282
+ ),
5283
+ children: [
5284
+ column.header,
5285
+ enableColumnResizing && /* @__PURE__ */ jsx37(
5286
+ ColumnResizeHandle,
5287
+ {
5288
+ columnId: column.id,
5289
+ label: String(column.header),
5290
+ width: columnSizing[column.id] ?? column.width,
5291
+ onResize: setColumnSize
5292
+ }
5293
+ )
5294
+ ]
5295
+ },
5296
+ column.id
5297
+ )),
5298
+ showRowActionsColumn && /* @__PURE__ */ jsx37("th", { className: "sticky top-0 z-20 border-b border-border bg-muted p-2", "aria-label": "Row actions" })
5299
+ ] }) }),
5300
+ /* @__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) => {
5301
+ const groupExpanded = isGroupExpanded(bucket.key);
5302
+ return /* @__PURE__ */ jsxs22(Fragment5, { children: [
5303
+ /* @__PURE__ */ jsx37("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx37(
5304
+ "td",
5199
5305
  {
5200
- type: "button",
5201
- className: "flex w-full items-center gap-1 text-left",
5202
- onClick: () => toggleGroupExpanded(bucket.key),
5203
- "aria-expanded": groupExpanded,
5306
+ colSpan: totalColumnCount,
5307
+ className: cn(
5308
+ "sticky z-20 border-b p-2 font-medium",
5309
+ zebra ? "bg-muted" : "bg-background"
5310
+ ),
5311
+ style: { top: groupHeaderTop },
5312
+ children: /* @__PURE__ */ jsxs22(
5313
+ "button",
5314
+ {
5315
+ type: "button",
5316
+ className: "flex w-full items-center gap-1 text-left",
5317
+ onClick: () => toggleGroupExpanded(bucket.key),
5318
+ "aria-expanded": groupExpanded,
5319
+ children: [
5320
+ /* @__PURE__ */ jsx37(
5321
+ ChevronRightIcon3,
5322
+ {
5323
+ className: `h-4 w-4 shrink-0 transition-transform${groupExpanded ? " rotate-90" : ""}`,
5324
+ "aria-hidden": true
5325
+ }
5326
+ ),
5327
+ renderGroupHeader ? renderGroupHeader(bucket.key, bucket.roots, groupExpanded) : `${bucket.key} (${bucket.roots.length})`
5328
+ ]
5329
+ }
5330
+ )
5331
+ }
5332
+ ) }),
5333
+ hasColumnSummary && /* @__PURE__ */ jsxs22(
5334
+ "tr",
5335
+ {
5336
+ "data-testid": `group-summary-${bucket.key}`,
5337
+ className: "border-b border-border divide-x divide-border",
5204
5338
  children: [
5205
- /* @__PURE__ */ jsx36(
5206
- ChevronRightIcon3,
5339
+ showSelectionColumn && /* @__PURE__ */ jsx37(
5340
+ "td",
5207
5341
  {
5208
- className: `h-4 w-4 shrink-0 transition-transform${groupExpanded ? " rotate-90" : ""}`,
5209
- "aria-hidden": true
5342
+ className: cn("p-2", zebra ? "bg-muted" : "bg-background"),
5343
+ style: { width: SELECTION_COLUMN_WIDTH }
5210
5344
  }
5211
5345
  ),
5212
- renderGroupHeader ? renderGroupHeader(bucket.key, bucket.roots, groupExpanded) : `${bucket.key} (${bucket.roots.length})`
5346
+ renderGroupSummaryCells(bucket.roots),
5347
+ showRowActionsColumn && /* @__PURE__ */ jsx37("td", { className: cn("p-2", zebra ? "bg-muted" : "bg-background"), "aria-hidden": true })
5213
5348
  ]
5214
5349
  }
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)
5350
+ ),
5351
+ groupExpanded && (() => {
5352
+ const startIndex = groupedRender?.startIndexByBucket.get(bucket.key) ?? 0;
5353
+ return bucket.flatRows.map((flatRow, i) => renderRow(flatRow, startIndex + i));
5354
+ })()
5355
+ ] }, `group-${bucket.key}`);
5356
+ }) : shouldVirtualize ? /* @__PURE__ */ jsxs22(Fragment6, { children: [
5357
+ paddingTop > 0 && /* @__PURE__ */ jsx37("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx37("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
5358
+ virtualItems.map(
5359
+ (virtualItem) => renderRow(flatRows[virtualItem.index], virtualItem.index, (el) => virtualizer.measureElement(el))
5276
5360
  ),
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
- ] })
5361
+ paddingBottom > 0 && /* @__PURE__ */ jsx37("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx37(
5362
+ "td",
5363
+ {
5364
+ colSpan: totalColumnCount,
5365
+ style: { padding: 0, border: "none", height: paddingBottom }
5366
+ }
5367
+ ) })
5368
+ ] }) : flatRows.map((flatRow, index) => renderRow(flatRow, index)) }),
5369
+ showTotals && hasColumnSummary && // Sticky to the BOTTOM of the scroll container, mirroring the
5370
+ // `<thead>`'s own `top-0` stickiness — see `<DataGrid>`'s own
5371
+ // `renderTotalsFooter` for the full reasoning. Summarizes `data`
5372
+ // (the root-level rows), not `flatRows`/`allFlatRows` — same
5373
+ // "roots only" scope `groupBy` itself uses, and for the same
5374
+ // reason (a child's value is usually already folded into its
5375
+ // parent's).
5376
+ /* @__PURE__ */ jsx37("tfoot", { children: /* @__PURE__ */ jsxs22("tr", { "data-testid": "totals-row", className: "divide-x divide-border", children: [
5377
+ showSelectionColumn && /* @__PURE__ */ jsx37(
5378
+ "td",
5379
+ {
5380
+ className: "sticky bottom-0 z-20 border-t border-border bg-muted p-2",
5381
+ style: { width: SELECTION_COLUMN_WIDTH }
5382
+ }
5383
+ ),
5384
+ visibleColumns.map((column) => /* @__PURE__ */ jsx37(
5385
+ "td",
5386
+ {
5387
+ style: columnWidthStyle(column),
5388
+ className: cn(
5389
+ "sticky bottom-0 z-20 border-t border-border bg-muted p-2 font-medium",
5390
+ enableColumnResizing && "overflow-hidden",
5391
+ alignClassName(column)
5392
+ ),
5393
+ children: column.summary?.(data)
5394
+ },
5395
+ column.id
5396
+ )),
5397
+ showRowActionsColumn && /* @__PURE__ */ jsx37("td", { className: "sticky bottom-0 z-20 border-t border-border bg-muted p-2", "aria-hidden": true })
5398
+ ] }) })
5399
+ ]
5400
+ }
5401
+ )
5284
5402
  }
5285
5403
  )
5286
5404
  ] });