@linzjs/step-ag-grid 14.1.1 → 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 +300 -90
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Grid.tsx +185 -27
- 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 -36
- 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,20 +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
|
-
|
|
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
|
+
};
|
|
354
362
|
},
|
|
355
363
|
[columnApi],
|
|
356
364
|
);
|
|
@@ -359,26 +367,83 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
359
367
|
* Resize columns to fit container
|
|
360
368
|
*/
|
|
361
369
|
const sizeColumnsToFit = useCallback((): void => {
|
|
362
|
-
|
|
363
|
-
gridApi.sizeColumnsToFit();
|
|
364
|
-
}
|
|
370
|
+
gridApi && gridApi.sizeColumnsToFit();
|
|
365
371
|
}, [gridApi]);
|
|
366
372
|
|
|
367
373
|
const stopEditing = useCallback((): void => {
|
|
374
|
+
if (!gridApi) return;
|
|
368
375
|
if (prePopupFocusedCell.current) {
|
|
369
376
|
gridApi?.setFocusedCell(prePopupFocusedCell.current.rowIndex, prePopupFocusedCell.current.column);
|
|
370
377
|
}
|
|
371
|
-
|
|
372
|
-
}, [gridApi
|
|
378
|
+
gridApi.stopEditing();
|
|
379
|
+
}, [gridApi]);
|
|
373
380
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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;
|
|
380
445
|
},
|
|
381
|
-
[
|
|
446
|
+
[gridApi, prePopupOps],
|
|
382
447
|
);
|
|
383
448
|
|
|
384
449
|
const updatingCells = useCallback(
|
|
@@ -393,6 +458,7 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
393
458
|
const selectedRows = props.selectedRows;
|
|
394
459
|
|
|
395
460
|
let ok = false;
|
|
461
|
+
|
|
396
462
|
await modifyUpdating(
|
|
397
463
|
props.field ?? "",
|
|
398
464
|
selectedRows.map((data) => data.id),
|
|
@@ -424,19 +490,20 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
424
490
|
// Only focus next cell if user hasn't already manually changed focus
|
|
425
491
|
const postPopupFocusedCell = gridApi.getFocusedCell();
|
|
426
492
|
if (
|
|
427
|
-
tabDirection &&
|
|
428
493
|
prePopupFocusedCell.current &&
|
|
429
494
|
postPopupFocusedCell &&
|
|
430
495
|
prePopupFocusedCell.current.rowIndex == postPopupFocusedCell.rowIndex &&
|
|
431
496
|
prePopupFocusedCell.current.column.getColId() == postPopupFocusedCell.column.getColId()
|
|
432
497
|
) {
|
|
433
|
-
|
|
498
|
+
if (!tabDirection || selectNextEditableCell(tabDirection)) {
|
|
499
|
+
cellEditingCompleteCallbackRef.current && cellEditingCompleteCallbackRef.current();
|
|
500
|
+
}
|
|
434
501
|
}
|
|
435
502
|
|
|
436
503
|
return ok;
|
|
437
504
|
});
|
|
438
505
|
},
|
|
439
|
-
[gridApiOp, modifyUpdating,
|
|
506
|
+
[gridApiOp, modifyUpdating, selectNextEditableCell],
|
|
440
507
|
);
|
|
441
508
|
|
|
442
509
|
const redrawRows = useCallback(
|
|
@@ -552,8 +619,9 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
552
619
|
ensureRowVisible,
|
|
553
620
|
ensureSelectedRowIsVisible,
|
|
554
621
|
sizeColumnsToFit,
|
|
555
|
-
|
|
622
|
+
autoSizeColumns,
|
|
556
623
|
stopEditing,
|
|
624
|
+
cancelEdit,
|
|
557
625
|
updatingCells,
|
|
558
626
|
redrawRows,
|
|
559
627
|
externallySelectedItemsAreInSync,
|
|
@@ -564,6 +632,7 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
564
632
|
isExternalFilterPresent,
|
|
565
633
|
doesExternalFilterPass,
|
|
566
634
|
downloadCsv,
|
|
635
|
+
setOnCellEditingComplete,
|
|
567
636
|
}}
|
|
568
637
|
>
|
|
569
638
|
{props.children}
|
|
@@ -612,6 +681,7 @@ export const downloadCsvUseValueFormattersProcessCellCallback = (params: Process
|
|
|
612
681
|
}
|
|
613
682
|
|
|
614
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
|
|
615
685
|
if (params.value != null && typeof result !== "string") {
|
|
616
686
|
console.error(`downloadCsv: valueFormatter is returning non string values, colDef:", colId: ${colDef.colId}`);
|
|
617
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);
|