@ornery/ui-grid-react 0.1.9 → 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 +4 -2
- package/dist/UiGrid.d.ts.map +1 -1
- package/dist/index.js +149 -27
- package/dist/index.mjs +150 -27
- package/dist/ui-grid.css +46 -0
- package/dist/useGridState.d.ts +5 -0
- package/dist/useGridState.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/UiGrid.tsx +22 -6
- package/src/ui-grid.css +46 -0
- package/src/useGridState.ts +168 -20
package/src/ui-grid.css
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
.ui-grid-host {
|
|
2
|
+
/* Stretch to fill the parent so the autoresize observer can measure a
|
|
3
|
+
deterministic height. Consumers can override these properties to opt
|
|
4
|
+
back into intrinsic sizing. */
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
min-height: 0;
|
|
8
|
+
height: 100%;
|
|
2
9
|
--_ui-grid-border-color: var(--ui-grid-border-color, var(--app-ui-grid-border-color, #d4d4d8));
|
|
3
10
|
--_ui-grid-header-background: var(
|
|
4
11
|
--ui-grid-header-background,
|
|
@@ -221,6 +228,12 @@
|
|
|
221
228
|
border: 1px solid var(--ui-grid-border-color);
|
|
222
229
|
box-shadow: var(--ui-grid-shadow);
|
|
223
230
|
overflow: hidden;
|
|
231
|
+
/* Allow the frame to grow inside the flexed host so the body grid can fill
|
|
232
|
+
the available height when no explicit viewportHeight is set. */
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
flex: 1 1 auto;
|
|
236
|
+
min-height: 0;
|
|
224
237
|
}
|
|
225
238
|
|
|
226
239
|
.metrics-strip {
|
|
@@ -313,6 +326,7 @@
|
|
|
313
326
|
background: var(--ui-grid-header-background);
|
|
314
327
|
font-weight: var(--ui-grid-header-weight);
|
|
315
328
|
align-items: center;
|
|
329
|
+
position: relative;
|
|
316
330
|
}
|
|
317
331
|
|
|
318
332
|
.header-cell[draggable='true'] {
|
|
@@ -337,6 +351,38 @@
|
|
|
337
351
|
z-index: var(--ui-grid-pin-menu-open-z-index);
|
|
338
352
|
}
|
|
339
353
|
|
|
354
|
+
.column-resizer {
|
|
355
|
+
position: absolute;
|
|
356
|
+
top: 0;
|
|
357
|
+
right: -4px;
|
|
358
|
+
width: 8px;
|
|
359
|
+
height: 100%;
|
|
360
|
+
padding: 0;
|
|
361
|
+
border: 0;
|
|
362
|
+
background: transparent;
|
|
363
|
+
cursor: col-resize;
|
|
364
|
+
z-index: 5;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.column-resizer::after {
|
|
368
|
+
content: '';
|
|
369
|
+
position: absolute;
|
|
370
|
+
top: 20%;
|
|
371
|
+
bottom: 20%;
|
|
372
|
+
left: 50%;
|
|
373
|
+
width: 2px;
|
|
374
|
+
transform: translateX(-50%);
|
|
375
|
+
border-radius: 999px;
|
|
376
|
+
background: color-mix(in srgb, var(--ui-grid-border-color, #888) 80%, transparent);
|
|
377
|
+
opacity: 0;
|
|
378
|
+
transition: opacity 120ms ease;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.header-cell:hover .column-resizer::after,
|
|
382
|
+
.column-resizer:focus-visible::after {
|
|
383
|
+
opacity: 1;
|
|
384
|
+
}
|
|
385
|
+
|
|
340
386
|
.header-label {
|
|
341
387
|
min-width: 0;
|
|
342
388
|
display: block;
|
package/src/useGridState.ts
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
buildGridFocusCellResult,
|
|
57
57
|
findNextGridCell,
|
|
58
58
|
isPrintableGridKey,
|
|
59
|
+
isGridNavigationKey,
|
|
59
60
|
isGridCellPosition,
|
|
60
61
|
exportCsvRows,
|
|
61
62
|
buildGridRows,
|
|
@@ -222,6 +223,7 @@ export interface UseGridStateResult {
|
|
|
222
223
|
paginationSelectedPageSize: number;
|
|
223
224
|
rowSize: number;
|
|
224
225
|
viewportHeightPx: string;
|
|
226
|
+
autoViewportHeight: number | null;
|
|
225
227
|
|
|
226
228
|
// Display helpers
|
|
227
229
|
headerLabel: (column: GridColumnDef) => string;
|
|
@@ -239,6 +241,7 @@ export interface UseGridStateResult {
|
|
|
239
241
|
groupDisclosureLabel: (item: GroupItem) => string;
|
|
240
242
|
displayValue: (row: GridRow, column: GridColumnDef) => string;
|
|
241
243
|
isFocusedCell: (row: GridRow, column: GridColumnDef) => boolean;
|
|
244
|
+
isFocusedRow: (row: GridRow) => boolean;
|
|
242
245
|
isEditingCell: (row: GridRow, column: GridColumnDef) => boolean;
|
|
243
246
|
editorInputType: (column: GridColumnDef) => string;
|
|
244
247
|
cellContext: (row: GridRow, column: GridColumnDef) => GridCellTemplateContext;
|
|
@@ -307,6 +310,9 @@ export interface UseGridStateResult {
|
|
|
307
310
|
toggleTreeRow: (row: GridRow, event?: React.MouseEvent) => void;
|
|
308
311
|
moveColumn: (fromIndex: number, toIndex: number) => void;
|
|
309
312
|
moveVisibleColumn: (columnName: string, targetColumnName: string) => void;
|
|
313
|
+
canResizeColumns: () => boolean;
|
|
314
|
+
handleHeaderResizeMouseDown: (column: GridColumnDef, event: React.MouseEvent) => void;
|
|
315
|
+
autoSizeColumn: (column: GridColumnDef, event: React.MouseEvent) => void;
|
|
310
316
|
nextPage: () => void;
|
|
311
317
|
previousPage: () => void;
|
|
312
318
|
onPageSizeChange: (value: string) => void;
|
|
@@ -344,6 +350,7 @@ export function useGridState(
|
|
|
344
350
|
});
|
|
345
351
|
const [autoViewportHeight, setAutoViewportHeight] = useState<number | null>(null);
|
|
346
352
|
const [pinnedColumns, setPinnedColumns] = useState<PinnedColumnState>({});
|
|
353
|
+
const [columnWidthOverrides, setColumnWidthOverrides] = useState<Record<string, string>>({});
|
|
347
354
|
|
|
348
355
|
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
|
349
356
|
const initializedGridIdRef = useRef<string | null>(null);
|
|
@@ -384,6 +391,16 @@ export function useGridState(
|
|
|
384
391
|
currentPageRef.current = currentPage;
|
|
385
392
|
const pageSizeRef = useRef(pageSize);
|
|
386
393
|
pageSizeRef.current = pageSize;
|
|
394
|
+
|
|
395
|
+
const setEditingCellState = useCallback((nextEditingCell: GridCellPosition | null): void => {
|
|
396
|
+
editingCellRef.current = nextEditingCell;
|
|
397
|
+
setEditingCell(nextEditingCell);
|
|
398
|
+
}, []);
|
|
399
|
+
|
|
400
|
+
const setEditingValueState = useCallback((nextEditingValue: string): void => {
|
|
401
|
+
editingValueRef.current = nextEditingValue;
|
|
402
|
+
setEditingValue(nextEditingValue);
|
|
403
|
+
}, []);
|
|
387
404
|
const infiniteScrollStateRef = useRef(infiniteScrollState);
|
|
388
405
|
infiniteScrollStateRef.current = infiniteScrollState;
|
|
389
406
|
const optionsRef = useRef(options);
|
|
@@ -393,9 +410,15 @@ export function useGridState(
|
|
|
393
410
|
|
|
394
411
|
const visibleColumns = useMemo(() => {
|
|
395
412
|
const orderedColumns = orderVisibleColumns(options.columnDefs, columnOrder);
|
|
413
|
+
const applyWidthOverrides = (columns: GridColumnDef[]): GridColumnDef[] =>
|
|
414
|
+
columns.map((col) => {
|
|
415
|
+
const override = columnWidthOverrides[col.name];
|
|
416
|
+
return override == null ? col : { ...col, width: override };
|
|
417
|
+
});
|
|
418
|
+
|
|
396
419
|
const pinnedEntries = Object.entries(pinnedColumns);
|
|
397
420
|
if (pinnedEntries.length === 0) {
|
|
398
|
-
return orderedColumns;
|
|
421
|
+
return applyWidthOverrides(orderedColumns);
|
|
399
422
|
}
|
|
400
423
|
|
|
401
424
|
const columnByName = new Map(orderedColumns.map((column) => [column.name, column]));
|
|
@@ -411,8 +434,8 @@ export function useGridState(
|
|
|
411
434
|
(column) => pinnedColumns[column.name] === undefined,
|
|
412
435
|
);
|
|
413
436
|
|
|
414
|
-
return [...pinnedLeft, ...centerColumns, ...pinnedRight];
|
|
415
|
-
}, [options.columnDefs, columnOrder, pinnedColumns]);
|
|
437
|
+
return applyWidthOverrides([...pinnedLeft, ...centerColumns, ...pinnedRight]);
|
|
438
|
+
}, [options.columnDefs, columnOrder, pinnedColumns, columnWidthOverrides]);
|
|
416
439
|
|
|
417
440
|
const visibleColumnsRef = useRef(visibleColumns);
|
|
418
441
|
visibleColumnsRef.current = visibleColumns;
|
|
@@ -578,12 +601,15 @@ export function useGridState(
|
|
|
578
601
|
if (retry) requestAnimationFrame(() => doFocus(false));
|
|
579
602
|
return;
|
|
580
603
|
}
|
|
581
|
-
target.focus();
|
|
604
|
+
target.focus({ preventScroll: true });
|
|
582
605
|
if (retry && container.ownerDocument.activeElement !== target) {
|
|
583
606
|
requestAnimationFrame(() => doFocus(false));
|
|
584
607
|
}
|
|
585
608
|
};
|
|
586
609
|
|
|
610
|
+
// Attempt synchronous focus first to avoid the browser scrolling the viewport
|
|
611
|
+
// (e.g. when ArrowDown is pressed) before async focus runs.
|
|
612
|
+
doFocus(true);
|
|
587
613
|
queueMicrotask(() => doFocus(true));
|
|
588
614
|
}, []);
|
|
589
615
|
|
|
@@ -944,8 +970,8 @@ export function useGridState(
|
|
|
944
970
|
gridApiRef.current!,
|
|
945
971
|
{
|
|
946
972
|
setFocusedCell: (fc) => setFocusedCell(fc),
|
|
947
|
-
setEditingCell:
|
|
948
|
-
setEditingValue:
|
|
973
|
+
setEditingCell: setEditingCellState,
|
|
974
|
+
setEditingValue: setEditingValueState,
|
|
949
975
|
},
|
|
950
976
|
row,
|
|
951
977
|
column,
|
|
@@ -958,7 +984,7 @@ export function useGridState(
|
|
|
958
984
|
queueMicrotask(() => focusEditorInput(focusToken));
|
|
959
985
|
}
|
|
960
986
|
},
|
|
961
|
-
[focusEditorInput],
|
|
987
|
+
[focusEditorInput, setEditingCellState, setEditingValueState],
|
|
962
988
|
);
|
|
963
989
|
|
|
964
990
|
const commitCellEditFn = useCallback(
|
|
@@ -966,8 +992,8 @@ export function useGridState(
|
|
|
966
992
|
const result = commitGridCellEditCommand(gridApiRef.current!, {
|
|
967
993
|
getEditingCell: () => editingCellRef.current,
|
|
968
994
|
getEditingValue: () => editingValueRef.current,
|
|
969
|
-
setEditingCell:
|
|
970
|
-
setEditingValue:
|
|
995
|
+
setEditingCell: setEditingCellState,
|
|
996
|
+
setEditingValue: setEditingValueState,
|
|
971
997
|
findRowById: (rowId) =>
|
|
972
998
|
coreFindGridRowById(buildRowsFromData(optionsRef.current.data), rowId),
|
|
973
999
|
findColumnByName: (columnName) =>
|
|
@@ -991,15 +1017,15 @@ export function useGridState(
|
|
|
991
1017
|
focusRenderedCell(result.focusTarget);
|
|
992
1018
|
}
|
|
993
1019
|
},
|
|
994
|
-
[buildRowsFromData, focusRenderedCell],
|
|
1020
|
+
[buildRowsFromData, focusRenderedCell, setEditingCellState, setEditingValueState],
|
|
995
1021
|
);
|
|
996
1022
|
|
|
997
1023
|
const cancelCellEditFn = useCallback((): void => {
|
|
998
1024
|
const hadEditingCell = editingCellRef.current !== null;
|
|
999
1025
|
const result = cancelGridCellEditCommand(gridApiRef.current!, {
|
|
1000
1026
|
getEditingCell: () => editingCellRef.current,
|
|
1001
|
-
setEditingCell:
|
|
1002
|
-
setEditingValue:
|
|
1027
|
+
setEditingCell: setEditingCellState,
|
|
1028
|
+
setEditingValue: setEditingValueState,
|
|
1003
1029
|
findRowById: (rowId) =>
|
|
1004
1030
|
coreFindGridRowById(buildRowsFromData(optionsRef.current.data), rowId),
|
|
1005
1031
|
findColumnByName: (columnName) =>
|
|
@@ -1009,7 +1035,7 @@ export function useGridState(
|
|
|
1009
1035
|
if (!hadEditingCell) return;
|
|
1010
1036
|
editorFocusTokenRef.current += 1;
|
|
1011
1037
|
if (result.focusTarget) focusRenderedCell(result.focusTarget);
|
|
1012
|
-
}, [buildRowsFromData, focusRenderedCell]);
|
|
1038
|
+
}, [buildRowsFromData, focusRenderedCell, setEditingCellState, setEditingValueState]);
|
|
1013
1039
|
|
|
1014
1040
|
const moveFocusFn = useCallback(
|
|
1015
1041
|
(
|
|
@@ -1019,7 +1045,9 @@ export function useGridState(
|
|
|
1019
1045
|
triggerEvent?: Event | KeyboardEvent | null,
|
|
1020
1046
|
): boolean => {
|
|
1021
1047
|
const nextCell = findNextGridCell({
|
|
1022
|
-
rows: pipelineRef.current.
|
|
1048
|
+
rows: pipelineRef.current.displayItems
|
|
1049
|
+
.filter((item) => item.kind === 'row')
|
|
1050
|
+
.map((item) => (item as RowItem).row),
|
|
1023
1051
|
columns: visibleColumnsRef.current,
|
|
1024
1052
|
rowId: row.id,
|
|
1025
1053
|
columnName: column.name,
|
|
@@ -1111,8 +1139,8 @@ export function useGridState(
|
|
|
1111
1139
|
setHiddenRowReasons({});
|
|
1112
1140
|
setCollapsedGroups({});
|
|
1113
1141
|
setFocusedCell(null);
|
|
1114
|
-
|
|
1115
|
-
|
|
1142
|
+
setEditingCellState(null);
|
|
1143
|
+
setEditingValueState('');
|
|
1116
1144
|
setExpandedRows({});
|
|
1117
1145
|
setExpandedTreeRows({});
|
|
1118
1146
|
setColumnOrder(options.columnDefs.map((column) => column.name));
|
|
@@ -1155,8 +1183,11 @@ export function useGridState(
|
|
|
1155
1183
|
|
|
1156
1184
|
// --- Auto resize effect ---
|
|
1157
1185
|
|
|
1186
|
+
// Auto-resize is on by default so the grid fills its container. Setting an
|
|
1187
|
+
// explicit `viewportHeight` opts back into fixed sizing because the observer
|
|
1188
|
+
// only writes `autoViewportHeight` when `viewportHeight` is unset.
|
|
1158
1189
|
useEffect(() => {
|
|
1159
|
-
if (!FEATURE_AUTO_RESIZE
|
|
1190
|
+
if (!FEATURE_AUTO_RESIZE) return;
|
|
1160
1191
|
|
|
1161
1192
|
const container = gridContainerRef.current;
|
|
1162
1193
|
if (!container) return;
|
|
@@ -1283,6 +1314,12 @@ export function useGridState(
|
|
|
1283
1314
|
return isGridCellPosition(focusedCellRef.current, row.id, column.name);
|
|
1284
1315
|
}, []);
|
|
1285
1316
|
|
|
1317
|
+
const isFocusedRowFn = useCallback((row: GridRow): boolean => {
|
|
1318
|
+
return (
|
|
1319
|
+
focusedCellRef.current?.rowId === row.id || editingCellRef.current?.rowId === row.id
|
|
1320
|
+
);
|
|
1321
|
+
}, []);
|
|
1322
|
+
|
|
1286
1323
|
const isEditingCellFn = useCallback((row: GridRow, column: GridColumnDef): boolean => {
|
|
1287
1324
|
return isGridCellPosition(editingCellRef.current, row.id, column.name);
|
|
1288
1325
|
}, []);
|
|
@@ -1445,35 +1482,46 @@ export function useGridState(
|
|
|
1445
1482
|
|
|
1446
1483
|
const handleCellKeyDownFn = useCallback(
|
|
1447
1484
|
(row: GridRow, column: GridColumnDef, event: React.KeyboardEvent): void => {
|
|
1448
|
-
|
|
1485
|
+
if (isGridNavigationKey(event.key)) {
|
|
1486
|
+
setFocusedCell({ rowId: row.id, columnName: column.name });
|
|
1487
|
+
} else {
|
|
1488
|
+
focusCellFn(row, column, event.nativeEvent);
|
|
1489
|
+
}
|
|
1449
1490
|
|
|
1450
1491
|
switch (event.key) {
|
|
1451
1492
|
case 'ArrowLeft':
|
|
1452
1493
|
event.preventDefault();
|
|
1494
|
+
event.stopPropagation();
|
|
1453
1495
|
moveFocusFn(row, column, 'left', event.nativeEvent);
|
|
1454
1496
|
return;
|
|
1455
1497
|
case 'ArrowRight':
|
|
1456
1498
|
event.preventDefault();
|
|
1499
|
+
event.stopPropagation();
|
|
1457
1500
|
moveFocusFn(row, column, 'right', event.nativeEvent);
|
|
1458
1501
|
return;
|
|
1459
1502
|
case 'ArrowUp':
|
|
1460
1503
|
event.preventDefault();
|
|
1504
|
+
event.stopPropagation();
|
|
1461
1505
|
moveFocusFn(row, column, 'up', event.nativeEvent);
|
|
1462
1506
|
return;
|
|
1463
1507
|
case 'ArrowDown':
|
|
1464
1508
|
event.preventDefault();
|
|
1509
|
+
event.stopPropagation();
|
|
1465
1510
|
moveFocusFn(row, column, 'down', event.nativeEvent);
|
|
1466
1511
|
return;
|
|
1467
1512
|
case 'Tab':
|
|
1468
1513
|
event.preventDefault();
|
|
1514
|
+
event.stopPropagation();
|
|
1469
1515
|
moveFocusFn(row, column, event.shiftKey ? 'left' : 'right', event.nativeEvent);
|
|
1470
1516
|
return;
|
|
1471
1517
|
case 'Enter':
|
|
1472
1518
|
event.preventDefault();
|
|
1519
|
+
event.stopPropagation();
|
|
1473
1520
|
moveFocusFn(row, column, event.shiftKey ? 'up' : 'down', event.nativeEvent);
|
|
1474
1521
|
return;
|
|
1475
1522
|
case 'F2':
|
|
1476
1523
|
event.preventDefault();
|
|
1524
|
+
event.stopPropagation();
|
|
1477
1525
|
if (isCellEditable(row, column, event.nativeEvent)) {
|
|
1478
1526
|
startCellEditFn(row, column, event.nativeEvent);
|
|
1479
1527
|
}
|
|
@@ -1482,6 +1530,7 @@ export function useGridState(
|
|
|
1482
1530
|
case 'Delete':
|
|
1483
1531
|
if (isCellEditable(row, column, event.nativeEvent)) {
|
|
1484
1532
|
event.preventDefault();
|
|
1533
|
+
event.stopPropagation();
|
|
1485
1534
|
startCellEditFn(row, column, event.nativeEvent, '');
|
|
1486
1535
|
}
|
|
1487
1536
|
return;
|
|
@@ -1494,6 +1543,7 @@ export function useGridState(
|
|
|
1494
1543
|
isCellEditable(row, column, event.nativeEvent)
|
|
1495
1544
|
) {
|
|
1496
1545
|
event.preventDefault();
|
|
1546
|
+
event.stopPropagation();
|
|
1497
1547
|
startCellEditFn(row, column, event.nativeEvent, event.key);
|
|
1498
1548
|
}
|
|
1499
1549
|
},
|
|
@@ -1511,24 +1561,33 @@ export function useGridState(
|
|
|
1511
1561
|
);
|
|
1512
1562
|
|
|
1513
1563
|
const updateEditingValueFn = useCallback((value: string): void => {
|
|
1514
|
-
|
|
1515
|
-
}, []);
|
|
1564
|
+
setEditingValueState(value);
|
|
1565
|
+
}, [setEditingValueState]);
|
|
1516
1566
|
|
|
1517
1567
|
const handleEditorKeyDownFn = useCallback(
|
|
1518
1568
|
(event: React.KeyboardEvent): void => {
|
|
1519
1569
|
if (event.key === 'Escape') {
|
|
1520
1570
|
event.preventDefault();
|
|
1571
|
+
event.stopPropagation();
|
|
1521
1572
|
cancelCellEditFn();
|
|
1522
1573
|
return;
|
|
1523
1574
|
}
|
|
1524
1575
|
if (event.key === 'Enter') {
|
|
1525
1576
|
event.preventDefault();
|
|
1577
|
+
event.stopPropagation();
|
|
1526
1578
|
commitCellEditFn(event.shiftKey ? 'up' : 'down');
|
|
1527
1579
|
return;
|
|
1528
1580
|
}
|
|
1529
1581
|
if (event.key === 'Tab') {
|
|
1530
1582
|
event.preventDefault();
|
|
1583
|
+
event.stopPropagation();
|
|
1531
1584
|
commitCellEditFn(event.shiftKey ? 'left' : 'right');
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
|
1588
|
+
event.preventDefault();
|
|
1589
|
+
event.stopPropagation();
|
|
1590
|
+
commitCellEditFn(event.key === 'ArrowUp' ? 'up' : 'down');
|
|
1532
1591
|
}
|
|
1533
1592
|
},
|
|
1534
1593
|
[cancelCellEditFn, commitCellEditFn],
|
|
@@ -1599,6 +1658,90 @@ export function useGridState(
|
|
|
1599
1658
|
[setPaginationPageSizeFn],
|
|
1600
1659
|
);
|
|
1601
1660
|
|
|
1661
|
+
// --- Column resizing ---
|
|
1662
|
+
|
|
1663
|
+
const canResizeColumnsFn = useCallback((): boolean => {
|
|
1664
|
+
return optionsRef.current.enableColumnResizing !== false;
|
|
1665
|
+
}, []);
|
|
1666
|
+
|
|
1667
|
+
const setColumnWidthOverrideFn = useCallback((columnName: string, widthPx: number): void => {
|
|
1668
|
+
const nextWidth = `${Math.max(88, Math.round(widthPx))}px`;
|
|
1669
|
+
setColumnWidthOverrides((current) => ({ ...current, [columnName]: nextWidth }));
|
|
1670
|
+
}, []);
|
|
1671
|
+
|
|
1672
|
+
const measureAutoColumnWidthFn = useCallback((columnName: string): number => {
|
|
1673
|
+
const container = gridContainerRef.current;
|
|
1674
|
+
if (container == null) return 176;
|
|
1675
|
+
const escaped = CSS.escape ? CSS.escape(columnName) : columnName.replace(/([\\".#:[\](){}+~> ])/g, '\\$1');
|
|
1676
|
+
const selectors = [
|
|
1677
|
+
`.header-cell[data-col-name="${escaped}"]`,
|
|
1678
|
+
`.filter-cell[data-col-name="${escaped}"]`,
|
|
1679
|
+
`.body-cell[data-col-name="${escaped}"] .cell-shell`,
|
|
1680
|
+
];
|
|
1681
|
+
let maxWidth = 0;
|
|
1682
|
+
for (const selector of selectors) {
|
|
1683
|
+
const elements = container.querySelectorAll<HTMLElement>(selector);
|
|
1684
|
+
for (const element of elements) {
|
|
1685
|
+
maxWidth = Math.max(maxWidth, element.scrollWidth);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
return maxWidth + 12;
|
|
1689
|
+
}, []);
|
|
1690
|
+
|
|
1691
|
+
const handleHeaderResizeMouseDownFn = useCallback(
|
|
1692
|
+
(column: GridColumnDef, event: React.MouseEvent): void => {
|
|
1693
|
+
if (!canResizeColumnsFn()) return;
|
|
1694
|
+
event.preventDefault();
|
|
1695
|
+
event.stopPropagation();
|
|
1696
|
+
|
|
1697
|
+
const headerCell = (event.currentTarget as HTMLElement).closest<HTMLElement>('.header-cell');
|
|
1698
|
+
if (headerCell == null) return;
|
|
1699
|
+
|
|
1700
|
+
const startX = event.clientX;
|
|
1701
|
+
const startWidth = headerCell.getBoundingClientRect().width;
|
|
1702
|
+
let lastWidth = startWidth;
|
|
1703
|
+
|
|
1704
|
+
const handleMove = (moveEvent: MouseEvent): void => {
|
|
1705
|
+
lastWidth = Math.max(88, startWidth + (moveEvent.clientX - startX));
|
|
1706
|
+
|
|
1707
|
+
// Compute the new column template directly — no React state, no re-render.
|
|
1708
|
+
// This keeps virtualized resize smooth since the pipeline never re-runs mid-drag.
|
|
1709
|
+
const widthStr = `${Math.round(lastWidth)}px`;
|
|
1710
|
+
const newTemplate = buildGridTemplateColumns(
|
|
1711
|
+
visibleColumnsRef.current.map((c) =>
|
|
1712
|
+
c.name === column.name ? { ...c, width: widthStr } : c,
|
|
1713
|
+
),
|
|
1714
|
+
);
|
|
1715
|
+
gridContainerRef.current
|
|
1716
|
+
?.querySelectorAll<HTMLElement>('.header-grid, .filter-grid, .body-grid')
|
|
1717
|
+
.forEach((el) => {
|
|
1718
|
+
el.style.gridTemplateColumns = newTemplate;
|
|
1719
|
+
});
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1722
|
+
const handleUp = (): void => {
|
|
1723
|
+
window.removeEventListener('mousemove', handleMove);
|
|
1724
|
+
window.removeEventListener('mouseup', handleUp);
|
|
1725
|
+
// Commit the final width to React state once — triggers one clean re-render.
|
|
1726
|
+
setColumnWidthOverrideFn(column.name, lastWidth);
|
|
1727
|
+
};
|
|
1728
|
+
|
|
1729
|
+
window.addEventListener('mousemove', handleMove);
|
|
1730
|
+
window.addEventListener('mouseup', handleUp);
|
|
1731
|
+
},
|
|
1732
|
+
[canResizeColumnsFn, setColumnWidthOverrideFn],
|
|
1733
|
+
);
|
|
1734
|
+
|
|
1735
|
+
const autoSizeColumnFn = useCallback(
|
|
1736
|
+
(column: GridColumnDef, event: React.MouseEvent): void => {
|
|
1737
|
+
if (!canResizeColumnsFn()) return;
|
|
1738
|
+
event.preventDefault();
|
|
1739
|
+
event.stopPropagation();
|
|
1740
|
+
setColumnWidthOverrideFn(column.name, measureAutoColumnWidthFn(column.name));
|
|
1741
|
+
},
|
|
1742
|
+
[canResizeColumnsFn, setColumnWidthOverrideFn, measureAutoColumnWidthFn],
|
|
1743
|
+
);
|
|
1744
|
+
|
|
1602
1745
|
const onViewportScrollFn = useCallback((startIndex: number): void => {
|
|
1603
1746
|
if (!scrollingRef.current) {
|
|
1604
1747
|
scrollingRef.current = true;
|
|
@@ -1667,6 +1810,7 @@ export function useGridState(
|
|
|
1667
1810
|
paginationSelectedPageSize,
|
|
1668
1811
|
rowSize,
|
|
1669
1812
|
viewportHeightPx,
|
|
1813
|
+
autoViewportHeight,
|
|
1670
1814
|
|
|
1671
1815
|
headerLabel: headerLabelFn,
|
|
1672
1816
|
isGroupItem: isGroupItemFn,
|
|
@@ -1683,6 +1827,7 @@ export function useGridState(
|
|
|
1683
1827
|
groupDisclosureLabel: groupDisclosureLabelFn,
|
|
1684
1828
|
displayValue: displayValueFn,
|
|
1685
1829
|
isFocusedCell: isFocusedCellFn,
|
|
1830
|
+
isFocusedRow: isFocusedRowFn,
|
|
1686
1831
|
isEditingCell: isEditingCellFn,
|
|
1687
1832
|
editorInputType: editorInputTypeFn,
|
|
1688
1833
|
cellContext: cellContextFn,
|
|
@@ -1732,6 +1877,9 @@ export function useGridState(
|
|
|
1732
1877
|
toggleTreeRow: toggleTreeRowFn,
|
|
1733
1878
|
moveColumn: moveColumnFn,
|
|
1734
1879
|
moveVisibleColumn: moveVisibleColumnFn,
|
|
1880
|
+
canResizeColumns: canResizeColumnsFn,
|
|
1881
|
+
handleHeaderResizeMouseDown: handleHeaderResizeMouseDownFn,
|
|
1882
|
+
autoSizeColumn: autoSizeColumnFn,
|
|
1735
1883
|
nextPage: nextPageFn,
|
|
1736
1884
|
previousPage: previousPageFn,
|
|
1737
1885
|
onPageSizeChange: onPageSizeChangeFn,
|