@ornery/ui-grid-react 0.1.8 → 0.1.10

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/demo/main.tsx CHANGED
@@ -45,6 +45,7 @@ function App() {
45
45
  enableFiltering: true,
46
46
  enableGrouping: true,
47
47
  enableColumnMoving: true,
48
+ enableColumnResizing: true,
48
49
  enableVirtualization: true,
49
50
  enableCellEditOnFocus: true,
50
51
  virtualizationThreshold: 25,
@@ -121,7 +122,8 @@ function App() {
121
122
  <h2>{options.title ?? 'UI Grid'}</h2>
122
123
  <p>
123
124
  Familiar <code>gridOptions</code> and <code>onRegisterApi</code>, rebuilt with React
124
- hooks, virtualization, grouping, sorting, filtering, and column ordering.
125
+ hooks, virtualization, grouping, sorting, filtering, column ordering, and Excel-style
126
+ column resizing with drag handles plus double-click auto fit.
125
127
  </p>
126
128
  </div>
127
129
 
@@ -169,7 +171,7 @@ function App() {
169
171
  </div>
170
172
  <p>
171
173
  <code>gridOptions</code> compatibility layer: sorting, filtering, grouping, column moving,
172
- templating, and virtualized rendering.
174
+ column resizing, templating, and virtualized rendering.
173
175
  </p>
174
176
  </div>
175
177
 
@@ -1 +1 @@
1
- {"version":3,"file":"UiGrid.d.ts","sourceRoot":"","sources":["../src/UiGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EACV,WAAW,EACX,uBAAuB,EACvB,6BAA6B,EAC7B,yBAAyB,EACzB,SAAS,EAGV,MAAM,sBAAsB,CAAC;AAK9B,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,KAAK,CAAC,SAAS,CAAC;IACrE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,KAAK,CAAC,SAAS,CAAC;IACzE,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,6BAA6B,KAAK,KAAK,CAAC,SAAS,CAAC;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,aAAa,EACb,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,SAAS,GACV,EAAE,WAAW,2CA+rBb"}
1
+ {"version":3,"file":"UiGrid.d.ts","sourceRoot":"","sources":["../src/UiGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EACV,WAAW,EACX,uBAAuB,EACvB,6BAA6B,EAC7B,yBAAyB,EACzB,SAAS,EAGV,MAAM,sBAAsB,CAAC;AAK9B,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,KAAK,CAAC,SAAS,CAAC;IACrE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,KAAK,CAAC,SAAS,CAAC;IACzE,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,6BAA6B,KAAK,KAAK,CAAC,SAAS,CAAC;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,aAAa,EACb,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,SAAS,GACV,EAAE,WAAW,2CA+sBb"}
package/dist/index.js CHANGED
@@ -136,6 +136,7 @@ function useGridState(options, onRegisterApi) {
136
136
  });
137
137
  const [autoViewportHeight, setAutoViewportHeight] = (0, import_react.useState)(null);
138
138
  const [pinnedColumns, setPinnedColumns] = (0, import_react.useState)({});
139
+ const [columnWidthOverrides, setColumnWidthOverrides] = (0, import_react.useState)({});
139
140
  const gridContainerRef = (0, import_react.useRef)(null);
140
141
  const initializedGridIdRef = (0, import_react.useRef)(null);
141
142
  const lastCanvasHeightRef = (0, import_react.useRef)(0);
@@ -173,6 +174,14 @@ function useGridState(options, onRegisterApi) {
173
174
  currentPageRef.current = currentPage;
174
175
  const pageSizeRef = (0, import_react.useRef)(pageSize);
175
176
  pageSizeRef.current = pageSize;
177
+ const setEditingCellState = (0, import_react.useCallback)((nextEditingCell) => {
178
+ editingCellRef.current = nextEditingCell;
179
+ setEditingCell(nextEditingCell);
180
+ }, []);
181
+ const setEditingValueState = (0, import_react.useCallback)((nextEditingValue) => {
182
+ editingValueRef.current = nextEditingValue;
183
+ setEditingValue(nextEditingValue);
184
+ }, []);
176
185
  const infiniteScrollStateRef = (0, import_react.useRef)(infiniteScrollState);
177
186
  infiniteScrollStateRef.current = infiniteScrollState;
178
187
  const optionsRef = (0, import_react.useRef)(options);
@@ -180,9 +189,13 @@ function useGridState(options, onRegisterApi) {
180
189
  const rowSize = options.rowHeight ?? 44;
181
190
  const visibleColumns = (0, import_react.useMemo)(() => {
182
191
  const orderedColumns = orderVisibleColumns(options.columnDefs, columnOrder);
192
+ const applyWidthOverrides = (columns) => columns.map((col) => {
193
+ const override = columnWidthOverrides[col.name];
194
+ return override == null ? col : { ...col, width: override };
195
+ });
183
196
  const pinnedEntries = Object.entries(pinnedColumns);
184
197
  if (pinnedEntries.length === 0) {
185
- return orderedColumns;
198
+ return applyWidthOverrides(orderedColumns);
186
199
  }
187
200
  const columnByName = new Map(orderedColumns.map((column) => [column.name, column]));
188
201
  const pinnedLeft = pinnedEntries.filter(([, direction]) => direction === "left").map(([columnName]) => columnByName.get(columnName)).filter((column) => column !== void 0);
@@ -190,8 +203,8 @@ function useGridState(options, onRegisterApi) {
190
203
  const centerColumns = orderedColumns.filter(
191
204
  (column) => pinnedColumns[column.name] === void 0
192
205
  );
193
- return [...pinnedLeft, ...centerColumns, ...pinnedRight];
194
- }, [options.columnDefs, columnOrder, pinnedColumns]);
206
+ return applyWidthOverrides([...pinnedLeft, ...centerColumns, ...pinnedRight]);
207
+ }, [options.columnDefs, columnOrder, pinnedColumns, columnWidthOverrides]);
195
208
  const visibleColumnsRef = (0, import_react.useRef)(visibleColumns);
196
209
  visibleColumnsRef.current = visibleColumns;
197
210
  const pipeline = (0, import_react.useMemo)(() => {
@@ -328,11 +341,12 @@ function useGridState(options, onRegisterApi) {
328
341
  if (retry) requestAnimationFrame(() => doFocus(false));
329
342
  return;
330
343
  }
331
- target.focus();
344
+ target.focus({ preventScroll: true });
332
345
  if (retry && container.ownerDocument.activeElement !== target) {
333
346
  requestAnimationFrame(() => doFocus(false));
334
347
  }
335
348
  };
349
+ doFocus(true);
336
350
  queueMicrotask(() => doFocus(true));
337
351
  }, []);
338
352
  const focusEditorInput = (0, import_react.useCallback)((focusToken) => {
@@ -660,8 +674,8 @@ function useGridState(options, onRegisterApi) {
660
674
  gridApiRef.current,
661
675
  {
662
676
  setFocusedCell: (fc) => setFocusedCell(fc),
663
- setEditingCell: (ec2) => setEditingCell(ec2),
664
- setEditingValue: (ev) => setEditingValue(ev)
677
+ setEditingCell: setEditingCellState,
678
+ setEditingValue: setEditingValueState
665
679
  },
666
680
  row,
667
681
  column,
@@ -673,15 +687,15 @@ function useGridState(options, onRegisterApi) {
673
687
  queueMicrotask(() => focusEditorInput(focusToken));
674
688
  }
675
689
  },
676
- [focusEditorInput]
690
+ [focusEditorInput, setEditingCellState, setEditingValueState]
677
691
  );
678
692
  const commitCellEditFn = (0, import_react.useCallback)(
679
693
  (direction, restoreFocus = true) => {
680
694
  const result = (0, import_ui_grid_core2.commitGridCellEditCommand)(gridApiRef.current, {
681
695
  getEditingCell: () => editingCellRef.current,
682
696
  getEditingValue: () => editingValueRef.current,
683
- setEditingCell: (ec) => setEditingCell(ec),
684
- setEditingValue: (ev) => setEditingValue(ev),
697
+ setEditingCell: setEditingCellState,
698
+ setEditingValue: setEditingValueState,
685
699
  findRowById: (rowId) => (0, import_ui_grid_core2.findGridRowById)(buildRowsFromData(optionsRef.current.data), rowId),
686
700
  findColumnByName: (columnName) => visibleColumnsRef.current.find((c) => c.name === columnName),
687
701
  parseEditedValue: (column, value, oldValue) => (0, import_ui_grid_core2.parseGridEditedValue)(column, value, oldValue),
@@ -699,25 +713,25 @@ function useGridState(options, onRegisterApi) {
699
713
  focusRenderedCell(result.focusTarget);
700
714
  }
701
715
  },
702
- [buildRowsFromData, focusRenderedCell]
716
+ [buildRowsFromData, focusRenderedCell, setEditingCellState, setEditingValueState]
703
717
  );
704
718
  const cancelCellEditFn = (0, import_react.useCallback)(() => {
705
719
  const hadEditingCell = editingCellRef.current !== null;
706
720
  const result = (0, import_ui_grid_core2.cancelGridCellEditCommand)(gridApiRef.current, {
707
721
  getEditingCell: () => editingCellRef.current,
708
- setEditingCell: (ec) => setEditingCell(ec),
709
- setEditingValue: (ev) => setEditingValue(ev),
722
+ setEditingCell: setEditingCellState,
723
+ setEditingValue: setEditingValueState,
710
724
  findRowById: (rowId) => (0, import_ui_grid_core2.findGridRowById)(buildRowsFromData(optionsRef.current.data), rowId),
711
725
  findColumnByName: (columnName) => visibleColumnsRef.current.find((c) => c.name === columnName)
712
726
  });
713
727
  if (!hadEditingCell) return;
714
728
  editorFocusTokenRef.current += 1;
715
729
  if (result.focusTarget) focusRenderedCell(result.focusTarget);
716
- }, [buildRowsFromData, focusRenderedCell]);
730
+ }, [buildRowsFromData, focusRenderedCell, setEditingCellState, setEditingValueState]);
717
731
  const moveFocusFn = (0, import_react.useCallback)(
718
732
  (row, column, direction, triggerEvent) => {
719
733
  const nextCell = (0, import_ui_grid_core2.findNextGridCell)({
720
- rows: pipelineRef.current.visibleRows,
734
+ rows: pipelineRef.current.displayItems.filter((item) => item.kind === "row").map((item) => item.row),
721
735
  columns: visibleColumnsRef.current,
722
736
  rowId: row.id,
723
737
  columnName: column.name,
@@ -794,8 +808,8 @@ function useGridState(options, onRegisterApi) {
794
808
  setHiddenRowReasons({});
795
809
  setCollapsedGroups({});
796
810
  setFocusedCell(null);
797
- setEditingCell(null);
798
- setEditingValue("");
811
+ setEditingCellState(null);
812
+ setEditingValueState("");
799
813
  setExpandedRows({});
800
814
  setExpandedTreeRows({});
801
815
  setColumnOrder(options.columnDefs.map((column) => column.name));
@@ -829,7 +843,7 @@ function useGridState(options, onRegisterApi) {
829
843
  }
830
844
  }, [pipeline, gridApi, rowSize]);
831
845
  (0, import_react.useEffect)(() => {
832
- if (!import_ui_grid_core2.FEATURE_AUTO_RESIZE || !options.enableAutoResize) return;
846
+ if (!import_ui_grid_core2.FEATURE_AUTO_RESIZE) return;
833
847
  const container = gridContainerRef.current;
834
848
  if (!container) return;
835
849
  const observer = (0, import_ui_grid_core2.observeGridHostSize)(container, ({ height: nextHeight, width: nextWidth }) => {
@@ -931,6 +945,9 @@ function useGridState(options, onRegisterApi) {
931
945
  const isFocusedCellFn = (0, import_react.useCallback)((row, column) => {
932
946
  return (0, import_ui_grid_core2.isGridCellPosition)(focusedCellRef.current, row.id, column.name);
933
947
  }, []);
948
+ const isFocusedRowFn = (0, import_react.useCallback)((row) => {
949
+ return focusedCellRef.current?.rowId === row.id || editingCellRef.current?.rowId === row.id;
950
+ }, []);
934
951
  const isEditingCellFn = (0, import_react.useCallback)((row, column) => {
935
952
  return (0, import_ui_grid_core2.isGridCellPosition)(editingCellRef.current, row.id, column.name);
936
953
  }, []);
@@ -1054,34 +1071,45 @@ function useGridState(options, onRegisterApi) {
1054
1071
  );
1055
1072
  const handleCellKeyDownFn = (0, import_react.useCallback)(
1056
1073
  (row, column, event) => {
1057
- focusCellFn(row, column, event.nativeEvent);
1074
+ if ((0, import_ui_grid_core2.isGridNavigationKey)(event.key)) {
1075
+ setFocusedCell({ rowId: row.id, columnName: column.name });
1076
+ } else {
1077
+ focusCellFn(row, column, event.nativeEvent);
1078
+ }
1058
1079
  switch (event.key) {
1059
1080
  case "ArrowLeft":
1060
1081
  event.preventDefault();
1082
+ event.stopPropagation();
1061
1083
  moveFocusFn(row, column, "left", event.nativeEvent);
1062
1084
  return;
1063
1085
  case "ArrowRight":
1064
1086
  event.preventDefault();
1087
+ event.stopPropagation();
1065
1088
  moveFocusFn(row, column, "right", event.nativeEvent);
1066
1089
  return;
1067
1090
  case "ArrowUp":
1068
1091
  event.preventDefault();
1092
+ event.stopPropagation();
1069
1093
  moveFocusFn(row, column, "up", event.nativeEvent);
1070
1094
  return;
1071
1095
  case "ArrowDown":
1072
1096
  event.preventDefault();
1097
+ event.stopPropagation();
1073
1098
  moveFocusFn(row, column, "down", event.nativeEvent);
1074
1099
  return;
1075
1100
  case "Tab":
1076
1101
  event.preventDefault();
1102
+ event.stopPropagation();
1077
1103
  moveFocusFn(row, column, event.shiftKey ? "left" : "right", event.nativeEvent);
1078
1104
  return;
1079
1105
  case "Enter":
1080
1106
  event.preventDefault();
1107
+ event.stopPropagation();
1081
1108
  moveFocusFn(row, column, event.shiftKey ? "up" : "down", event.nativeEvent);
1082
1109
  return;
1083
1110
  case "F2":
1084
1111
  event.preventDefault();
1112
+ event.stopPropagation();
1085
1113
  if (isCellEditable(row, column, event.nativeEvent)) {
1086
1114
  startCellEditFn(row, column, event.nativeEvent);
1087
1115
  }
@@ -1090,6 +1118,7 @@ function useGridState(options, onRegisterApi) {
1090
1118
  case "Delete":
1091
1119
  if (isCellEditable(row, column, event.nativeEvent)) {
1092
1120
  event.preventDefault();
1121
+ event.stopPropagation();
1093
1122
  startCellEditFn(row, column, event.nativeEvent, "");
1094
1123
  }
1095
1124
  return;
@@ -1098,6 +1127,7 @@ function useGridState(options, onRegisterApi) {
1098
1127
  }
1099
1128
  if ((0, import_ui_grid_core2.isPrintableGridKey)(event.key, event.ctrlKey, event.metaKey, event.altKey) && isCellEditable(row, column, event.nativeEvent)) {
1100
1129
  event.preventDefault();
1130
+ event.stopPropagation();
1101
1131
  startCellEditFn(row, column, event.nativeEvent, event.key);
1102
1132
  }
1103
1133
  },
@@ -1113,23 +1143,32 @@ function useGridState(options, onRegisterApi) {
1113
1143
  [focusCellFn, isCellEditable, startCellEditFn]
1114
1144
  );
1115
1145
  const updateEditingValueFn = (0, import_react.useCallback)((value) => {
1116
- setEditingValue(value);
1117
- }, []);
1146
+ setEditingValueState(value);
1147
+ }, [setEditingValueState]);
1118
1148
  const handleEditorKeyDownFn = (0, import_react.useCallback)(
1119
1149
  (event) => {
1120
1150
  if (event.key === "Escape") {
1121
1151
  event.preventDefault();
1152
+ event.stopPropagation();
1122
1153
  cancelCellEditFn();
1123
1154
  return;
1124
1155
  }
1125
1156
  if (event.key === "Enter") {
1126
1157
  event.preventDefault();
1158
+ event.stopPropagation();
1127
1159
  commitCellEditFn(event.shiftKey ? "up" : "down");
1128
1160
  return;
1129
1161
  }
1130
1162
  if (event.key === "Tab") {
1131
1163
  event.preventDefault();
1164
+ event.stopPropagation();
1132
1165
  commitCellEditFn(event.shiftKey ? "left" : "right");
1166
+ return;
1167
+ }
1168
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
1169
+ event.preventDefault();
1170
+ event.stopPropagation();
1171
+ commitCellEditFn(event.key === "ArrowUp" ? "up" : "down");
1133
1172
  }
1134
1173
  },
1135
1174
  [cancelCellEditFn, commitCellEditFn]
@@ -1191,6 +1230,72 @@ function useGridState(options, onRegisterApi) {
1191
1230
  },
1192
1231
  [setPaginationPageSizeFn]
1193
1232
  );
1233
+ const canResizeColumnsFn = (0, import_react.useCallback)(() => {
1234
+ return optionsRef.current.enableColumnResizing !== false;
1235
+ }, []);
1236
+ const setColumnWidthOverrideFn = (0, import_react.useCallback)((columnName, widthPx) => {
1237
+ const nextWidth = `${Math.max(88, Math.round(widthPx))}px`;
1238
+ setColumnWidthOverrides((current) => ({ ...current, [columnName]: nextWidth }));
1239
+ }, []);
1240
+ const measureAutoColumnWidthFn = (0, import_react.useCallback)((columnName) => {
1241
+ const container = gridContainerRef.current;
1242
+ if (container == null) return 176;
1243
+ const escaped = CSS.escape ? CSS.escape(columnName) : columnName.replace(/([\\".#:[\](){}+~> ])/g, "\\$1");
1244
+ const selectors = [
1245
+ `.header-cell[data-col-name="${escaped}"]`,
1246
+ `.filter-cell[data-col-name="${escaped}"]`,
1247
+ `.body-cell[data-col-name="${escaped}"] .cell-shell`
1248
+ ];
1249
+ let maxWidth = 0;
1250
+ for (const selector of selectors) {
1251
+ const elements = container.querySelectorAll(selector);
1252
+ for (const element of elements) {
1253
+ maxWidth = Math.max(maxWidth, element.scrollWidth);
1254
+ }
1255
+ }
1256
+ return maxWidth + 12;
1257
+ }, []);
1258
+ const handleHeaderResizeMouseDownFn = (0, import_react.useCallback)(
1259
+ (column, event) => {
1260
+ if (!canResizeColumnsFn()) return;
1261
+ event.preventDefault();
1262
+ event.stopPropagation();
1263
+ const headerCell = event.currentTarget.closest(".header-cell");
1264
+ if (headerCell == null) return;
1265
+ const startX = event.clientX;
1266
+ const startWidth = headerCell.getBoundingClientRect().width;
1267
+ let lastWidth = startWidth;
1268
+ const handleMove = (moveEvent) => {
1269
+ lastWidth = Math.max(88, startWidth + (moveEvent.clientX - startX));
1270
+ const widthStr = `${Math.round(lastWidth)}px`;
1271
+ const newTemplate = buildGridTemplateColumns(
1272
+ visibleColumnsRef.current.map(
1273
+ (c) => c.name === column.name ? { ...c, width: widthStr } : c
1274
+ )
1275
+ );
1276
+ gridContainerRef.current?.querySelectorAll(".header-grid, .filter-grid, .body-grid").forEach((el) => {
1277
+ el.style.gridTemplateColumns = newTemplate;
1278
+ });
1279
+ };
1280
+ const handleUp = () => {
1281
+ window.removeEventListener("mousemove", handleMove);
1282
+ window.removeEventListener("mouseup", handleUp);
1283
+ setColumnWidthOverrideFn(column.name, lastWidth);
1284
+ };
1285
+ window.addEventListener("mousemove", handleMove);
1286
+ window.addEventListener("mouseup", handleUp);
1287
+ },
1288
+ [canResizeColumnsFn, setColumnWidthOverrideFn]
1289
+ );
1290
+ const autoSizeColumnFn = (0, import_react.useCallback)(
1291
+ (column, event) => {
1292
+ if (!canResizeColumnsFn()) return;
1293
+ event.preventDefault();
1294
+ event.stopPropagation();
1295
+ setColumnWidthOverrideFn(column.name, measureAutoColumnWidthFn(column.name));
1296
+ },
1297
+ [canResizeColumnsFn, setColumnWidthOverrideFn, measureAutoColumnWidthFn]
1298
+ );
1194
1299
  const onViewportScrollFn = (0, import_react.useCallback)((startIndex) => {
1195
1300
  if (!scrollingRef.current) {
1196
1301
  scrollingRef.current = true;
@@ -1248,6 +1353,7 @@ function useGridState(options, onRegisterApi) {
1248
1353
  paginationSelectedPageSize,
1249
1354
  rowSize,
1250
1355
  viewportHeightPx,
1356
+ autoViewportHeight,
1251
1357
  headerLabel: headerLabelFn,
1252
1358
  isGroupItem: isGroupItemFn,
1253
1359
  isExpandableItem: isExpandableItemFn,
@@ -1263,6 +1369,7 @@ function useGridState(options, onRegisterApi) {
1263
1369
  groupDisclosureLabel: groupDisclosureLabelFn,
1264
1370
  displayValue: displayValueFn,
1265
1371
  isFocusedCell: isFocusedCellFn,
1372
+ isFocusedRow: isFocusedRowFn,
1266
1373
  isEditingCell: isEditingCellFn,
1267
1374
  editorInputType: editorInputTypeFn,
1268
1375
  cellContext: cellContextFn,
@@ -1309,6 +1416,9 @@ function useGridState(options, onRegisterApi) {
1309
1416
  toggleTreeRow: toggleTreeRowFn,
1310
1417
  moveColumn: moveColumnFn,
1311
1418
  moveVisibleColumn: moveVisibleColumnFn,
1419
+ canResizeColumns: canResizeColumnsFn,
1420
+ handleHeaderResizeMouseDown: handleHeaderResizeMouseDownFn,
1421
+ autoSizeColumn: autoSizeColumnFn,
1312
1422
  nextPage: nextPageFn,
1313
1423
  previousPage: previousPageFn,
1314
1424
  onPageSizeChange: onPageSizeChangeFn,
@@ -1396,6 +1506,7 @@ function UiGrid({
1396
1506
  virtualizationEnabled,
1397
1507
  rowSize,
1398
1508
  editingValue,
1509
+ autoViewportHeight,
1399
1510
  sortingFeature,
1400
1511
  filteringFeature,
1401
1512
  groupingFeature,
@@ -1413,10 +1524,8 @@ function UiGrid({
1413
1524
  const [headerStickyHeight, setHeaderStickyHeight] = import_react3.default.useState(0);
1414
1525
  const [filterStickyHeight, setFilterStickyHeight] = import_react3.default.useState(0);
1415
1526
  const stickyChromeHeight = headerStickyHeight + filterStickyHeight;
1416
- const bodyViewportHeight = Math.max(
1417
- rowSize,
1418
- (options.viewportHeight ?? 560) - stickyChromeHeight
1419
- );
1527
+ const resolvedViewportHeight = options.viewportHeight ?? (autoViewportHeight && autoViewportHeight > 0 ? autoViewportHeight : 560);
1528
+ const bodyViewportHeight = Math.max(rowSize, resolvedViewportHeight - stickyChromeHeight);
1420
1529
  const virtualScroll = useVirtualScroll({
1421
1530
  itemCount: displayItems.length,
1422
1531
  itemSize: rowSize,
@@ -1426,7 +1535,7 @@ function UiGrid({
1426
1535
  const [openPinMenuColumn, setOpenPinMenuColumn] = import_react3.default.useState(null);
1427
1536
  const [draggedColumnName, setDraggedColumnName] = import_react3.default.useState(null);
1428
1537
  const [dropTargetColumnName, setDropTargetColumnName] = import_react3.default.useState(null);
1429
- const scrollContainerHeight = `${options.viewportHeight ?? 560}px`;
1538
+ const scrollContainerHeight = `${resolvedViewportHeight}px`;
1430
1539
  function renderHeaderContent(column) {
1431
1540
  const value = state.headerLabel(column);
1432
1541
  const context = {
@@ -1732,6 +1841,7 @@ function UiGrid({
1732
1841
  if (column.align === "center") classes.push("align-center");
1733
1842
  if (column.align === "end") classes.push("align-end");
1734
1843
  if (state.isFocusedCell(item.row, column)) classes.push("cell-focused");
1844
+ if (state.isFocusedRow(item.row)) classes.push("row-focused");
1735
1845
  if (cellEditFeature && state.isEditingCell(item.row, column)) classes.push("cell-editing");
1736
1846
  return classes.join(" ");
1737
1847
  }
@@ -1779,7 +1889,7 @@ function UiGrid({
1779
1889
  {
1780
1890
  className: `header-cell ui-grid-header-cell${sortingFeature && state.sortDirection(column) !== "none" ? " is-active" : ""}${pinned ? " is-pinned" : ""}${pinMenuOpen ? " is-pin-menu-open" : ""}${draggedColumnName === column.name ? " is-dragging" : ""}${dropTargetColumnName === column.name ? " is-drag-target" : ""}`,
1781
1891
  "data-part": "header-cell",
1782
- role: "columnheader",
1892
+ "data-col-name": column.name,
1783
1893
  "aria-sort": sortingFeature ? state.sortAriaSort(column) : void 0,
1784
1894
  draggable: columnMovingFeature,
1785
1895
  onDragStart: (event) => handleHeaderDragStart(column, event),
@@ -1902,7 +2012,19 @@ function UiGrid({
1902
2012
  ]
1903
2013
  }
1904
2014
  )
1905
- ] })
2015
+ ] }),
2016
+ state.canResizeColumns() && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2017
+ "button",
2018
+ {
2019
+ type: "button",
2020
+ className: "column-resizer",
2021
+ "data-col-name": column.name,
2022
+ "aria-label": `Resize ${state.headerLabel(column)} column`,
2023
+ title: "Drag to resize, double-click to auto fit",
2024
+ onMouseDown: (event) => state.handleHeaderResizeMouseDown(column, event),
2025
+ onDoubleClick: (event) => state.autoSizeColumn(column, event)
2026
+ }
2027
+ )
1906
2028
  ]
1907
2029
  },
1908
2030
  column.name