@linzjs/step-ag-grid 14.1.2 → 14.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/src/components/Grid.d.ts +5 -0
- package/dist/src/components/GridCell.d.ts +1 -0
- package/dist/src/contexts/GridContext.d.ts +11 -5
- package/dist/src/contexts/GridUpdatingContext.d.ts +1 -0
- package/dist/step-ag-grid.esm.js +293 -95
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Grid.tsx +178 -31
- package/src/components/GridCell.tsx +6 -2
- package/src/components/GridPopoverHook.tsx +4 -4
- package/src/components/gridForm/GridFormDropDown.tsx +1 -8
- package/src/components/gridPopoverEdit/GridPopoverMenu.tsx +1 -0
- package/src/contexts/GridContext.tsx +19 -3
- package/src/contexts/GridContextProvider.tsx +106 -41
- package/src/contexts/GridUpdatingContext.tsx +5 -0
- package/src/contexts/GridUpdatingContextProvider.tsx +8 -3
- package/src/react-menu3/components/MenuList.tsx +21 -1
- package/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx +7 -1
- package/src/stories/grid/GridPopoverEditDropDown.stories.tsx +5 -0
- package/src/stories/grid/interactions/GridKeyboardInteractions.stories.tsx +3 -1
|
@@ -8,7 +8,7 @@ import { ReactElement, ReactNode, useCallback, useContext, useEffect, useMemo, u
|
|
|
8
8
|
|
|
9
9
|
import { ColDefT, GridBaseRow } from "../components";
|
|
10
10
|
import { isNotEmpty, sanitiseFileName, wait } from "../utils/util";
|
|
11
|
-
import { GridContext, GridFilterExternal } from "./GridContext";
|
|
11
|
+
import { AutoSizeColumnsProps, AutoSizeColumnsResult, GridContext, GridFilterExternal } from "./GridContext";
|
|
12
12
|
import { GridUpdatingContext } from "./GridUpdatingContext";
|
|
13
13
|
|
|
14
14
|
interface GridContextProps {
|
|
@@ -64,14 +64,13 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
64
64
|
*/
|
|
65
65
|
const ensureRowVisible = useCallback(
|
|
66
66
|
(id: number | string): boolean => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
67
|
+
if (!gridApi) return false;
|
|
68
|
+
const node = gridApi?.getRowNode(`${id}`);
|
|
69
|
+
if (!node) return false;
|
|
70
|
+
defer(() => gridApi.ensureNodeVisible(node));
|
|
71
|
+
return true;
|
|
73
72
|
},
|
|
74
|
-
[
|
|
73
|
+
[gridApi],
|
|
75
74
|
);
|
|
76
75
|
|
|
77
76
|
const getFirstRowId = useCallback((): number => {
|
|
@@ -211,17 +210,16 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
211
210
|
);
|
|
212
211
|
const firstNode = rowsThatNeedSelecting[0];
|
|
213
212
|
if (firstNode) {
|
|
214
|
-
gridApi.ensureNodeVisible(firstNode);
|
|
213
|
+
defer(() => gridApi.ensureNodeVisible(firstNode));
|
|
215
214
|
const colDefs = gridApi.getColumnDefs();
|
|
216
|
-
if (colDefs
|
|
215
|
+
if (colDefs?.length) {
|
|
217
216
|
const col = colDefs[0] as ColDef; // We don't support ColGroupDef
|
|
218
217
|
const rowIndex = firstNode.rowIndex;
|
|
219
218
|
if (rowIndex != null && col != null) {
|
|
220
219
|
const colId = col.colId;
|
|
221
220
|
// We need to make sure we aren't currently editing a cell otherwise tests will fail
|
|
222
221
|
// as they will start to edit the cell before this stuff has a chance to run
|
|
223
|
-
colId
|
|
224
|
-
defer(() => isEmpty(gridApi.getEditingCells()) && gridApi.setFocusedCell(rowIndex, colId));
|
|
222
|
+
colId && defer(() => isEmpty(gridApi.getEditingCells()) && gridApi.setFocusedCell(rowIndex, colId));
|
|
225
223
|
}
|
|
226
224
|
}
|
|
227
225
|
}
|
|
@@ -337,25 +335,30 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
337
335
|
gridApiOp((gridApi) => {
|
|
338
336
|
const selectedNodes = gridApi.getSelectedNodes();
|
|
339
337
|
if (isEmpty(selectedNodes)) return;
|
|
340
|
-
gridApi.ensureNodeVisible(last(selectedNodes));
|
|
338
|
+
defer(() => gridApi.ensureNodeVisible(last(selectedNodes)));
|
|
341
339
|
});
|
|
342
340
|
}, [gridApiOp]);
|
|
343
341
|
|
|
344
342
|
/**
|
|
345
343
|
* Resize columns to fit container
|
|
346
344
|
*/
|
|
347
|
-
const
|
|
348
|
-
({ skipHeader }
|
|
349
|
-
if (columnApi)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
return
|
|
345
|
+
const autoSizeColumns = useCallback(
|
|
346
|
+
({ skipHeader, colIds, userSizedColIds }: AutoSizeColumnsProps = {}): AutoSizeColumnsResult => {
|
|
347
|
+
if (!columnApi) return null;
|
|
348
|
+
const colIdsSet = colIds instanceof Set ? colIds : new Set<string>(colIds ?? []);
|
|
349
|
+
columnApi.getColumnState().forEach((col) => {
|
|
350
|
+
const colId = col.colId;
|
|
351
|
+
if ((isEmpty(colIdsSet) || colIdsSet.has(colId)) && !userSizedColIds?.has(colId)) {
|
|
352
|
+
columnApi.autoSizeColumn(colId, skipHeader);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
width: sumBy(
|
|
358
|
+
columnApi.getColumnState().filter((col) => !col.hide),
|
|
359
|
+
"width",
|
|
360
|
+
),
|
|
361
|
+
};
|
|
359
362
|
},
|
|
360
363
|
[columnApi],
|
|
361
364
|
);
|
|
@@ -364,26 +367,83 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
364
367
|
* Resize columns to fit container
|
|
365
368
|
*/
|
|
366
369
|
const sizeColumnsToFit = useCallback((): void => {
|
|
367
|
-
|
|
368
|
-
gridApi.sizeColumnsToFit();
|
|
369
|
-
}
|
|
370
|
+
gridApi && gridApi.sizeColumnsToFit();
|
|
370
371
|
}, [gridApi]);
|
|
371
372
|
|
|
372
373
|
const stopEditing = useCallback((): void => {
|
|
374
|
+
if (!gridApi) return;
|
|
373
375
|
if (prePopupFocusedCell.current) {
|
|
374
376
|
gridApi?.setFocusedCell(prePopupFocusedCell.current.rowIndex, prePopupFocusedCell.current.column);
|
|
375
377
|
}
|
|
376
|
-
|
|
377
|
-
}, [gridApi
|
|
378
|
+
gridApi.stopEditing();
|
|
379
|
+
}, [gridApi]);
|
|
378
380
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
381
|
+
/**
|
|
382
|
+
* This differs from stopEdit in that it will also invoke cellEditingCompleteCallback
|
|
383
|
+
*/
|
|
384
|
+
const cancelEdit = useCallback((): void => {
|
|
385
|
+
stopEditing();
|
|
386
|
+
cellEditingCompleteCallbackRef.current && cellEditingCompleteCallbackRef.current();
|
|
387
|
+
}, [stopEditing]);
|
|
388
|
+
|
|
389
|
+
const cellEditingCompleteCallbackRef = useRef<() => void>();
|
|
390
|
+
const setOnCellEditingComplete = useCallback((cellEditingCompleteCallback: (() => void) | undefined) => {
|
|
391
|
+
cellEditingCompleteCallbackRef.current = cellEditingCompleteCallback;
|
|
392
|
+
}, []);
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Returns true if an editable cell on same row was selected, else false.
|
|
396
|
+
*/
|
|
397
|
+
const selectNextEditableCell = useCallback(
|
|
398
|
+
(tabDirection: -1 | 1): boolean => {
|
|
399
|
+
// Pretend it succeeded to prevent unwanted cellEditingCompleteCallback
|
|
400
|
+
if (!gridApi) return true;
|
|
401
|
+
|
|
402
|
+
const focusedCellIsEditable = () => {
|
|
403
|
+
const focusedCell = gridApi.getFocusedCell();
|
|
404
|
+
const nextColumn = focusedCell?.column;
|
|
405
|
+
const nextColDef = nextColumn?.getColDef();
|
|
406
|
+
const rowNode = focusedCell && gridApi.getDisplayedRowAtIndex(focusedCell?.rowIndex);
|
|
407
|
+
return (
|
|
408
|
+
!!(rowNode && nextColumn && nextColDef) &&
|
|
409
|
+
nextColumn.isCellEditable(rowNode) &&
|
|
410
|
+
!nextColDef.cellEditorParams?.preventAutoEdit &&
|
|
411
|
+
!nextColDef.cellRendererParams?.editAction
|
|
412
|
+
);
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
let foundEditableCell = false;
|
|
416
|
+
|
|
417
|
+
// Just in case I've missed something, we don't want the loop to hang everything
|
|
418
|
+
let maxIterations = 50;
|
|
419
|
+
let preRow: CellPosition | null = null;
|
|
420
|
+
let postRow: CellPosition | null = null;
|
|
421
|
+
do {
|
|
422
|
+
preRow = gridApi.getFocusedCell();
|
|
423
|
+
tabDirection === 1 ? gridApi.tabToNextCell() : gridApi.tabToPreviousCell();
|
|
424
|
+
postRow = gridApi.getFocusedCell();
|
|
425
|
+
foundEditableCell = focusedCellIsEditable();
|
|
426
|
+
} while (
|
|
427
|
+
preRow?.rowIndex === postRow?.rowIndex &&
|
|
428
|
+
preRow?.column !== postRow?.column &&
|
|
429
|
+
!foundEditableCell &&
|
|
430
|
+
maxIterations-- > 0
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
if (foundEditableCell) {
|
|
434
|
+
prePopupOps();
|
|
435
|
+
const focusedCell = gridApi?.getFocusedCell();
|
|
436
|
+
if (focusedCell) {
|
|
437
|
+
gridApi.startEditingCell({
|
|
438
|
+
rowIndex: focusedCell.rowIndex,
|
|
439
|
+
colKey: focusedCell.column.getColId(),
|
|
440
|
+
});
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return true;
|
|
385
445
|
},
|
|
386
|
-
[
|
|
446
|
+
[gridApi, prePopupOps],
|
|
387
447
|
);
|
|
388
448
|
|
|
389
449
|
const updatingCells = useCallback(
|
|
@@ -398,6 +458,7 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
398
458
|
const selectedRows = props.selectedRows;
|
|
399
459
|
|
|
400
460
|
let ok = false;
|
|
461
|
+
|
|
401
462
|
await modifyUpdating(
|
|
402
463
|
props.field ?? "",
|
|
403
464
|
selectedRows.map((data) => data.id),
|
|
@@ -429,19 +490,20 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
429
490
|
// Only focus next cell if user hasn't already manually changed focus
|
|
430
491
|
const postPopupFocusedCell = gridApi.getFocusedCell();
|
|
431
492
|
if (
|
|
432
|
-
tabDirection &&
|
|
433
493
|
prePopupFocusedCell.current &&
|
|
434
494
|
postPopupFocusedCell &&
|
|
435
495
|
prePopupFocusedCell.current.rowIndex == postPopupFocusedCell.rowIndex &&
|
|
436
496
|
prePopupFocusedCell.current.column.getColId() == postPopupFocusedCell.column.getColId()
|
|
437
497
|
) {
|
|
438
|
-
|
|
498
|
+
if (!tabDirection || selectNextEditableCell(tabDirection)) {
|
|
499
|
+
cellEditingCompleteCallbackRef.current && cellEditingCompleteCallbackRef.current();
|
|
500
|
+
}
|
|
439
501
|
}
|
|
440
502
|
|
|
441
503
|
return ok;
|
|
442
504
|
});
|
|
443
505
|
},
|
|
444
|
-
[gridApiOp, modifyUpdating,
|
|
506
|
+
[gridApiOp, modifyUpdating, selectNextEditableCell],
|
|
445
507
|
);
|
|
446
508
|
|
|
447
509
|
const redrawRows = useCallback(
|
|
@@ -557,8 +619,9 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
557
619
|
ensureRowVisible,
|
|
558
620
|
ensureSelectedRowIsVisible,
|
|
559
621
|
sizeColumnsToFit,
|
|
560
|
-
|
|
622
|
+
autoSizeColumns,
|
|
561
623
|
stopEditing,
|
|
624
|
+
cancelEdit,
|
|
562
625
|
updatingCells,
|
|
563
626
|
redrawRows,
|
|
564
627
|
externallySelectedItemsAreInSync,
|
|
@@ -569,6 +632,7 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
569
632
|
isExternalFilterPresent,
|
|
570
633
|
doesExternalFilterPass,
|
|
571
634
|
downloadCsv,
|
|
635
|
+
setOnCellEditingComplete,
|
|
572
636
|
}}
|
|
573
637
|
>
|
|
574
638
|
{props.children}
|
|
@@ -617,6 +681,7 @@ export const downloadCsvUseValueFormattersProcessCellCallback = (params: Process
|
|
|
617
681
|
}
|
|
618
682
|
|
|
619
683
|
const result = valueFormatter({ ...params, data: params.node?.data, colDef } as ValueFormatterParams);
|
|
684
|
+
// type may not be string due to casting, leave the type check in
|
|
620
685
|
if (params.value != null && typeof result !== "string") {
|
|
621
686
|
console.error(`downloadCsv: valueFormatter is returning non string values, colDef:", colId: ${colDef.colId}`);
|
|
622
687
|
}
|
|
@@ -2,6 +2,7 @@ import { createContext } from "react";
|
|
|
2
2
|
|
|
3
3
|
export type GridUpdatingContextType = {
|
|
4
4
|
checkUpdating: (fields: string | string[], id: number | string) => boolean;
|
|
5
|
+
isUpdating: () => boolean;
|
|
5
6
|
modifyUpdating: (
|
|
6
7
|
fields: string | string[],
|
|
7
8
|
ids: (number | string)[],
|
|
@@ -15,6 +16,10 @@ export const GridUpdatingContext = createContext<GridUpdatingContextType>({
|
|
|
15
16
|
console.error("Missing GridUpdatingContext");
|
|
16
17
|
return false;
|
|
17
18
|
},
|
|
19
|
+
isUpdating: () => {
|
|
20
|
+
console.error("Missing GridUpdatingContext");
|
|
21
|
+
return false;
|
|
22
|
+
},
|
|
18
23
|
modifyUpdating: async () => {
|
|
19
24
|
console.error("Missing GridUpdatingContext");
|
|
20
25
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { castArray, flatten, remove } from "lodash-es";
|
|
1
|
+
import { castArray, flatten, isEmpty, remove } from "lodash-es";
|
|
2
2
|
import { ReactNode, useRef, useState } from "react";
|
|
3
3
|
|
|
4
4
|
import { GridUpdatingContext } from "./GridUpdatingContext";
|
|
@@ -21,12 +21,17 @@ export const GridUpdatingContextProvider = (props: UpdatingContextProviderProps)
|
|
|
21
21
|
const resetUpdating = () => {
|
|
22
22
|
const mergedUpdatingBlocks: GridUpdatingContextStatus = {};
|
|
23
23
|
for (const key in updatingBlocks.current) {
|
|
24
|
-
|
|
24
|
+
const arr = flatten(updatingBlocks.current[key]);
|
|
25
|
+
if (arr.length) {
|
|
26
|
+
mergedUpdatingBlocks[key] = arr;
|
|
27
|
+
}
|
|
25
28
|
}
|
|
26
29
|
updating.current = mergedUpdatingBlocks;
|
|
27
30
|
setUpdatedDep((updatedDep) => updatedDep + 1);
|
|
28
31
|
};
|
|
29
32
|
|
|
33
|
+
const isUpdating = () => !isEmpty(updating.current);
|
|
34
|
+
|
|
30
35
|
const modifyUpdating = async (
|
|
31
36
|
fields: string | string[],
|
|
32
37
|
ids: (number | string)[],
|
|
@@ -50,7 +55,7 @@ export const GridUpdatingContextProvider = (props: UpdatingContextProviderProps)
|
|
|
50
55
|
castArray(fields).some((f) => updating.current[f]?.includes(id));
|
|
51
56
|
|
|
52
57
|
return (
|
|
53
|
-
<GridUpdatingContext.Provider value={{ modifyUpdating, checkUpdating, updatedDep }}>
|
|
58
|
+
<GridUpdatingContext.Provider value={{ modifyUpdating, checkUpdating, isUpdating, updatedDep }}>
|
|
54
59
|
{props.children}
|
|
55
60
|
</GridUpdatingContext.Provider>
|
|
56
61
|
);
|
|
@@ -477,7 +477,27 @@ export const MenuList = ({
|
|
|
477
477
|
top: menuPosition.y,
|
|
478
478
|
}}
|
|
479
479
|
>
|
|
480
|
-
<div
|
|
480
|
+
<div
|
|
481
|
+
ref={focusRef}
|
|
482
|
+
tabIndex={-1}
|
|
483
|
+
style={{ position: "absolute", left: 0, top: 0 }}
|
|
484
|
+
onKeyDown={(event) => {
|
|
485
|
+
if (event.key == "Tab") {
|
|
486
|
+
event.preventDefault();
|
|
487
|
+
event.stopPropagation();
|
|
488
|
+
safeCall(onClose, {
|
|
489
|
+
key: event.key,
|
|
490
|
+
shiftKey: event.shiftKey,
|
|
491
|
+
reason:
|
|
492
|
+
event.key === "Tab"
|
|
493
|
+
? event.shiftKey
|
|
494
|
+
? CloseReason.TAB_BACKWARD
|
|
495
|
+
: CloseReason.TAB_FORWARD
|
|
496
|
+
: CloseReason.CLICK,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}}
|
|
500
|
+
/>
|
|
481
501
|
{arrow && (
|
|
482
502
|
<div
|
|
483
503
|
className={_arrowClass}
|
|
@@ -192,7 +192,7 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
|
|
|
192
192
|
setRowData([
|
|
193
193
|
...rowData,
|
|
194
194
|
{
|
|
195
|
-
id: lastRow
|
|
195
|
+
id: (lastRow?.id ?? 0) + 1,
|
|
196
196
|
name: "?",
|
|
197
197
|
nameType: "?",
|
|
198
198
|
numba: "?",
|
|
@@ -212,6 +212,12 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
|
|
|
212
212
|
columnDefs={columnDefs}
|
|
213
213
|
rowData={rowData}
|
|
214
214
|
domLayout={"autoHeight"}
|
|
215
|
+
defaultColDef={{ minWidth: 70 }}
|
|
216
|
+
sizeColumns={"auto"}
|
|
217
|
+
onCellEditingComplete={() => {
|
|
218
|
+
/* eslint-disable-next-line no-console */
|
|
219
|
+
console.log("Cell editing complete");
|
|
220
|
+
}}
|
|
215
221
|
/>
|
|
216
222
|
<ActionButton icon={"ic_add"} name={"Add new row"} inProgressName={"Adding..."} onClick={addRowAction} />
|
|
217
223
|
</>
|
|
@@ -155,6 +155,7 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
|
|
|
155
155
|
{
|
|
156
156
|
field: "position3",
|
|
157
157
|
headerName: "Filtered",
|
|
158
|
+
editable: false,
|
|
158
159
|
},
|
|
159
160
|
{
|
|
160
161
|
multiEdit: true,
|
|
@@ -302,6 +303,10 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
|
|
|
302
303
|
columnDefs={columnDefs}
|
|
303
304
|
rowData={rowData}
|
|
304
305
|
domLayout={"autoHeight"}
|
|
306
|
+
onCellEditingComplete={() => {
|
|
307
|
+
/* eslint-disable-next-line no-console */
|
|
308
|
+
console.log("Cell editing complete");
|
|
309
|
+
}}
|
|
305
310
|
/>
|
|
306
311
|
</GridWrapper>
|
|
307
312
|
);
|
|
@@ -248,7 +248,9 @@ GridKeyboardInteractions.play = async ({ canvasElement }) => {
|
|
|
248
248
|
await test(() => userEvent.keyboard("{Enter}"), "8", "2");
|
|
249
249
|
await test(() => userEvent.tab(), "9", "2");
|
|
250
250
|
userEvent.tab({ shift: true });
|
|
251
|
-
await test(() => userEvent.tab({ shift: true }), "
|
|
251
|
+
await test(() => userEvent.tab({ shift: true }), "6", "2");
|
|
252
|
+
userEvent.keyboard("{Esc}");
|
|
253
|
+
userEvent.tab();
|
|
252
254
|
|
|
253
255
|
userEvent.keyboard("{Enter}");
|
|
254
256
|
await wait(250);
|