@ornery/ui-grid-react 0.1.4 → 0.1.6
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/UiGrid.d.ts +11 -0
- package/dist/UiGrid.d.ts.map +1 -0
- package/dist/gridStateMath.d.ts +8 -0
- package/dist/gridStateMath.d.ts.map +1 -0
- package/dist/index.d.ts +13 -133
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1248 -1202
- package/dist/index.mjs +1198 -1131
- package/dist/mountUiGrid.d.ts +4 -0
- package/dist/mountUiGrid.d.ts.map +1 -0
- package/dist/rustWasmGridEngine.d.ts +8 -0
- package/dist/rustWasmGridEngine.d.ts.map +1 -0
- package/dist/{index.d.mts → useGridState.d.ts} +14 -37
- package/dist/useGridState.d.ts.map +1 -0
- package/dist/useVirtualScroll.d.ts +20 -0
- package/dist/useVirtualScroll.d.ts.map +1 -0
- package/dist/virtualScrollMath.d.ts +17 -0
- package/dist/virtualScrollMath.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/UiGrid.test.tsx +2 -1
- package/src/UiGrid.tsx +330 -74
- package/src/gridStateMath.test.ts +49 -0
- package/src/gridStateMath.ts +32 -0
- package/src/index.ts +3 -0
- package/src/mountUiGrid.tsx +10 -0
- package/src/rustWasmGridEngine.test.ts +56 -0
- package/src/rustWasmGridEngine.ts +23 -0
- package/src/ui-grid.css +161 -1
- package/src/useGridState.ts +664 -343
- package/src/useVirtualScroll.ts +13 -10
- package/src/virtualScrollMath.test.ts +44 -0
- package/src/virtualScrollMath.ts +36 -0
- package/tsconfig.build.json +6 -0
- package/tsconfig.dts.json +15 -0
- package/CLAUDE.md +0 -283
package/src/useGridState.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
buildGridTemplateColumns,
|
|
4
|
+
computeViewportHeightPx,
|
|
5
|
+
computeViewportRows,
|
|
6
|
+
formatPaginationSummary,
|
|
7
|
+
orderVisibleColumns,
|
|
8
|
+
resolveBenchmarkIterations,
|
|
9
|
+
} from './gridStateMath';
|
|
2
10
|
import {
|
|
3
11
|
createGridApi,
|
|
4
12
|
UiGridApi,
|
|
@@ -16,7 +24,7 @@ import {
|
|
|
16
24
|
getCellValue,
|
|
17
25
|
setPathValue,
|
|
18
26
|
SORT_DIRECTIONS,
|
|
19
|
-
|
|
27
|
+
defaultGridEngine,
|
|
20
28
|
resolveGridLabels,
|
|
21
29
|
gridColumnWidth,
|
|
22
30
|
headerLabel as coreHeaderLabel,
|
|
@@ -79,19 +87,11 @@ import {
|
|
|
79
87
|
FEATURE_CSV_EXPORT,
|
|
80
88
|
FEATURE_AUTO_RESIZE,
|
|
81
89
|
FEATURE_SAVE_STATE,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
RowItem,
|
|
88
|
-
PipelineResult,
|
|
89
|
-
GridInfiniteScrollState,
|
|
90
|
-
GridMoveDirection,
|
|
91
|
-
GridCellTemplateContext,
|
|
92
|
-
GridExpandableTemplateContext,
|
|
93
|
-
} from '@ornery/ui-grid';
|
|
94
|
-
import {
|
|
90
|
+
FEATURE_PINNING,
|
|
91
|
+
buildInitialPinnedState,
|
|
92
|
+
computePinnedOffset,
|
|
93
|
+
isColumnPinnable,
|
|
94
|
+
isPinningEnabled,
|
|
95
95
|
applyGridSortStateCommand,
|
|
96
96
|
updateGridFilterCommand,
|
|
97
97
|
clearGridFiltersCommand,
|
|
@@ -117,8 +117,7 @@ import {
|
|
|
117
117
|
saveGridInfiniteScrollPercentageCommand,
|
|
118
118
|
setGridInfiniteScrollDirectionsCommand,
|
|
119
119
|
restoreGridStateCommand,
|
|
120
|
-
|
|
121
|
-
import {
|
|
120
|
+
pinGridColumnCommand,
|
|
122
121
|
raiseGridRenderingComplete,
|
|
123
122
|
raiseGridRowsRendered,
|
|
124
123
|
raiseGridRowsVisibleChanged,
|
|
@@ -127,11 +126,69 @@ import {
|
|
|
127
126
|
raiseGridScrollBegin,
|
|
128
127
|
raiseGridScrollEnd,
|
|
129
128
|
raiseGridBenchmarkComplete,
|
|
130
|
-
} from '../../ui-grid/src/lib/grid/ui-grid.events';
|
|
131
|
-
import {
|
|
132
129
|
downloadGridCsvFile,
|
|
133
130
|
observeGridHostSize,
|
|
134
|
-
} from '
|
|
131
|
+
} from '@ornery/ui-grid';
|
|
132
|
+
import type {
|
|
133
|
+
DisplayItem,
|
|
134
|
+
GroupItem,
|
|
135
|
+
ExpandableItem,
|
|
136
|
+
RowItem,
|
|
137
|
+
PipelineResult,
|
|
138
|
+
GridInfiniteScrollState,
|
|
139
|
+
GridMoveDirection,
|
|
140
|
+
GridCellTemplateContext,
|
|
141
|
+
GridExpandableTemplateContext,
|
|
142
|
+
PinDirection,
|
|
143
|
+
PinnedColumnState,
|
|
144
|
+
} from '@ornery/ui-grid';
|
|
145
|
+
|
|
146
|
+
function escapeCssSelectorValue(value: string): string {
|
|
147
|
+
const nativeEscape = globalThis.CSS?.escape;
|
|
148
|
+
if (typeof nativeEscape === 'function') {
|
|
149
|
+
return nativeEscape(value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let output = '';
|
|
153
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
154
|
+
const codePoint = value.charCodeAt(index);
|
|
155
|
+
const character = value.charAt(index);
|
|
156
|
+
|
|
157
|
+
if (codePoint === 0x0000) {
|
|
158
|
+
output += '\uFFFD';
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const isControlCharacter = (codePoint >= 0x0001 && codePoint <= 0x001f) || codePoint === 0x007f;
|
|
163
|
+
const startsWithDigit = index === 0 && codePoint >= 0x0030 && codePoint <= 0x0039;
|
|
164
|
+
const secondCharDigitAfterHyphen =
|
|
165
|
+
index === 1 &&
|
|
166
|
+
codePoint >= 0x0030 && codePoint <= 0x0039 &&
|
|
167
|
+
value.charCodeAt(0) === 0x002d;
|
|
168
|
+
|
|
169
|
+
if (isControlCharacter || startsWithDigit || secondCharDigitAfterHyphen) {
|
|
170
|
+
output += `\\${codePoint.toString(16)} `;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (index === 0 && value.length === 1 && codePoint === 0x002d) {
|
|
175
|
+
output += `\\${character}`;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const isSafeCharacter =
|
|
180
|
+
codePoint >= 0x0080 ||
|
|
181
|
+
codePoint === 0x002d ||
|
|
182
|
+
codePoint === 0x005f ||
|
|
183
|
+
(codePoint >= 0x0030 && codePoint <= 0x0039) ||
|
|
184
|
+
(codePoint >= 0x0041 && codePoint <= 0x005a) ||
|
|
185
|
+
(codePoint >= 0x0061 && codePoint <= 0x007a);
|
|
186
|
+
|
|
187
|
+
output += isSafeCharacter ? character : `\\${character}`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return output;
|
|
191
|
+
}
|
|
135
192
|
|
|
136
193
|
export interface UseGridStateResult {
|
|
137
194
|
pipeline: PipelineResult;
|
|
@@ -201,9 +258,21 @@ export interface UseGridStateResult {
|
|
|
201
258
|
showPaginationControls: () => boolean;
|
|
202
259
|
paginationSummary: () => string;
|
|
203
260
|
pageSizeOptions: () => number[];
|
|
204
|
-
isCellEditable: (
|
|
261
|
+
isCellEditable: (
|
|
262
|
+
row: GridRow,
|
|
263
|
+
column: GridColumnDef,
|
|
264
|
+
triggerEvent?: Event | KeyboardEvent | null,
|
|
265
|
+
) => boolean;
|
|
205
266
|
shouldEditOnFocus: (column: GridColumnDef) => boolean;
|
|
206
267
|
|
|
268
|
+
// Pinning
|
|
269
|
+
isPinned: (column: GridColumnDef) => boolean;
|
|
270
|
+
pinnedOffset: (column: GridColumnDef) => { side: 'left' | 'right'; offset: string } | null;
|
|
271
|
+
isPinningEnabled: () => boolean;
|
|
272
|
+
isColumnPinnable: (column: GridColumnDef) => boolean;
|
|
273
|
+
togglePin: (column: GridColumnDef) => void;
|
|
274
|
+
pinningFeature: boolean;
|
|
275
|
+
|
|
207
276
|
// Feature flags
|
|
208
277
|
sortingFeature: boolean;
|
|
209
278
|
filteringFeature: boolean;
|
|
@@ -226,7 +295,11 @@ export interface UseGridStateResult {
|
|
|
226
295
|
clearAllFilters: () => void;
|
|
227
296
|
toggleGrouping: (column: GridColumnDef, event?: React.MouseEvent) => void;
|
|
228
297
|
toggleGroup: (item: GroupItem) => void;
|
|
229
|
-
focusCell: (
|
|
298
|
+
focusCell: (
|
|
299
|
+
row: GridRow,
|
|
300
|
+
column: GridColumnDef,
|
|
301
|
+
triggerEvent?: Event | KeyboardEvent | null,
|
|
302
|
+
) => void;
|
|
230
303
|
handleCellKeyDown: (row: GridRow, column: GridColumnDef, event: React.KeyboardEvent) => void;
|
|
231
304
|
handleCellDoubleClick: (row: GridRow, column: GridColumnDef, event: React.MouseEvent) => void;
|
|
232
305
|
updateEditingValue: (value: string) => void;
|
|
@@ -243,13 +316,19 @@ export interface UseGridStateResult {
|
|
|
243
316
|
onViewportScroll: (startIndex: number) => void;
|
|
244
317
|
}
|
|
245
318
|
|
|
246
|
-
export function useGridState(
|
|
319
|
+
export function useGridState(
|
|
320
|
+
options: GridOptions,
|
|
321
|
+
onRegisterApi?: (api: UiGridApi) => void,
|
|
322
|
+
): UseGridStateResult {
|
|
247
323
|
const [activeFilters, setActiveFilters] = useState<Record<string, string>>({});
|
|
248
324
|
const [groupByColumns, setGroupByColumns] = useState<string[]>([]);
|
|
249
325
|
const [collapsedGroups, setCollapsedGroups] = useState<Record<string, boolean>>({});
|
|
250
326
|
const [columnOrder, setColumnOrder] = useState<string[]>([]);
|
|
251
327
|
const [hiddenRowReasons, setHiddenRowReasons] = useState<Record<string, string[]>>({});
|
|
252
|
-
const [sortState, setSortState] = useState<SortState>({
|
|
328
|
+
const [sortState, setSortState] = useState<SortState>({
|
|
329
|
+
columnName: null,
|
|
330
|
+
direction: SORT_DIRECTIONS.none,
|
|
331
|
+
});
|
|
253
332
|
const [focusedCell, setFocusedCell] = useState<GridCellPosition | null>(null);
|
|
254
333
|
const [editingCell, setEditingCell] = useState<GridCellPosition | null>(null);
|
|
255
334
|
const [editingValue, setEditingValue] = useState('');
|
|
@@ -265,6 +344,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
265
344
|
previousVisibleRows: 0,
|
|
266
345
|
});
|
|
267
346
|
const [autoViewportHeight, setAutoViewportHeight] = useState<number | null>(null);
|
|
347
|
+
const [pinnedColumns, setPinnedColumns] = useState<PinnedColumnState>({});
|
|
268
348
|
|
|
269
349
|
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
|
270
350
|
const initializedGridIdRef = useRef<string | null>(null);
|
|
@@ -299,6 +379,8 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
299
379
|
expandedRowsRef.current = expandedRows;
|
|
300
380
|
const expandedTreeRowsRef = useRef(expandedTreeRows);
|
|
301
381
|
expandedTreeRowsRef.current = expandedTreeRows;
|
|
382
|
+
const pinnedColumnsRef = useRef(pinnedColumns);
|
|
383
|
+
pinnedColumnsRef.current = pinnedColumns;
|
|
302
384
|
const currentPageRef = useRef(currentPage);
|
|
303
385
|
currentPageRef.current = currentPage;
|
|
304
386
|
const pageSizeRef = useRef(pageSize);
|
|
@@ -311,17 +393,31 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
311
393
|
const rowSize = options.rowHeight ?? 44;
|
|
312
394
|
|
|
313
395
|
const visibleColumns = useMemo(() => {
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
396
|
+
const orderedColumns = orderVisibleColumns(options.columnDefs, columnOrder);
|
|
397
|
+
const pinnedEntries = Object.entries(pinnedColumns);
|
|
398
|
+
if (pinnedEntries.length === 0) {
|
|
399
|
+
return orderedColumns;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const columnByName = new Map(orderedColumns.map((column) => [column.name, column]));
|
|
403
|
+
const pinnedLeft = pinnedEntries
|
|
404
|
+
.filter(([, direction]) => direction === 'left')
|
|
405
|
+
.map(([columnName]) => columnByName.get(columnName))
|
|
406
|
+
.filter((column): column is GridColumnDef => column !== undefined);
|
|
407
|
+
const pinnedRight = pinnedEntries
|
|
408
|
+
.filter(([, direction]) => direction === 'right')
|
|
409
|
+
.map(([columnName]) => columnByName.get(columnName))
|
|
410
|
+
.filter((column): column is GridColumnDef => column !== undefined);
|
|
411
|
+
const centerColumns = orderedColumns.filter((column) => pinnedColumns[column.name] === undefined);
|
|
412
|
+
|
|
413
|
+
return [...pinnedLeft, ...centerColumns, ...pinnedRight];
|
|
414
|
+
}, [options.columnDefs, columnOrder, pinnedColumns]);
|
|
319
415
|
|
|
320
416
|
const visibleColumnsRef = useRef(visibleColumns);
|
|
321
417
|
visibleColumnsRef.current = visibleColumns;
|
|
322
418
|
|
|
323
419
|
const pipeline = useMemo<PipelineResult>(() => {
|
|
324
|
-
return
|
|
420
|
+
return defaultGridEngine.buildPipeline({
|
|
325
421
|
options,
|
|
326
422
|
columns: visibleColumns,
|
|
327
423
|
activeFilters,
|
|
@@ -335,7 +431,20 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
335
431
|
pageSize,
|
|
336
432
|
rowSize,
|
|
337
433
|
});
|
|
338
|
-
}, [
|
|
434
|
+
}, [
|
|
435
|
+
options,
|
|
436
|
+
visibleColumns,
|
|
437
|
+
activeFilters,
|
|
438
|
+
sortState,
|
|
439
|
+
groupByColumns,
|
|
440
|
+
collapsedGroups,
|
|
441
|
+
hiddenRowReasons,
|
|
442
|
+
expandedRows,
|
|
443
|
+
expandedTreeRows,
|
|
444
|
+
currentPage,
|
|
445
|
+
pageSize,
|
|
446
|
+
rowSize,
|
|
447
|
+
]);
|
|
339
448
|
|
|
340
449
|
const pipelineRef = useRef(pipeline);
|
|
341
450
|
pipelineRef.current = pipeline;
|
|
@@ -343,10 +452,26 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
343
452
|
const labels = useMemo(() => resolveGridLabels(options.labels), [options.labels]);
|
|
344
453
|
|
|
345
454
|
const gridTemplateColumns = useMemo(
|
|
346
|
-
() => visibleColumns
|
|
347
|
-
[visibleColumns]
|
|
455
|
+
() => buildGridTemplateColumns(visibleColumns),
|
|
456
|
+
[visibleColumns],
|
|
348
457
|
);
|
|
349
458
|
|
|
459
|
+
const isPinningEnabledFn = useCallback((): boolean => {
|
|
460
|
+
return isPinningEnabled(optionsRef.current);
|
|
461
|
+
}, []);
|
|
462
|
+
|
|
463
|
+
const isColumnPinnableFn = useCallback((column: GridColumnDef): boolean => {
|
|
464
|
+
return isColumnPinnable(optionsRef.current, column);
|
|
465
|
+
}, []);
|
|
466
|
+
|
|
467
|
+
const isPinnedFn = useCallback((column: GridColumnDef): boolean => {
|
|
468
|
+
return pinnedColumnsRef.current[column.name] !== undefined;
|
|
469
|
+
}, []);
|
|
470
|
+
|
|
471
|
+
const pinnedOffsetFn = useCallback((column: GridColumnDef) => {
|
|
472
|
+
return computePinnedOffset(visibleColumnsRef.current, pinnedColumnsRef.current, column);
|
|
473
|
+
}, []);
|
|
474
|
+
|
|
350
475
|
// --- Helper functions (all pure, no state closures needed beyond refs) ---
|
|
351
476
|
|
|
352
477
|
const resolveRowId = useCallback((row: GridRow | GridRecord | string): string => {
|
|
@@ -358,13 +483,16 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
358
483
|
{ ...optionsRef.current, data },
|
|
359
484
|
optionsRef.current.rowHeight ?? 44,
|
|
360
485
|
hiddenRowReasonsRef.current,
|
|
361
|
-
expandedRowsRef.current
|
|
486
|
+
expandedRowsRef.current,
|
|
362
487
|
);
|
|
363
488
|
}, []);
|
|
364
489
|
|
|
365
|
-
const findRowById = useCallback(
|
|
366
|
-
|
|
367
|
-
|
|
490
|
+
const findRowById = useCallback(
|
|
491
|
+
(rowId: string): GridRow | null => {
|
|
492
|
+
return coreFindGridRowById(buildRowsFromData(optionsRef.current.data), rowId);
|
|
493
|
+
},
|
|
494
|
+
[buildRowsFromData],
|
|
495
|
+
);
|
|
368
496
|
|
|
369
497
|
const canExpandRowsFn = useCallback((): boolean => {
|
|
370
498
|
return FEATURE_EXPANDABLE && canGridExpandRows(optionsRef.current);
|
|
@@ -376,7 +504,12 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
376
504
|
|
|
377
505
|
const getCurrentPageValueFn = useCallback((totalItems?: number): number => {
|
|
378
506
|
const ti = totalItems ?? pipelineRef.current.totalItems;
|
|
379
|
-
return coreGetCurrentPageValue(
|
|
507
|
+
return coreGetCurrentPageValue(
|
|
508
|
+
optionsRef.current,
|
|
509
|
+
currentPageRef.current,
|
|
510
|
+
ti,
|
|
511
|
+
pageSizeRef.current,
|
|
512
|
+
);
|
|
380
513
|
}, []);
|
|
381
514
|
|
|
382
515
|
const getTotalPagesValueFn = useCallback((totalItems?: number): number => {
|
|
@@ -386,30 +519,44 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
386
519
|
|
|
387
520
|
const getFirstRowIndexValueFn = useCallback((totalItems?: number): number => {
|
|
388
521
|
const ti = totalItems ?? pipelineRef.current.totalItems;
|
|
389
|
-
return coreGetFirstRowIndexValue(
|
|
522
|
+
return coreGetFirstRowIndexValue(
|
|
523
|
+
optionsRef.current,
|
|
524
|
+
currentPageRef.current,
|
|
525
|
+
ti,
|
|
526
|
+
pageSizeRef.current,
|
|
527
|
+
);
|
|
390
528
|
}, []);
|
|
391
529
|
|
|
392
530
|
const getLastRowIndexValueFn = useCallback((totalItems?: number): number => {
|
|
393
531
|
const ti = totalItems ?? pipelineRef.current.totalItems;
|
|
394
|
-
return coreGetLastRowIndexValue(
|
|
532
|
+
return coreGetLastRowIndexValue(
|
|
533
|
+
optionsRef.current,
|
|
534
|
+
currentPageRef.current,
|
|
535
|
+
ti,
|
|
536
|
+
pageSizeRef.current,
|
|
537
|
+
);
|
|
395
538
|
}, []);
|
|
396
539
|
|
|
397
|
-
const isCellEditable = useCallback(
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
540
|
+
const isCellEditable = useCallback(
|
|
541
|
+
(row: GridRow, column: GridColumnDef, triggerEvent?: Event | KeyboardEvent | null): boolean => {
|
|
542
|
+
if (!FEATURE_CELL_EDIT) return false;
|
|
543
|
+
const editable = column.enableCellEdit ?? optionsRef.current.enableCellEdit ?? false;
|
|
544
|
+
if (!editable) return false;
|
|
545
|
+
|
|
546
|
+
const condition =
|
|
547
|
+
column.cellEditableCondition ?? optionsRef.current.cellEditableCondition ?? true;
|
|
548
|
+
if (typeof condition === 'boolean') return condition;
|
|
549
|
+
|
|
550
|
+
const context: GridCellEditableContext = {
|
|
551
|
+
row: row.entity,
|
|
552
|
+
column,
|
|
553
|
+
rowIndex: row.index,
|
|
554
|
+
triggerEvent,
|
|
555
|
+
};
|
|
556
|
+
return condition(context);
|
|
557
|
+
},
|
|
558
|
+
[],
|
|
559
|
+
);
|
|
413
560
|
|
|
414
561
|
const shouldEditOnFocusFn = useCallback((column: GridColumnDef): boolean => {
|
|
415
562
|
return column.enableCellEditOnFocus ?? optionsRef.current.enableCellEditOnFocus ?? false;
|
|
@@ -419,7 +566,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
419
566
|
|
|
420
567
|
const focusRenderedCell = useCallback((position: GridCellPosition): void => {
|
|
421
568
|
const focusToken = ++renderedCellFocusTokenRef.current;
|
|
422
|
-
const selector = `.body-cell[data-row-id="${position.rowId}"][data-col-name="${position.columnName}"]`;
|
|
569
|
+
const selector = `.body-cell[data-row-id="${escapeCssSelectorValue(position.rowId)}"][data-col-name="${escapeCssSelectorValue(position.columnName)}"]`;
|
|
423
570
|
|
|
424
571
|
const doFocus = (retry = true): void => {
|
|
425
572
|
if (focusToken !== renderedCellFocusTokenRef.current) return;
|
|
@@ -444,12 +591,13 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
444
591
|
const ec = editingCellRef.current;
|
|
445
592
|
if (!ec) return;
|
|
446
593
|
|
|
447
|
-
const selector = `.cell-editor[data-row-id="${ec.rowId}"][data-col-name="${ec.columnName}"]`;
|
|
594
|
+
const selector = `.cell-editor[data-row-id="${escapeCssSelectorValue(ec.rowId)}"][data-col-name="${escapeCssSelectorValue(ec.columnName)}"]`;
|
|
448
595
|
|
|
449
596
|
const doFocus = (retry = true): void => {
|
|
450
597
|
if (focusToken !== editorFocusTokenRef.current) return;
|
|
451
598
|
const currentEc = editingCellRef.current;
|
|
452
|
-
if (!currentEc || currentEc.rowId !== ec.rowId || currentEc.columnName !== ec.columnName)
|
|
599
|
+
if (!currentEc || currentEc.rowId !== ec.rowId || currentEc.columnName !== ec.columnName)
|
|
600
|
+
return;
|
|
453
601
|
|
|
454
602
|
const container = gridContainerRef.current;
|
|
455
603
|
if (!container) return;
|
|
@@ -500,10 +648,10 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
500
648
|
moveColumn: (fromIndex, toIndex) => {
|
|
501
649
|
moveGridColumnCommand(
|
|
502
650
|
gridApiRef.current!,
|
|
503
|
-
FEATURE_COLUMN_MOVING &&
|
|
651
|
+
FEATURE_COLUMN_MOVING && optionsRef.current.enableColumnMoving === true,
|
|
504
652
|
(updater) => setColumnOrder((current) => updater(current)),
|
|
505
653
|
fromIndex,
|
|
506
|
-
toIndex
|
|
654
|
+
toIndex,
|
|
507
655
|
);
|
|
508
656
|
},
|
|
509
657
|
toggleGrouping: (columnName) => {
|
|
@@ -517,7 +665,11 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
517
665
|
gridApiRef.current!.core.raise.groupingChanged(next);
|
|
518
666
|
},
|
|
519
667
|
clearGrouping: () => {
|
|
520
|
-
clearGridGroupingCommand(
|
|
668
|
+
clearGridGroupingCommand(
|
|
669
|
+
gridApiRef.current!,
|
|
670
|
+
(grouping) => setGroupByColumns(grouping),
|
|
671
|
+
false,
|
|
672
|
+
);
|
|
521
673
|
},
|
|
522
674
|
benchmark: (iterations) => {
|
|
523
675
|
return runBenchmarkFn(iterations);
|
|
@@ -543,7 +695,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
543
695
|
expandAllGridTreeRowsCommand(
|
|
544
696
|
(data) => buildRowsFromData(data),
|
|
545
697
|
optionsRef.current.data,
|
|
546
|
-
(e) => setExpandedTreeRows(e)
|
|
698
|
+
(e) => setExpandedTreeRows(e),
|
|
547
699
|
);
|
|
548
700
|
},
|
|
549
701
|
treeCollapseAllRows: () => {
|
|
@@ -563,35 +715,35 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
563
715
|
infiniteScrollStateRef.current,
|
|
564
716
|
(s) => setInfiniteScrollState(s),
|
|
565
717
|
scrollUp ?? infiniteScrollStateRef.current.scrollUp,
|
|
566
|
-
scrollDown ?? infiniteScrollStateRef.current.scrollDown
|
|
718
|
+
scrollDown ?? infiniteScrollStateRef.current.scrollDown,
|
|
567
719
|
);
|
|
568
720
|
},
|
|
569
721
|
infiniteScrollReset: (scrollUp, scrollDown) => {
|
|
570
722
|
resetGridInfiniteScrollCommand(
|
|
571
723
|
(s) => setInfiniteScrollState(s),
|
|
572
724
|
scrollUp ?? infiniteScrollStateRef.current.scrollUp,
|
|
573
|
-
scrollDown ?? infiniteScrollStateRef.current.scrollDown
|
|
725
|
+
scrollDown ?? infiniteScrollStateRef.current.scrollDown,
|
|
574
726
|
);
|
|
575
727
|
},
|
|
576
728
|
infiniteScrollSaveScrollPercentage: () => {
|
|
577
729
|
saveGridInfiniteScrollPercentageCommand(
|
|
578
730
|
infiniteScrollStateRef.current,
|
|
579
731
|
pipelineRef.current.visibleRows.length,
|
|
580
|
-
(s) => setInfiniteScrollState(s)
|
|
732
|
+
(s) => setInfiniteScrollState(s),
|
|
581
733
|
);
|
|
582
734
|
},
|
|
583
735
|
infiniteScrollDataRemovedTop: (scrollUp, scrollDown) => {
|
|
584
736
|
resetGridInfiniteScrollCommand(
|
|
585
737
|
(s) => setInfiniteScrollState(s),
|
|
586
738
|
scrollUp ?? infiniteScrollStateRef.current.scrollUp,
|
|
587
|
-
scrollDown ?? infiniteScrollStateRef.current.scrollDown
|
|
739
|
+
scrollDown ?? infiniteScrollStateRef.current.scrollDown,
|
|
588
740
|
);
|
|
589
741
|
},
|
|
590
742
|
infiniteScrollDataRemovedBottom: (scrollUp, scrollDown) => {
|
|
591
743
|
resetGridInfiniteScrollCommand(
|
|
592
744
|
(s) => setInfiniteScrollState(s),
|
|
593
745
|
scrollUp ?? infiniteScrollStateRef.current.scrollUp,
|
|
594
|
-
scrollDown ?? infiniteScrollStateRef.current.scrollDown
|
|
746
|
+
scrollDown ?? infiniteScrollStateRef.current.scrollDown,
|
|
595
747
|
);
|
|
596
748
|
},
|
|
597
749
|
infiniteScrollSetDirections: (scrollUp, scrollDown) => {
|
|
@@ -599,7 +751,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
599
751
|
infiniteScrollStateRef.current,
|
|
600
752
|
(s) => setInfiniteScrollState(s),
|
|
601
753
|
scrollUp,
|
|
602
|
-
scrollDown
|
|
754
|
+
scrollDown,
|
|
603
755
|
);
|
|
604
756
|
},
|
|
605
757
|
saveState: () => {
|
|
@@ -613,6 +765,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
613
765
|
totalItems: pipelineRef.current.totalItems,
|
|
614
766
|
expandedRows: expandedRowsRef.current,
|
|
615
767
|
expandedTreeRows: expandedTreeRowsRef.current,
|
|
768
|
+
pinnedColumns: pinnedColumnsRef.current,
|
|
616
769
|
});
|
|
617
770
|
},
|
|
618
771
|
restoreState: (state) => {
|
|
@@ -625,6 +778,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
625
778
|
setPageSize: (ps) => setPageSize(ps),
|
|
626
779
|
setExpandedRows: (e) => setExpandedRows(e),
|
|
627
780
|
setExpandedTreeRows: (e) => setExpandedTreeRows(e),
|
|
781
|
+
setPinnedColumns: (p) => setPinnedColumns(p),
|
|
628
782
|
getEffectivePageSize: () => effectivePageSizeFn(pipelineRef.current.totalItems),
|
|
629
783
|
});
|
|
630
784
|
},
|
|
@@ -638,6 +792,16 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
638
792
|
endCellEdit: () => commitCellEditFn(),
|
|
639
793
|
cancelCellEdit: () => cancelCellEditFn(),
|
|
640
794
|
getEditingCell: () => editingCellRef.current,
|
|
795
|
+
pinColumn: (columnName: string, direction: PinDirection) => {
|
|
796
|
+
pinGridColumnCommand(
|
|
797
|
+
gridApiRef.current!,
|
|
798
|
+
isPinningEnabledFn(),
|
|
799
|
+
(v) => setPinnedColumns(v),
|
|
800
|
+
() => pinnedColumnsRef.current,
|
|
801
|
+
columnName,
|
|
802
|
+
direction,
|
|
803
|
+
);
|
|
804
|
+
},
|
|
641
805
|
};
|
|
642
806
|
|
|
643
807
|
gridApiRef.current = createGridApi(bindings);
|
|
@@ -647,48 +811,71 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
647
811
|
|
|
648
812
|
// --- Memoized action functions ---
|
|
649
813
|
|
|
650
|
-
const seekPageFn = useCallback(
|
|
651
|
-
|
|
814
|
+
const seekPageFn = useCallback(
|
|
815
|
+
(page: number): void => {
|
|
816
|
+
seekGridPaginationCommand(
|
|
817
|
+
gridApiRef.current!,
|
|
818
|
+
(nextPage) => setCurrentPage(nextPage),
|
|
819
|
+
() => getTotalPagesValueFn(),
|
|
820
|
+
() => effectivePageSizeFn(pipelineRef.current.totalItems),
|
|
821
|
+
page,
|
|
822
|
+
);
|
|
823
|
+
},
|
|
824
|
+
[getTotalPagesValueFn, effectivePageSizeFn],
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
const togglePinFn = useCallback((column: GridColumnDef): void => {
|
|
828
|
+
const current = pinnedColumnsRef.current[column.name];
|
|
829
|
+
const next: PinDirection = current === 'left' ? 'right' : current === 'right' ? 'none' : 'left';
|
|
830
|
+
pinGridColumnCommand(
|
|
652
831
|
gridApiRef.current!,
|
|
653
|
-
(
|
|
654
|
-
() =>
|
|
655
|
-
() =>
|
|
656
|
-
|
|
832
|
+
isPinningEnabledFn(),
|
|
833
|
+
(v) => setPinnedColumns(v),
|
|
834
|
+
() => pinnedColumnsRef.current,
|
|
835
|
+
column.name,
|
|
836
|
+
next,
|
|
657
837
|
);
|
|
658
|
-
}, [
|
|
838
|
+
}, []);
|
|
659
839
|
|
|
660
840
|
const setPaginationPageSizeFn = useCallback((ps: number): void => {
|
|
661
841
|
setGridPaginationPageSizeCommand(
|
|
662
842
|
gridApiRef.current!,
|
|
663
843
|
(nextPageSize) => setPageSize(nextPageSize),
|
|
664
844
|
(nextPage) => setCurrentPage(nextPage),
|
|
665
|
-
ps
|
|
845
|
+
ps,
|
|
666
846
|
);
|
|
667
847
|
}, []);
|
|
668
848
|
|
|
669
|
-
const toggleRowExpansionByRefFn = useCallback(
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
849
|
+
const toggleRowExpansionByRefFn = useCallback(
|
|
850
|
+
(row: GridRow | GridRecord | string): void => {
|
|
851
|
+
const rowId = coreResolveGridRowId(optionsRef.current, row);
|
|
852
|
+
toggleGridRowExpansionCommand(
|
|
853
|
+
gridApiRef.current!,
|
|
854
|
+
FEATURE_EXPANDABLE && canGridExpandRows(optionsRef.current),
|
|
855
|
+
expandedRowsRef.current,
|
|
856
|
+
rowId,
|
|
857
|
+
(e) => setExpandedRows(e),
|
|
858
|
+
(resolvedRowId) =>
|
|
859
|
+
coreFindGridRowById(buildRowsFromData(optionsRef.current.data), resolvedRowId),
|
|
860
|
+
);
|
|
861
|
+
},
|
|
862
|
+
[buildRowsFromData],
|
|
863
|
+
);
|
|
680
864
|
|
|
681
865
|
const expandAllRowsFn = useCallback((): void => {
|
|
682
866
|
if (!canGridExpandRows(optionsRef.current)) return;
|
|
683
867
|
expandAllGridRowsCommand(
|
|
684
868
|
(data) => buildRowsFromData(data),
|
|
685
869
|
optionsRef.current.data,
|
|
686
|
-
(e) => setExpandedRows(e)
|
|
870
|
+
(e) => setExpandedRows(e),
|
|
687
871
|
);
|
|
688
872
|
}, [buildRowsFromData]);
|
|
689
873
|
|
|
690
874
|
const toggleAllRowsFn = useCallback((): void => {
|
|
691
|
-
const allExpanded = areAllGridRowsExpanded(
|
|
875
|
+
const allExpanded = areAllGridRowsExpanded(
|
|
876
|
+
buildRowsFromData(optionsRef.current.data),
|
|
877
|
+
expandedRowsRef.current,
|
|
878
|
+
);
|
|
692
879
|
if (allExpanded) {
|
|
693
880
|
collapseAllGridRowsCommand((e) => setExpandedRows(e));
|
|
694
881
|
} else {
|
|
@@ -696,89 +883,115 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
696
883
|
}
|
|
697
884
|
}, [buildRowsFromData, expandAllRowsFn]);
|
|
698
885
|
|
|
699
|
-
const toggleTreeRowByRefFn = useCallback(
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
gridApiRef.current!,
|
|
714
|
-
expandedTreeRowsRef.current,
|
|
715
|
-
rowId,
|
|
716
|
-
true,
|
|
717
|
-
(e) => setExpandedTreeRows(e),
|
|
718
|
-
(resolvedRowId) => coreFindGridRowById(buildRowsFromData(optionsRef.current.data), resolvedRowId)
|
|
719
|
-
);
|
|
720
|
-
}, [buildRowsFromData]);
|
|
886
|
+
const toggleTreeRowByRefFn = useCallback(
|
|
887
|
+
(row: GridRow | GridRecord | string): void => {
|
|
888
|
+
const rowId = coreResolveGridRowId(optionsRef.current, row);
|
|
889
|
+
toggleGridTreeRowCommand(
|
|
890
|
+
gridApiRef.current!,
|
|
891
|
+
expandedTreeRowsRef.current,
|
|
892
|
+
rowId,
|
|
893
|
+
(e) => setExpandedTreeRows(e),
|
|
894
|
+
(resolvedRowId) =>
|
|
895
|
+
coreFindGridRowById(buildRowsFromData(optionsRef.current.data), resolvedRowId),
|
|
896
|
+
);
|
|
897
|
+
},
|
|
898
|
+
[buildRowsFromData],
|
|
899
|
+
);
|
|
721
900
|
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
901
|
+
const expandTreeRowByRefFn = useCallback(
|
|
902
|
+
(row: GridRow | GridRecord | string): void => {
|
|
903
|
+
const rowId = coreResolveGridRowId(optionsRef.current, row);
|
|
904
|
+
setGridTreeRowExpandedCommand(
|
|
905
|
+
gridApiRef.current!,
|
|
906
|
+
expandedTreeRowsRef.current,
|
|
907
|
+
rowId,
|
|
908
|
+
true,
|
|
909
|
+
(e) => setExpandedTreeRows(e),
|
|
910
|
+
(resolvedRowId) =>
|
|
911
|
+
coreFindGridRowById(buildRowsFromData(optionsRef.current.data), resolvedRowId),
|
|
912
|
+
);
|
|
913
|
+
},
|
|
914
|
+
[buildRowsFromData],
|
|
915
|
+
);
|
|
733
916
|
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
);
|
|
917
|
+
const collapseTreeRowByRefFn = useCallback(
|
|
918
|
+
(row: GridRow | GridRecord | string): void => {
|
|
919
|
+
const rowId = coreResolveGridRowId(optionsRef.current, row);
|
|
920
|
+
setGridTreeRowExpandedCommand(
|
|
921
|
+
gridApiRef.current!,
|
|
922
|
+
expandedTreeRowsRef.current,
|
|
923
|
+
rowId,
|
|
924
|
+
false,
|
|
925
|
+
(e) => setExpandedTreeRows(e),
|
|
926
|
+
(resolvedRowId) =>
|
|
927
|
+
coreFindGridRowById(buildRowsFromData(optionsRef.current.data), resolvedRowId),
|
|
928
|
+
);
|
|
929
|
+
},
|
|
930
|
+
[buildRowsFromData],
|
|
931
|
+
);
|
|
750
932
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
933
|
+
const startCellEditFn = useCallback(
|
|
934
|
+
(
|
|
935
|
+
row: GridRow,
|
|
936
|
+
column: GridColumnDef,
|
|
937
|
+
triggerEvent?: Event | KeyboardEvent | null,
|
|
938
|
+
initialValue?: string,
|
|
939
|
+
): void => {
|
|
940
|
+
const currentValue = getCellValue(row.entity, column);
|
|
941
|
+
const focusToken = ++editorFocusTokenRef.current;
|
|
942
|
+
const ec = beginGridCellEditCommand(
|
|
943
|
+
gridApiRef.current!,
|
|
944
|
+
{
|
|
945
|
+
setFocusedCell: (fc) => setFocusedCell(fc),
|
|
946
|
+
setEditingCell: (ec2) => setEditingCell(ec2),
|
|
947
|
+
setEditingValue: (ev) => setEditingValue(ev),
|
|
948
|
+
},
|
|
949
|
+
row,
|
|
950
|
+
column,
|
|
951
|
+
currentValue,
|
|
952
|
+
triggerEvent,
|
|
953
|
+
initialValue,
|
|
954
|
+
);
|
|
955
|
+
|
|
956
|
+
if (ec) {
|
|
957
|
+
queueMicrotask(() => focusEditorInput(focusToken));
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
[focusEditorInput],
|
|
961
|
+
);
|
|
755
962
|
|
|
756
|
-
const commitCellEditFn = useCallback(
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
963
|
+
const commitCellEditFn = useCallback(
|
|
964
|
+
(direction?: GridMoveDirection, restoreFocus = true): void => {
|
|
965
|
+
const result = commitGridCellEditCommand(gridApiRef.current!, {
|
|
966
|
+
getEditingCell: () => editingCellRef.current,
|
|
967
|
+
getEditingValue: () => editingValueRef.current,
|
|
968
|
+
setEditingCell: (ec) => setEditingCell(ec),
|
|
969
|
+
setEditingValue: (ev) => setEditingValue(ev),
|
|
970
|
+
findRowById: (rowId) =>
|
|
971
|
+
coreFindGridRowById(buildRowsFromData(optionsRef.current.data), rowId),
|
|
972
|
+
findColumnByName: (columnName) =>
|
|
973
|
+
visibleColumnsRef.current.find((c) => c.name === columnName),
|
|
974
|
+
parseEditedValue: (column, value, oldValue) =>
|
|
975
|
+
parseGridEditedValue(column, value, oldValue),
|
|
976
|
+
setCellValue: (rowEntity, column, value) => {
|
|
977
|
+
const fieldPath = column.editModelField ?? column.field ?? column.name;
|
|
978
|
+
setPathValue(rowEntity, fieldPath, value);
|
|
979
|
+
},
|
|
980
|
+
});
|
|
770
981
|
|
|
771
|
-
|
|
982
|
+
if (!result.committed || !result.row || !result.column || !result.focusTarget) return;
|
|
772
983
|
|
|
773
|
-
|
|
984
|
+
editorFocusTokenRef.current += 1;
|
|
774
985
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
986
|
+
if (direction) {
|
|
987
|
+
const moved = moveFocusFn(result.row, result.column, direction);
|
|
988
|
+
if (!moved) focusRenderedCell(result.focusTarget);
|
|
989
|
+
} else if (restoreFocus) {
|
|
990
|
+
focusRenderedCell(result.focusTarget);
|
|
991
|
+
}
|
|
992
|
+
},
|
|
993
|
+
[buildRowsFromData, focusRenderedCell],
|
|
994
|
+
);
|
|
782
995
|
|
|
783
996
|
const cancelCellEditFn = useCallback((): void => {
|
|
784
997
|
const hadEditingCell = editingCellRef.current !== null;
|
|
@@ -786,8 +999,10 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
786
999
|
getEditingCell: () => editingCellRef.current,
|
|
787
1000
|
setEditingCell: (ec) => setEditingCell(ec),
|
|
788
1001
|
setEditingValue: (ev) => setEditingValue(ev),
|
|
789
|
-
findRowById: (rowId) =>
|
|
790
|
-
|
|
1002
|
+
findRowById: (rowId) =>
|
|
1003
|
+
coreFindGridRowById(buildRowsFromData(optionsRef.current.data), rowId),
|
|
1004
|
+
findColumnByName: (columnName) =>
|
|
1005
|
+
visibleColumnsRef.current.find((c) => c.name === columnName),
|
|
791
1006
|
});
|
|
792
1007
|
|
|
793
1008
|
if (!hadEditingCell) return;
|
|
@@ -795,30 +1010,44 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
795
1010
|
if (result.focusTarget) focusRenderedCell(result.focusTarget);
|
|
796
1011
|
}, [buildRowsFromData, focusRenderedCell]);
|
|
797
1012
|
|
|
798
|
-
const moveFocusFn = useCallback(
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
1013
|
+
const moveFocusFn = useCallback(
|
|
1014
|
+
(
|
|
1015
|
+
row: GridRow,
|
|
1016
|
+
column: GridColumnDef,
|
|
1017
|
+
direction: GridMoveDirection,
|
|
1018
|
+
triggerEvent?: Event | KeyboardEvent | null,
|
|
1019
|
+
): boolean => {
|
|
1020
|
+
const nextCell = findNextGridCell({
|
|
1021
|
+
rows: pipelineRef.current.visibleRows,
|
|
1022
|
+
columns: visibleColumnsRef.current,
|
|
1023
|
+
rowId: row.id,
|
|
1024
|
+
columnName: column.name,
|
|
1025
|
+
direction,
|
|
1026
|
+
});
|
|
1027
|
+
if (!nextCell) return false;
|
|
807
1028
|
|
|
808
|
-
|
|
809
|
-
|
|
1029
|
+
setFocusedCell({ rowId: nextCell.row.id, columnName: nextCell.column.name });
|
|
1030
|
+
focusRenderedCell({ rowId: nextCell.row.id, columnName: nextCell.column.name });
|
|
810
1031
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1032
|
+
if (
|
|
1033
|
+
shouldEditOnFocusFn(nextCell.column) &&
|
|
1034
|
+
isCellEditable(nextCell.row, nextCell.column, triggerEvent)
|
|
1035
|
+
) {
|
|
1036
|
+
startCellEditFn(nextCell.row, nextCell.column, triggerEvent);
|
|
1037
|
+
}
|
|
814
1038
|
|
|
815
|
-
|
|
816
|
-
|
|
1039
|
+
return true;
|
|
1040
|
+
},
|
|
1041
|
+
[focusRenderedCell, isCellEditable, shouldEditOnFocusFn, startCellEditFn],
|
|
1042
|
+
);
|
|
817
1043
|
|
|
818
1044
|
const runBenchmarkFn = useCallback((iterations?: number): GridBenchmarkResult => {
|
|
819
|
-
const safeIterations =
|
|
1045
|
+
const safeIterations = resolveBenchmarkIterations(
|
|
1046
|
+
iterations,
|
|
1047
|
+
optionsRef.current.benchmark?.iterations,
|
|
1048
|
+
);
|
|
820
1049
|
const startedAt = performance.now();
|
|
821
|
-
let lastResult =
|
|
1050
|
+
let lastResult = defaultGridEngine.buildPipeline({
|
|
822
1051
|
options: optionsRef.current,
|
|
823
1052
|
columns: visibleColumnsRef.current,
|
|
824
1053
|
activeFilters: activeFiltersRef.current,
|
|
@@ -834,7 +1063,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
834
1063
|
});
|
|
835
1064
|
|
|
836
1065
|
for (let i = 1; i < safeIterations; i++) {
|
|
837
|
-
lastResult =
|
|
1066
|
+
lastResult = defaultGridEngine.buildPipeline({
|
|
838
1067
|
options: optionsRef.current,
|
|
839
1068
|
columns: visibleColumnsRef.current,
|
|
840
1069
|
activeFilters: activeFiltersRef.current,
|
|
@@ -887,6 +1116,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
887
1116
|
setExpandedTreeRows({});
|
|
888
1117
|
setColumnOrder(options.columnDefs.map((column) => column.name));
|
|
889
1118
|
setGroupByColumns(options.grouping?.groupBy ?? []);
|
|
1119
|
+
setPinnedColumns(buildInitialPinnedState(options.columnDefs));
|
|
890
1120
|
setCurrentPage(options.paginationCurrentPage ?? 1);
|
|
891
1121
|
setPageSize(coreGetEffectivePageSize(options, 0, options.data.length));
|
|
892
1122
|
|
|
@@ -898,7 +1128,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
898
1128
|
});
|
|
899
1129
|
|
|
900
1130
|
const initialSort = options.columnDefs.find(
|
|
901
|
-
(column) => column.sort?.direction && !column.sort.ignoreSort
|
|
1131
|
+
(column) => column.sort?.direction && !column.sort.ignoreSort,
|
|
902
1132
|
);
|
|
903
1133
|
setSortState({
|
|
904
1134
|
columnName: initialSort?.name ?? null,
|
|
@@ -931,9 +1161,16 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
931
1161
|
if (!container) return;
|
|
932
1162
|
|
|
933
1163
|
const observer = observeGridHostSize(container, ({ height: nextHeight, width: nextWidth }) => {
|
|
934
|
-
if (nextHeight === lastGridHeightRef.current && nextWidth === lastGridWidthRef.current)
|
|
1164
|
+
if (nextHeight === lastGridHeightRef.current && nextWidth === lastGridWidthRef.current)
|
|
1165
|
+
return;
|
|
935
1166
|
|
|
936
|
-
raiseGridDimensionChanged(
|
|
1167
|
+
raiseGridDimensionChanged(
|
|
1168
|
+
gridApi,
|
|
1169
|
+
lastGridHeightRef.current,
|
|
1170
|
+
lastGridWidthRef.current,
|
|
1171
|
+
nextHeight,
|
|
1172
|
+
nextWidth,
|
|
1173
|
+
);
|
|
937
1174
|
lastGridHeightRef.current = nextHeight;
|
|
938
1175
|
lastGridWidthRef.current = nextWidth;
|
|
939
1176
|
|
|
@@ -956,55 +1193,90 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
956
1193
|
const paginationCurrentPage = getCurrentPageValueFn();
|
|
957
1194
|
const paginationTotalPages = getTotalPagesValueFn();
|
|
958
1195
|
const paginationSelectedPageSize = effectivePageSizeFn(pipeline.totalItems);
|
|
959
|
-
const viewportHeightPx =
|
|
1196
|
+
const viewportHeightPx = computeViewportHeightPx(options.viewportHeight, autoViewportHeight);
|
|
960
1197
|
|
|
961
1198
|
// --- Display helper functions ---
|
|
962
1199
|
|
|
963
1200
|
const headerLabelFn = useCallback((column: GridColumnDef): string => coreHeaderLabel(column), []);
|
|
964
|
-
const isGroupItemFn = useCallback(
|
|
965
|
-
|
|
1201
|
+
const isGroupItemFn = useCallback(
|
|
1202
|
+
(item: DisplayItem): item is GroupItem => item.kind === 'group',
|
|
1203
|
+
[],
|
|
1204
|
+
);
|
|
1205
|
+
const isExpandableItemFn = useCallback(
|
|
1206
|
+
(item: DisplayItem): item is ExpandableItem => item.kind === 'expandable',
|
|
1207
|
+
[],
|
|
1208
|
+
);
|
|
966
1209
|
const isRowItemFn = useCallback((item: DisplayItem): item is RowItem => item.kind === 'row', []);
|
|
967
|
-
const isOddStripedRowFn = useCallback(
|
|
1210
|
+
const isOddStripedRowFn = useCallback(
|
|
1211
|
+
(item: DisplayItem): boolean => item.kind === 'row' && item.visibleIndex % 2 === 0,
|
|
1212
|
+
[],
|
|
1213
|
+
);
|
|
968
1214
|
|
|
969
1215
|
const sortDirectionFn = useCallback((column: GridColumnDef): string => {
|
|
970
|
-
return sortStateRef.current.columnName === column.name
|
|
1216
|
+
return sortStateRef.current.columnName === column.name
|
|
1217
|
+
? sortStateRef.current.direction
|
|
1218
|
+
: SORT_DIRECTIONS.none;
|
|
971
1219
|
}, []);
|
|
972
1220
|
|
|
973
|
-
const sortButtonLabelFn = useCallback(
|
|
974
|
-
|
|
975
|
-
|
|
1221
|
+
const sortButtonLabelFn = useCallback(
|
|
1222
|
+
(column: GridColumnDef): string => {
|
|
1223
|
+
return gridSortButtonLabel(sortDirectionFn(column) as any, labels);
|
|
1224
|
+
},
|
|
1225
|
+
[labels, sortDirectionFn],
|
|
1226
|
+
);
|
|
976
1227
|
|
|
977
|
-
const sortAriaSortFn = useCallback(
|
|
978
|
-
|
|
979
|
-
|
|
1228
|
+
const sortAriaSortFn = useCallback(
|
|
1229
|
+
(column: GridColumnDef): string => {
|
|
1230
|
+
return gridSortAriaSort(sortDirectionFn(column) as any);
|
|
1231
|
+
},
|
|
1232
|
+
[sortDirectionFn],
|
|
1233
|
+
);
|
|
980
1234
|
|
|
981
|
-
const groupingButtonLabelFn = useCallback(
|
|
982
|
-
|
|
983
|
-
|
|
1235
|
+
const groupingButtonLabelFn = useCallback(
|
|
1236
|
+
(column: GridColumnDef): string => {
|
|
1237
|
+
return gridGroupingButtonLabel(
|
|
1238
|
+
isGridColumnGrouped(groupByColumnsRef.current, column),
|
|
1239
|
+
labels,
|
|
1240
|
+
);
|
|
1241
|
+
},
|
|
1242
|
+
[labels],
|
|
1243
|
+
);
|
|
984
1244
|
|
|
985
1245
|
const filterValueFn = useCallback((columnName: string): string => {
|
|
986
1246
|
return activeFiltersRef.current[columnName] ?? '';
|
|
987
1247
|
}, []);
|
|
988
1248
|
|
|
989
|
-
const filterPlaceholderFn = useCallback(
|
|
990
|
-
|
|
991
|
-
|
|
1249
|
+
const filterPlaceholderFn = useCallback(
|
|
1250
|
+
(column: GridColumnDef): string => {
|
|
1251
|
+
return gridFilterPlaceholder(isGridColumnFilterable(optionsRef.current, column), labels);
|
|
1252
|
+
},
|
|
1253
|
+
[labels],
|
|
1254
|
+
);
|
|
992
1255
|
|
|
993
1256
|
const isFilterInputDisabledFn = useCallback((column: GridColumnDef): boolean => {
|
|
994
1257
|
return !isGridColumnFilterable(optionsRef.current, column);
|
|
995
1258
|
}, []);
|
|
996
1259
|
|
|
997
|
-
const groupDisclosureLabelFn = useCallback(
|
|
998
|
-
|
|
999
|
-
|
|
1260
|
+
const groupDisclosureLabelFn = useCallback(
|
|
1261
|
+
(item: GroupItem): string => {
|
|
1262
|
+
return gridGroupDisclosureLabel(item.collapsed, labels);
|
|
1263
|
+
},
|
|
1264
|
+
[labels],
|
|
1265
|
+
);
|
|
1000
1266
|
|
|
1001
|
-
const cellContextFn = useCallback(
|
|
1002
|
-
|
|
1003
|
-
|
|
1267
|
+
const cellContextFn = useCallback(
|
|
1268
|
+
(row: GridRow, column: GridColumnDef): GridCellTemplateContext => {
|
|
1269
|
+
return buildGridCellContext(row, column);
|
|
1270
|
+
},
|
|
1271
|
+
[],
|
|
1272
|
+
);
|
|
1004
1273
|
|
|
1005
|
-
const displayValueFn = useCallback(
|
|
1006
|
-
|
|
1007
|
-
|
|
1274
|
+
const displayValueFn = useCallback(
|
|
1275
|
+
(row: GridRow, column: GridColumnDef): string => {
|
|
1276
|
+
return formatGridCellDisplayValue(cellContextFn(row, column));
|
|
1277
|
+
},
|
|
1278
|
+
[cellContextFn],
|
|
1279
|
+
);
|
|
1008
1280
|
|
|
1009
1281
|
const isFocusedCellFn = useCallback((row: GridRow, column: GridColumnDef): boolean => {
|
|
1010
1282
|
return isGridCellPosition(focusedCellRef.current, row.id, column.name);
|
|
@@ -1018,15 +1290,18 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1018
1290
|
return gridEditorInputType(column);
|
|
1019
1291
|
}, []);
|
|
1020
1292
|
|
|
1021
|
-
const expandedContextFn = useCallback(
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1293
|
+
const expandedContextFn = useCallback(
|
|
1294
|
+
(row: GridRow): GridExpandableTemplateContext & Record<string, unknown> => {
|
|
1295
|
+
return {
|
|
1296
|
+
$implicit: row.entity,
|
|
1297
|
+
row: row.entity,
|
|
1298
|
+
rowIndex: row.index,
|
|
1299
|
+
expanded: true,
|
|
1300
|
+
...(optionsRef.current.expandableRowScope ?? {}),
|
|
1301
|
+
};
|
|
1302
|
+
},
|
|
1303
|
+
[],
|
|
1304
|
+
);
|
|
1030
1305
|
|
|
1031
1306
|
const columnWidthFn = useCallback((column: GridColumnDef): string => gridColumnWidth(column), []);
|
|
1032
1307
|
|
|
@@ -1042,17 +1317,23 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1042
1317
|
return gridCellIndent(optionsRef.current, visibleColumnsRef.current, row, column);
|
|
1043
1318
|
}, []);
|
|
1044
1319
|
|
|
1045
|
-
const treeToggleLabelFn = useCallback(
|
|
1046
|
-
|
|
1047
|
-
|
|
1320
|
+
const treeToggleLabelFn = useCallback(
|
|
1321
|
+
(row: GridRow): string => {
|
|
1322
|
+
return gridTreeToggleLabelForRow(expandedTreeRowsRef.current, row, labels);
|
|
1323
|
+
},
|
|
1324
|
+
[labels],
|
|
1325
|
+
);
|
|
1048
1326
|
|
|
1049
1327
|
const isTreeRowExpandedFn = useCallback((row: GridRow): boolean => {
|
|
1050
1328
|
return isGridTreeRowExpanded(expandedTreeRowsRef.current, row);
|
|
1051
1329
|
}, []);
|
|
1052
1330
|
|
|
1053
|
-
const expandToggleLabelFn = useCallback(
|
|
1054
|
-
|
|
1055
|
-
|
|
1331
|
+
const expandToggleLabelFn = useCallback(
|
|
1332
|
+
(row: GridRow): string => {
|
|
1333
|
+
return gridExpandToggleLabelForRow(row, labels);
|
|
1334
|
+
},
|
|
1335
|
+
[labels],
|
|
1336
|
+
);
|
|
1056
1337
|
|
|
1057
1338
|
const isGroupedFn = useCallback((column: GridColumnDef): boolean => {
|
|
1058
1339
|
return isGridColumnGrouped(groupByColumnsRef.current, column);
|
|
@@ -1072,8 +1353,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1072
1353
|
|
|
1073
1354
|
const paginationSummaryFn = useCallback((): string => {
|
|
1074
1355
|
const ti = pipelineRef.current.totalItems;
|
|
1075
|
-
|
|
1076
|
-
return `${getFirstRowIndexValueFn(ti) + 1}-${getLastRowIndexValueFn(ti) + 1} of ${ti}`;
|
|
1356
|
+
return formatPaginationSummary(ti, getFirstRowIndexValueFn(ti), getLastRowIndexValueFn(ti));
|
|
1077
1357
|
}, [getFirstRowIndexValueFn, getLastRowIndexValueFn]);
|
|
1078
1358
|
|
|
1079
1359
|
const pageSizeOptionsFn = useCallback((): number[] => {
|
|
@@ -1093,7 +1373,10 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1093
1373
|
const toggleSortFn = useCallback((column: GridColumnDef): void => {
|
|
1094
1374
|
if (!FEATURE_SORTING || !isGridColumnSortable(optionsRef.current, column)) return;
|
|
1095
1375
|
|
|
1096
|
-
const currentDirection =
|
|
1376
|
+
const currentDirection =
|
|
1377
|
+
sortStateRef.current.columnName === column.name
|
|
1378
|
+
? sortStateRef.current.direction
|
|
1379
|
+
: SORT_DIRECTIONS.none;
|
|
1097
1380
|
const nextDirection =
|
|
1098
1381
|
currentDirection === SORT_DIRECTIONS.none
|
|
1099
1382
|
? SORT_DIRECTIONS.asc
|
|
@@ -1113,7 +1396,7 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1113
1396
|
(updater) => setActiveFilters((current) => updater(current)),
|
|
1114
1397
|
() => activeFiltersRef.current,
|
|
1115
1398
|
columnName,
|
|
1116
|
-
value
|
|
1399
|
+
value,
|
|
1117
1400
|
);
|
|
1118
1401
|
}, []);
|
|
1119
1402
|
|
|
@@ -1140,126 +1423,151 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1140
1423
|
}));
|
|
1141
1424
|
}, []);
|
|
1142
1425
|
|
|
1143
|
-
const focusCellFn = useCallback(
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1426
|
+
const focusCellFn = useCallback(
|
|
1427
|
+
(row: GridRow, column: GridColumnDef, triggerEvent?: Event | KeyboardEvent | null): void => {
|
|
1428
|
+
const nextFocusResult = buildGridFocusCellResult({
|
|
1429
|
+
currentFocusedCell: focusedCellRef.current,
|
|
1430
|
+
currentEditingCell: editingCellRef.current,
|
|
1431
|
+
rowId: row.id,
|
|
1432
|
+
columnName: column.name,
|
|
1433
|
+
shouldEditOnFocus: shouldEditOnFocusFn(column),
|
|
1434
|
+
isCellEditable: isCellEditable(row, column, triggerEvent),
|
|
1435
|
+
});
|
|
1436
|
+
setFocusedCell(nextFocusResult.focusedCell);
|
|
1153
1437
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1438
|
+
if (nextFocusResult.shouldBeginEdit) {
|
|
1439
|
+
startCellEditFn(row, column, triggerEvent);
|
|
1440
|
+
}
|
|
1441
|
+
},
|
|
1442
|
+
[isCellEditable, shouldEditOnFocusFn, startCellEditFn],
|
|
1443
|
+
);
|
|
1158
1444
|
|
|
1159
|
-
const handleCellKeyDownFn = useCallback(
|
|
1160
|
-
|
|
1445
|
+
const handleCellKeyDownFn = useCallback(
|
|
1446
|
+
(row: GridRow, column: GridColumnDef, event: React.KeyboardEvent): void => {
|
|
1447
|
+
focusCellFn(row, column, event.nativeEvent);
|
|
1161
1448
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
event.preventDefault();
|
|
1165
|
-
moveFocusFn(row, column, 'left', event.nativeEvent);
|
|
1166
|
-
return;
|
|
1167
|
-
case 'ArrowRight':
|
|
1168
|
-
event.preventDefault();
|
|
1169
|
-
moveFocusFn(row, column, 'right', event.nativeEvent);
|
|
1170
|
-
return;
|
|
1171
|
-
case 'ArrowUp':
|
|
1172
|
-
event.preventDefault();
|
|
1173
|
-
moveFocusFn(row, column, 'up', event.nativeEvent);
|
|
1174
|
-
return;
|
|
1175
|
-
case 'ArrowDown':
|
|
1176
|
-
event.preventDefault();
|
|
1177
|
-
moveFocusFn(row, column, 'down', event.nativeEvent);
|
|
1178
|
-
return;
|
|
1179
|
-
case 'Tab':
|
|
1180
|
-
event.preventDefault();
|
|
1181
|
-
moveFocusFn(row, column, event.shiftKey ? 'left' : 'right', event.nativeEvent);
|
|
1182
|
-
return;
|
|
1183
|
-
case 'Enter':
|
|
1184
|
-
event.preventDefault();
|
|
1185
|
-
moveFocusFn(row, column, event.shiftKey ? 'up' : 'down', event.nativeEvent);
|
|
1186
|
-
return;
|
|
1187
|
-
case 'F2':
|
|
1188
|
-
event.preventDefault();
|
|
1189
|
-
if (isCellEditable(row, column, event.nativeEvent)) {
|
|
1190
|
-
startCellEditFn(row, column, event.nativeEvent);
|
|
1191
|
-
}
|
|
1192
|
-
return;
|
|
1193
|
-
case 'Backspace':
|
|
1194
|
-
case 'Delete':
|
|
1195
|
-
if (isCellEditable(row, column, event.nativeEvent)) {
|
|
1449
|
+
switch (event.key) {
|
|
1450
|
+
case 'ArrowLeft':
|
|
1196
1451
|
event.preventDefault();
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1452
|
+
moveFocusFn(row, column, 'left', event.nativeEvent);
|
|
1453
|
+
return;
|
|
1454
|
+
case 'ArrowRight':
|
|
1455
|
+
event.preventDefault();
|
|
1456
|
+
moveFocusFn(row, column, 'right', event.nativeEvent);
|
|
1457
|
+
return;
|
|
1458
|
+
case 'ArrowUp':
|
|
1459
|
+
event.preventDefault();
|
|
1460
|
+
moveFocusFn(row, column, 'up', event.nativeEvent);
|
|
1461
|
+
return;
|
|
1462
|
+
case 'ArrowDown':
|
|
1463
|
+
event.preventDefault();
|
|
1464
|
+
moveFocusFn(row, column, 'down', event.nativeEvent);
|
|
1465
|
+
return;
|
|
1466
|
+
case 'Tab':
|
|
1467
|
+
event.preventDefault();
|
|
1468
|
+
moveFocusFn(row, column, event.shiftKey ? 'left' : 'right', event.nativeEvent);
|
|
1469
|
+
return;
|
|
1470
|
+
case 'Enter':
|
|
1471
|
+
event.preventDefault();
|
|
1472
|
+
moveFocusFn(row, column, event.shiftKey ? 'up' : 'down', event.nativeEvent);
|
|
1473
|
+
return;
|
|
1474
|
+
case 'F2':
|
|
1475
|
+
event.preventDefault();
|
|
1476
|
+
if (isCellEditable(row, column, event.nativeEvent)) {
|
|
1477
|
+
startCellEditFn(row, column, event.nativeEvent);
|
|
1478
|
+
}
|
|
1479
|
+
return;
|
|
1480
|
+
case 'Backspace':
|
|
1481
|
+
case 'Delete':
|
|
1482
|
+
if (isCellEditable(row, column, event.nativeEvent)) {
|
|
1483
|
+
event.preventDefault();
|
|
1484
|
+
startCellEditFn(row, column, event.nativeEvent, '');
|
|
1485
|
+
}
|
|
1486
|
+
return;
|
|
1487
|
+
default:
|
|
1488
|
+
break;
|
|
1489
|
+
}
|
|
1203
1490
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1491
|
+
if (
|
|
1492
|
+
isPrintableGridKey(event.key, event.ctrlKey, event.metaKey, event.altKey) &&
|
|
1493
|
+
isCellEditable(row, column, event.nativeEvent)
|
|
1494
|
+
) {
|
|
1495
|
+
event.preventDefault();
|
|
1496
|
+
startCellEditFn(row, column, event.nativeEvent, event.key);
|
|
1497
|
+
}
|
|
1498
|
+
},
|
|
1499
|
+
[focusCellFn, moveFocusFn, isCellEditable, startCellEditFn],
|
|
1500
|
+
);
|
|
1209
1501
|
|
|
1210
|
-
const handleCellDoubleClickFn = useCallback(
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1502
|
+
const handleCellDoubleClickFn = useCallback(
|
|
1503
|
+
(row: GridRow, column: GridColumnDef, event: React.MouseEvent): void => {
|
|
1504
|
+
focusCellFn(row, column, event.nativeEvent);
|
|
1505
|
+
if (isCellEditable(row, column, event.nativeEvent)) {
|
|
1506
|
+
startCellEditFn(row, column, event.nativeEvent);
|
|
1507
|
+
}
|
|
1508
|
+
},
|
|
1509
|
+
[focusCellFn, isCellEditable, startCellEditFn],
|
|
1510
|
+
);
|
|
1216
1511
|
|
|
1217
1512
|
const updateEditingValueFn = useCallback((value: string): void => {
|
|
1218
1513
|
setEditingValue(value);
|
|
1219
1514
|
}, []);
|
|
1220
1515
|
|
|
1221
|
-
const handleEditorKeyDownFn = useCallback(
|
|
1222
|
-
|
|
1223
|
-
event.
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
event.
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
event.
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1516
|
+
const handleEditorKeyDownFn = useCallback(
|
|
1517
|
+
(event: React.KeyboardEvent): void => {
|
|
1518
|
+
if (event.key === 'Escape') {
|
|
1519
|
+
event.preventDefault();
|
|
1520
|
+
cancelCellEditFn();
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1523
|
+
if (event.key === 'Enter') {
|
|
1524
|
+
event.preventDefault();
|
|
1525
|
+
commitCellEditFn(event.shiftKey ? 'up' : 'down');
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
if (event.key === 'Tab') {
|
|
1529
|
+
event.preventDefault();
|
|
1530
|
+
commitCellEditFn(event.shiftKey ? 'left' : 'right');
|
|
1531
|
+
}
|
|
1532
|
+
},
|
|
1533
|
+
[cancelCellEditFn, commitCellEditFn],
|
|
1534
|
+
);
|
|
1237
1535
|
|
|
1238
|
-
const handleEditorBlurFn = useCallback(
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1536
|
+
const handleEditorBlurFn = useCallback(
|
|
1537
|
+
(event: React.FocusEvent): void => {
|
|
1538
|
+
const ec = editingCellRef.current;
|
|
1539
|
+
const target = event.target as HTMLElement | null;
|
|
1540
|
+
if (!ec || !target) return;
|
|
1541
|
+
if (target.dataset['rowId'] !== ec.rowId || target.dataset['colName'] !== ec.columnName)
|
|
1542
|
+
return;
|
|
1543
|
+
commitCellEditFn(undefined, false);
|
|
1544
|
+
},
|
|
1545
|
+
[commitCellEditFn],
|
|
1546
|
+
);
|
|
1245
1547
|
|
|
1246
|
-
const toggleRowExpansionFn = useCallback(
|
|
1247
|
-
event
|
|
1248
|
-
|
|
1249
|
-
|
|
1548
|
+
const toggleRowExpansionFn = useCallback(
|
|
1549
|
+
(row: GridRow, event?: React.MouseEvent): void => {
|
|
1550
|
+
event?.stopPropagation();
|
|
1551
|
+
toggleRowExpansionByRefFn(row);
|
|
1552
|
+
},
|
|
1553
|
+
[toggleRowExpansionByRefFn],
|
|
1554
|
+
);
|
|
1250
1555
|
|
|
1251
|
-
const toggleTreeRowFn = useCallback(
|
|
1252
|
-
event
|
|
1253
|
-
|
|
1254
|
-
|
|
1556
|
+
const toggleTreeRowFn = useCallback(
|
|
1557
|
+
(row: GridRow, event?: React.MouseEvent): void => {
|
|
1558
|
+
event?.stopPropagation();
|
|
1559
|
+
toggleTreeRowByRefFn(row);
|
|
1560
|
+
},
|
|
1561
|
+
[toggleTreeRowByRefFn],
|
|
1562
|
+
);
|
|
1255
1563
|
|
|
1256
1564
|
const moveColumnFn = useCallback((fromIndex: number, toIndex: number): void => {
|
|
1257
1565
|
moveGridColumnCommand(
|
|
1258
1566
|
gridApiRef.current!,
|
|
1259
|
-
FEATURE_COLUMN_MOVING &&
|
|
1567
|
+
FEATURE_COLUMN_MOVING && optionsRef.current.enableColumnMoving === true,
|
|
1260
1568
|
(updater) => setColumnOrder((current) => updater(current)),
|
|
1261
1569
|
fromIndex,
|
|
1262
|
-
toIndex
|
|
1570
|
+
toIndex,
|
|
1263
1571
|
);
|
|
1264
1572
|
}, []);
|
|
1265
1573
|
|
|
@@ -1271,9 +1579,12 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1271
1579
|
seekPageFn(getCurrentPageValueFn() - 1);
|
|
1272
1580
|
}, [seekPageFn, getCurrentPageValueFn]);
|
|
1273
1581
|
|
|
1274
|
-
const onPageSizeChangeFn = useCallback(
|
|
1275
|
-
|
|
1276
|
-
|
|
1582
|
+
const onPageSizeChangeFn = useCallback(
|
|
1583
|
+
(value: string): void => {
|
|
1584
|
+
setPaginationPageSizeFn(Number(value));
|
|
1585
|
+
},
|
|
1586
|
+
[setPaginationPageSizeFn],
|
|
1587
|
+
);
|
|
1277
1588
|
|
|
1278
1589
|
const onViewportScrollFn = useCallback((startIndex: number): void => {
|
|
1279
1590
|
if (!scrollingRef.current) {
|
|
@@ -1290,11 +1601,11 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1290
1601
|
raiseGridScrollEnd(gridApiRef.current!);
|
|
1291
1602
|
}, 120);
|
|
1292
1603
|
|
|
1293
|
-
const isInfiniteScrollEnabled =
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1604
|
+
const isInfiniteScrollEnabled =
|
|
1605
|
+
FEATURE_INFINITE_SCROLL &&
|
|
1606
|
+
(optionsRef.current.infiniteScrollRowsFromEnd !== undefined ||
|
|
1607
|
+
optionsRef.current.infiniteScrollUp === true ||
|
|
1608
|
+
optionsRef.current.infiniteScrollDown !== undefined);
|
|
1298
1609
|
|
|
1299
1610
|
maybeRequestInfiniteScrollCommand(gridApiRef.current!, {
|
|
1300
1611
|
enabled: isInfiniteScrollEnabled,
|
|
@@ -1302,7 +1613,10 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1302
1613
|
state: infiniteScrollStateRef.current,
|
|
1303
1614
|
startIndex,
|
|
1304
1615
|
visibleRows: pipelineRef.current.visibleRows.length,
|
|
1305
|
-
viewportRows:
|
|
1616
|
+
viewportRows: computeViewportRows(
|
|
1617
|
+
optionsRef.current.viewportHeight,
|
|
1618
|
+
optionsRef.current.rowHeight,
|
|
1619
|
+
),
|
|
1306
1620
|
threshold: optionsRef.current.infiniteScrollRowsFromEnd ?? 20,
|
|
1307
1621
|
setState: (state) => setInfiniteScrollState(state),
|
|
1308
1622
|
});
|
|
@@ -1410,5 +1724,12 @@ export function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridA
|
|
|
1410
1724
|
runBenchmark: runBenchmarkFn,
|
|
1411
1725
|
exportCsv: exportCsvFn,
|
|
1412
1726
|
onViewportScroll: onViewportScrollFn,
|
|
1727
|
+
// Pinning
|
|
1728
|
+
isPinned: isPinnedFn,
|
|
1729
|
+
pinnedOffset: pinnedOffsetFn,
|
|
1730
|
+
isPinningEnabled: isPinningEnabledFn,
|
|
1731
|
+
isColumnPinnable: isColumnPinnableFn,
|
|
1732
|
+
togglePin: togglePinFn,
|
|
1733
|
+
pinningFeature: FEATURE_PINNING,
|
|
1413
1734
|
};
|
|
1414
1735
|
}
|