@linzjs/step-ag-grid 28.5.1 → 29.0.1

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.
Files changed (44) hide show
  1. package/dist/index.css +11 -7
  2. package/dist/src/components/Grid.d.ts +5 -1
  3. package/dist/src/contexts/GridContext.d.ts +3 -4
  4. package/dist/src/contexts/GridPopoverContext.d.ts +1 -0
  5. package/dist/src/contexts/GridUpdatingContext.d.ts +1 -0
  6. package/dist/src/lui/reactUtils.d.ts +4 -0
  7. package/dist/step-ag-grid.cjs +272 -250
  8. package/dist/step-ag-grid.cjs.map +1 -1
  9. package/dist/step-ag-grid.esm.js +273 -251
  10. package/dist/step-ag-grid.esm.js.map +1 -1
  11. package/package.json +21 -22
  12. package/src/components/Grid.tsx +114 -112
  13. package/src/components/GridCell.tsx +6 -2
  14. package/src/components/GridPopoverHook.tsx +25 -20
  15. package/src/components/gridFilter/useGridFilter.ts +3 -3
  16. package/src/components/gridForm/GridFormTextInput.tsx +6 -2
  17. package/src/components/gridHook/useGridContextMenu.tsx +3 -3
  18. package/src/contexts/GridContext.tsx +11 -14
  19. package/src/contexts/GridContextProvider.tsx +164 -137
  20. package/src/contexts/GridPopoverContext.tsx +2 -0
  21. package/src/contexts/GridPopoverContextProvider.tsx +9 -5
  22. package/src/contexts/GridUpdatingContext.tsx +5 -0
  23. package/src/contexts/GridUpdatingContextProvider.tsx +35 -28
  24. package/src/lui/reactUtils.tsx +24 -0
  25. package/src/react-menu3/components/MenuList.tsx +3 -0
  26. package/src/stories/grid/GridFilterButtons.stories.tsx +1 -1
  27. package/src/stories/grid/GridNonEditableRow.stories.tsx +1 -1
  28. package/src/stories/grid/GridPinnedRow.stories.tsx +1 -1
  29. package/src/stories/grid/GridPopoutContextMenu.stories.tsx +3 -3
  30. package/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx +3 -3
  31. package/src/stories/grid/GridPopoverEditDropDown.stories.tsx +8 -4
  32. package/src/stories/grid/GridPopoverEditMultiSelect.stories.tsx +1 -0
  33. package/src/stories/grid/GridReadOnly.stories.tsx +1 -1
  34. package/src/stories/grid/GridViewList.stories.tsx +1 -1
  35. package/src/stories/grid/gridFormInteraction/GridFormDropDownInteraction.stories.tsx +5 -3
  36. package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +3 -2
  37. package/src/stories/grid/gridFormInteraction/GridFormEditBearingInteraction.stories.tsx +3 -2
  38. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectGridInteraction.stories.tsx +3 -2
  39. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectInteraction.stories.tsx +3 -2
  40. package/src/stories/grid/gridFormInteraction/GridFormPopoverMenuInteraction.stories.tsx +3 -2
  41. package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +3 -2
  42. package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +3 -2
  43. package/src/stories/grid/gridFormStatic/GridFormDropDown.stories.tsx +2 -2
  44. package/src/stories/grid/interactions/GridKeyboardInteractions.stories.tsx +2 -2
@@ -449,9 +449,6 @@ const GridContext = React.createContext({
449
449
  prePopupOps: () => {
450
450
  console.error('no context provider for prePopupOps');
451
451
  },
452
- postPopupOps: () => {
453
- console.error('no context provider for prePopupOps');
454
- },
455
452
  externallySelectedItemsAreInSync: false,
456
453
  setApis: () => {
457
454
  console.error('no context provider for setApis');
@@ -523,15 +520,12 @@ const GridContext = React.createContext({
523
520
  console.error('no context provider for editingCells');
524
521
  return false;
525
522
  },
526
- cancelEdit: () => {
527
- console.error('no context provider for cancelEdit');
528
- },
529
523
  // eslint-disable-next-line @typescript-eslint/require-await
530
524
  startCellEditing: async () => {
531
525
  console.error('no context provider for startCellEditing');
532
526
  },
533
- stopEditing: () => {
534
- console.error('no context provider for stopEditing');
527
+ resetFocusedCellAfterCellEditing: () => {
528
+ console.error('no context provider for resetFocusedCellAfterCellEditing');
535
529
  },
536
530
  // eslint-disable-next-line @typescript-eslint/require-await
537
531
  updatingCells: async () => {
@@ -565,8 +559,11 @@ const GridContext = React.createContext({
565
559
  downloadCsv: () => {
566
560
  console.error('no context provider for downloadCsv');
567
561
  },
568
- setOnCellEditingComplete: () => {
569
- console.error('no context provider for setOnCellEditingComplete');
562
+ onBulkEditingComplete: () => {
563
+ console.error('no context provider for onBulkEditingComplete');
564
+ },
565
+ setOnBulkEditingComplete: () => {
566
+ console.error('no context provider for setOnBulkEditingComplete');
570
567
  },
571
568
  showNoRowsOverlay: () => {
572
569
  console.error('no context provider for showLoadingOverlay');
@@ -575,6 +572,10 @@ const GridContext = React.createContext({
575
572
  const useGridContext = () => React.useContext(GridContext);
576
573
 
577
574
  const GridUpdatingContext = React.createContext({
575
+ anyUpdating: () => {
576
+ console.error('Missing GridUpdatingContext');
577
+ return false;
578
+ },
578
579
  checkUpdating: () => {
579
580
  console.error('Missing GridUpdatingContext');
580
581
  return false;
@@ -1796,6 +1797,9 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
1796
1797
  return;
1797
1798
  }
1798
1799
  const callback = lodashEs.debounce(() => {
1800
+ if (!menuRef.current) {
1801
+ return;
1802
+ }
1799
1803
  const { width, height } = menuRef.current.ownerDocument.body.getBoundingClientRect();
1800
1804
  if (width === 0 || height === 0)
1801
1805
  return;
@@ -2541,7 +2545,7 @@ const SubMenuFr = ({ 'aria-label': ariaLabel, className, disabled, direction, la
2541
2545
  const SubMenu = withHovering('SubMenu', SubMenuFr);
2542
2546
 
2543
2547
  const useGridContextMenu = ({ contextMenuSelectRow, contextMenu: ContextMenu, }) => {
2544
- const { redrawRows, prePopupOps, postPopupOps, getSelectedRows } = React.useContext(GridContext);
2548
+ const { redrawRows, prePopupOps, resetFocusedCellAfterCellEditing, getSelectedRows } = React.useContext(GridContext);
2545
2549
  const [isOpen, setOpen] = React.useState(false);
2546
2550
  const [anchorPoint, setAnchorPoint] = React.useState({ x: 0, y: 0 });
2547
2551
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -2558,8 +2562,8 @@ const useGridContextMenu = ({ contextMenuSelectRow, contextMenu: ContextMenu, })
2558
2562
  const closeMenu = React.useCallback(() => {
2559
2563
  setOpen(false);
2560
2564
  redrawRows();
2561
- postPopupOps();
2562
- }, [postPopupOps, redrawRows]);
2565
+ resetFocusedCellAfterCellEditing();
2566
+ }, [resetFocusedCellAfterCellEditing, redrawRows]);
2563
2567
  const cellContextMenu = React.useCallback((event) => {
2564
2568
  if (contextMenuSelectRow && !event.node.isSelected()) {
2565
2569
  event.node.setSelected(true, true);
@@ -2755,36 +2759,11 @@ const usePostSortRowsHook = ({ setStaleGrid }) => {
2755
2759
  };
2756
2760
 
2757
2761
  agGridCommunity.ModuleRegistry.registerModules([agGridCommunity.AllCommunityModule]);
2758
- let timeOfLastSingleClick = 0;
2759
- let lastClickColId;
2760
- let lastClickRowIndex;
2761
- const resetClickDebounce = () => {
2762
- timeOfLastSingleClick = 0;
2763
- lastClickColId = '';
2764
- lastClickRowIndex = -1;
2765
- };
2766
- /**
2767
- * If click is more than 200ms since last click return true.
2768
- */
2769
- const clickDebounceSkipClick = (colId, rowIndex) => {
2770
- const doubleClickMs = 200;
2771
- if (Date.now() - timeOfLastSingleClick < doubleClickMs &&
2772
- lastClickColId === colId &&
2773
- lastClickRowIndex === rowIndex) {
2774
- // Skipping double click due to single click edit
2775
- return true;
2776
- }
2777
- timeOfLastSingleClick = Date.now();
2778
- lastClickColId = colId;
2779
- lastClickRowIndex = rowIndex;
2780
- return false;
2781
- };
2782
2762
  /**
2783
2763
  * Wrapper for AgGrid to add commonly used functionality.
2784
2764
  */
2785
2765
  const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection = 'multiple', suppressColumnVirtualization = true, theme = 'ag-theme-step-default', sizeColumns = 'auto', selectColumnPinned = 'left', contextMenuSelectRow = false, singleClickEdit = false, rowData, rowHeight = theme === 'ag-theme-step-default' ? 40 : theme === 'ag-theme-step-compact' ? 36 : 40, selectable, ...params }) => {
2786
- const { gridReady, gridRenderState, setApis, ensureRowVisible, getFirstRowId, selectRowsById, focusByRowById, ensureSelectedRowIsVisible, autoSizeColumns, sizeColumnsToFit, externallySelectedItemsAreInSync, setExternallySelectedItemsAreInSync, isExternalFilterPresent, doesExternalFilterPass, setOnCellEditingComplete, getColDef, showNoRowsOverlay, prePopupOps, stopEditing, } = React.useContext(GridContext);
2787
- const { startCellEditing } = useGridContext();
2766
+ const { gridReady, gridRenderState, setApis, ensureRowVisible, getFirstRowId, selectRowsById, focusByRowById, ensureSelectedRowIsVisible, autoSizeColumns, sizeColumnsToFit, externallySelectedItemsAreInSync, setExternallySelectedItemsAreInSync, isExternalFilterPresent, doesExternalFilterPass, setOnBulkEditingComplete, getColDef, showNoRowsOverlay, prePopupOps, startCellEditing, } = useGridContext();
2788
2767
  const { updatedDep, updatingCols } = React.useContext(GridUpdatingContext);
2789
2768
  const gridDivRef = React.useRef(null);
2790
2769
  const lastSelectedIds = React.useRef([]);
@@ -3003,26 +2982,10 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3003
2982
  const onModelUpdated = React.useCallback((event) => {
3004
2983
  event.api.showNoRowsOverlay();
3005
2984
  }, []);
3006
- /**
3007
- * Force-refresh all selected rows to re-run class function, to update selection highlighting
3008
- */
3009
- const refreshSelectedRows = React.useCallback((_event) => {
3010
- // MATT Disabled I don't believe these are needed anymore
3011
- // I've left them here just in case they are
3012
- /*event.api.refreshCells({
3013
- force: true,
3014
- rowNodes: event.api.getSelectedNodes(),
3015
- });*/
3016
- }, []);
3017
2985
  /**
3018
2986
  * Handle double click edit
3019
2987
  */
3020
2988
  const onCellDoubleClick = React.useCallback((event) => {
3021
- if (clickDebounceSkipClick(event.colDef.colId, event.rowIndex)) {
3022
- // the next click will be a single click, we want it to pass
3023
- resetClickDebounce();
3024
- return;
3025
- }
3026
2989
  const editable = fnOrVar(event.colDef?.editable, event);
3027
2990
  if (editable && !invokeEditAction(event)) {
3028
2991
  void startCellEditing({ rowId: event.data.id, colId: event.column.getColId() });
@@ -3034,9 +2997,6 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3034
2997
  const onCellClicked = React.useCallback((event) => {
3035
2998
  const editable = fnOrVar(event.colDef?.editable, event);
3036
2999
  if ((editable && event.colDef.singleClickEdit) ?? singleClickEdit) {
3037
- if (clickDebounceSkipClick(event.colDef.colId, event.rowIndex)) {
3038
- return;
3039
- }
3040
3000
  void startCellEditing({ rowId: event.data.id, colId: event.column.getColId() });
3041
3001
  }
3042
3002
  }, [singleClickEdit, startCellEditing]);
@@ -3132,11 +3092,10 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3132
3092
  React.useEffect(() => {
3133
3093
  const newLoading = !rowData || params.loading === true;
3134
3094
  if (newLoading && !prevLoading.current) {
3135
- stopEditing();
3136
3095
  showNoRowsOverlay();
3137
3096
  }
3138
3097
  prevLoading.current = newLoading;
3139
- }, [params.loading, rowData, showNoRowsOverlay, stopEditing]);
3098
+ }, [params.loading, rowData, showNoRowsOverlay]);
3140
3099
  /**
3141
3100
  * Resize columns to fit if required on window/container resize
3142
3101
  */
@@ -3198,6 +3157,23 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3198
3157
  });
3199
3158
  }
3200
3159
  }, [clearHighlightRowClasses]);
3160
+ const onCellFocused = React.useCallback((event) => {
3161
+ if (!params.onCellFocused || event.rowIndex == null) {
3162
+ return;
3163
+ }
3164
+ const api = event.api;
3165
+ const rowNode = api.getDisplayedRowAtIndex(event.rowIndex);
3166
+ const data = rowNode?.data;
3167
+ const column = event.column;
3168
+ if (!data || !column || typeof column === 'string') {
3169
+ return;
3170
+ }
3171
+ const colDef = column.getColDef();
3172
+ if (!colDef || typeof colDef === 'string') {
3173
+ return;
3174
+ }
3175
+ params.onCellFocused({ colDef, data });
3176
+ }, [params]);
3201
3177
  const onRowDragEnd = React.useCallback((event) => {
3202
3178
  clearHighlightRowClasses();
3203
3179
  gridElementRef.current = undefined;
@@ -3222,57 +3198,76 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3222
3198
  }
3223
3199
  }, [params.setExternalSelectedItems, selectable]);
3224
3200
  // This is setting a ref in the GridContext so won't be triggering an update loop
3225
- setOnCellEditingComplete(params.onCellEditingComplete);
3226
- const selectWidth = params.hideSelectColumn ? 0 : selectable && params.onRowDragEnd ? 76 : 48;
3227
- const headerRowCount = columnDefs.some((c) => c.children) ? 2 : 1;
3201
+ setOnBulkEditingComplete(params.onBulkEditingComplete);
3202
+ const getRowId = React.useCallback((params) => `${params.data.id}`, []);
3203
+ const defaultColDef = React.useMemo(() => ({
3204
+ minWidth: 48,
3205
+ ...lodashEs.omit(params.defaultColDef, ['editable', 'field', 'colId', 'tooltipField']),
3206
+ }), [params.defaultColDef]);
3207
+ const noRowsOverlayComponent = React.useCallback((event) => {
3208
+ const headerRowCount = columnDefs.some((c) => c.children) ? 2 : 1;
3209
+ let rowCount = 0;
3210
+ event.api.forEachNode(() => rowCount++);
3211
+ return (jsxRuntime.jsx(GridNoRowsOverlay, { loading: !rowData || params.loading === true, rowCount: rowCount, headerRowHeight: headerRowCount * rowHeight, filteredRowCount: event.api.getDisplayedRowCount(), noRowsOverlayText: params.noRowsOverlayText, noRowsMatchingOverlayText: params.noRowsMatchingOverlayText }));
3212
+ }, [columnDefs, params.loading, params.noRowsMatchingOverlayText, params.noRowsOverlayText, rowData, rowHeight]);
3213
+ const selectionColumnDef = React.useMemo(() => {
3214
+ const selectWidth = params.hideSelectColumn ? 0 : selectable && params.onRowDragEnd ? 76 : 48;
3215
+ return {
3216
+ suppressNavigable: params.hideSelectColumn,
3217
+ rowDrag: !!params.onRowDragEnd,
3218
+ minWidth: selectWidth,
3219
+ maxWidth: selectWidth,
3220
+ pinned: selectColumnPinned,
3221
+ headerComponentParams: {
3222
+ exportable: false,
3223
+ },
3224
+ headerClass: clsx('ag-header-hide-default-select', params.onRowDragEnd && 'ag-header-select-draggable'),
3225
+ headerComponent: rowSelection == 'multiple' ? GridHeaderSelect : undefined,
3226
+ suppressHeaderKeyboardEvent: (e) => {
3227
+ if (!selectable)
3228
+ return false;
3229
+ if ((e.event.key === 'Enter' || e.event.key === ' ') && !e.event.repeat) {
3230
+ if (lodashEs.isEmpty(e.api.getSelectedRows())) {
3231
+ e.api.selectAll('filtered');
3232
+ }
3233
+ else {
3234
+ e.api.deselectAll();
3235
+ }
3236
+ return true;
3237
+ }
3238
+ return false;
3239
+ },
3240
+ onCellClicked: params.enableSelectionWithoutKeys || params.enableClickSelection
3241
+ ? undefined
3242
+ : clickInputWhenContainingCellClicked,
3243
+ };
3244
+ }, [
3245
+ params.enableClickSelection,
3246
+ params.enableSelectionWithoutKeys,
3247
+ params.hideSelectColumn,
3248
+ params.onRowDragEnd,
3249
+ rowSelection,
3250
+ selectColumnPinned,
3251
+ selectable,
3252
+ ]);
3228
3253
  return (jsxRuntime.jsxs("div", { "data-testid": dataTestId, className: clsx('Grid-container', theme, 'theme-specific', staleGrid && 'Grid-sortIsStale', gridReady && rowData && autoSized && 'Grid-ready'), children: [gridContextMenu.component, jsxRuntime.jsx("div", { style: { flex: 1 }, ref: gridDivRef, children: jsxRuntime.jsx(agGridReact.AgGridReact, { theme: 'legacy', rowSelection: selectable
3229
3254
  ? {
3230
3255
  enableSelectionWithoutKeys: params.enableSelectionWithoutKeys ?? false,
3231
3256
  enableClickSelection: params.enableClickSelection ?? false,
3232
3257
  mode: rowSelection == 'single' ? 'singleRow' : 'multiRow',
3233
3258
  }
3234
- : undefined, rowHeight: rowHeight, animateRows: params.animateRows ?? false, rowClassRules: params.rowClassRules, getRowId: (params) => `${params.data.id}`, onGridSizeChanged: onGridSizeChanged, suppressColumnVirtualisation: suppressColumnVirtualization, suppressClickEdit: true, onColumnVisible: setInitialContentSize, onRowDataUpdated: onRowDataChanged, onCellKeyDown: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, onCellEditingStarted: refreshSelectedRows, domLayout: params.domLayout, onColumnResized: onColumnResized, defaultColDef: { minWidth: 48, ...lodashEs.omit(params.defaultColDef, ['editable']) }, columnDefs: columnDefsAdjusted, rowData: rowData, noRowsOverlayComponent: (event) => {
3235
- let rowCount = 0;
3236
- event.api.forEachNode(() => rowCount++);
3237
- return (jsxRuntime.jsx(GridNoRowsOverlay, { loading: !rowData || params.loading === true, rowCount: rowCount, headerRowHeight: headerRowCount * rowHeight, filteredRowCount: event.api.getDisplayedRowCount(), noRowsOverlayText: params.noRowsOverlayText, noRowsMatchingOverlayText: params.noRowsMatchingOverlayText }));
3238
- }, quickFilterParser: (filterStr) => {
3239
- // filter is exact matches exactly groups separated by commas
3240
- return filterStr.split(',').map((str) => str.trim());
3241
- }, onModelUpdated: onModelUpdated, onGridReady: onGridReady, onSortChanged: ensureSelectedRowIsVisible, postSortRows: params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows, onSelectionChanged: synchroniseExternalStateToGridSelection, onColumnMoved: params.onColumnMoved, alwaysShowVerticalScroll: params.alwaysShowVerticalScroll, isExternalFilterPresent: isExternalFilterPresent, doesExternalFilterPass: doesExternalFilterPass, maintainColumnOrder: true, preventDefaultOnContextMenu: true, onCellContextMenu: gridContextMenu.cellContextMenu, rowDragText: params.rowDragText, onRowDragCancel: clearHighlightRowClasses, onRowDragMove: onRowDragMove, onRowDragEnd: onRowDragEnd, suppressCellFocus: params.suppressCellFocus, pinnedTopRowData: params.pinnedTopRowData, pinnedBottomRowData: params.pinnedBottomRowData, selectionColumnDef: {
3242
- suppressNavigable: params.hideSelectColumn,
3243
- rowDrag: !!params.onRowDragEnd,
3244
- minWidth: selectWidth,
3245
- maxWidth: selectWidth,
3246
- pinned: selectColumnPinned,
3247
- headerComponentParams: {
3248
- exportable: false,
3249
- },
3250
- headerClass: clsx('ag-header-hide-default-select', params.onRowDragEnd && 'ag-header-select-draggable'),
3251
- headerComponent: rowSelection == 'multiple' ? GridHeaderSelect : undefined,
3252
- suppressHeaderKeyboardEvent: (e) => {
3253
- if (!selectable)
3254
- return false;
3255
- if ((e.event.key === 'Enter' || e.event.key === ' ') && !e.event.repeat) {
3256
- if (lodashEs.isEmpty(e.api.getSelectedRows())) {
3257
- e.api.selectAll('filtered');
3258
- }
3259
- else {
3260
- e.api.deselectAll();
3261
- }
3262
- return true;
3263
- }
3264
- return false;
3265
- },
3266
- onCellClicked: params.enableSelectionWithoutKeys || params.enableClickSelection
3267
- ? undefined
3268
- : clickInputWhenContainingCellClicked,
3269
- } }) })] }));
3259
+ : undefined, rowHeight: rowHeight, animateRows: params.animateRows ?? false, rowClassRules: params.rowClassRules, getRowId: getRowId, onGridSizeChanged: onGridSizeChanged, suppressColumnVirtualisation: suppressColumnVirtualization, suppressClickEdit: true, onColumnVisible: setInitialContentSize, onRowDataUpdated: onRowDataChanged, onCellFocused: onCellFocused, onCellKeyDown: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, domLayout: params.domLayout, onColumnResized: onColumnResized, defaultColDef: defaultColDef, columnDefs: columnDefsAdjusted, selectionColumnDef: selectionColumnDef, rowData: rowData, postSortRows: params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows, onModelUpdated: onModelUpdated, onGridReady: onGridReady, onSortChanged: ensureSelectedRowIsVisible, quickFilterParser: quickFilterParser, onSelectionChanged: synchroniseExternalStateToGridSelection, onColumnMoved: params.onColumnMoved, noRowsOverlayComponent: noRowsOverlayComponent, alwaysShowVerticalScroll: params.alwaysShowVerticalScroll, isExternalFilterPresent: isExternalFilterPresent, doesExternalFilterPass: doesExternalFilterPass, maintainColumnOrder: true, preventDefaultOnContextMenu: true, onCellContextMenu: gridContextMenu.cellContextMenu, rowDragText: params.rowDragText, onRowDragCancel: clearHighlightRowClasses, onRowDragMove: onRowDragMove, onRowDragEnd: onRowDragEnd, suppressCellFocus: params.suppressCellFocus, pinnedTopRowData: params.pinnedTopRowData, pinnedBottomRowData: params.pinnedBottomRowData }) })] }));
3260
+ };
3261
+ const quickFilterParser = (filterStr) => {
3262
+ // filter is exact matches exactly groups separated by commas
3263
+ return filterStr.split(',').map((str) => str.trim());
3270
3264
  };
3271
3265
 
3272
3266
  const GridPopoverContext = React.createContext({
3273
3267
  anchorRef: { current: null },
3274
3268
  saving: false,
3275
3269
  setSaving: () => { },
3270
+ stopEditing: () => { },
3276
3271
  colId: '',
3277
3272
  field: '',
3278
3273
  value: null,
@@ -3290,7 +3285,7 @@ const GridPopoverContextProvider = (props2) => {
3290
3285
  const anchorRef = React.useRef(props.eGridCell);
3291
3286
  const hasSaved = React.useRef(false);
3292
3287
  const [saving, setSaving] = React.useState(false);
3293
- const { colDef } = props;
3288
+ const { colDef, stopEditing, formatValue, data, value } = props;
3294
3289
  const { cellEditorParams } = colDef;
3295
3290
  const multiEdit = cellEditorParams?.multiEdit ?? false;
3296
3291
  // Then item that is clicked on will always be first in the list
@@ -3298,8 +3293,10 @@ const GridPopoverContextProvider = (props2) => {
3298
3293
  const field = props.colDef?.field ?? '';
3299
3294
  const colId = props.colDef?.colId ?? field ?? '';
3300
3295
  const updateValue = React.useCallback(async (saveFn, tabDirection) => {
3301
- if (hasSaved.current)
3296
+ if (hasSaved.current) {
3297
+ // already called save so ignore
3302
3298
  return true;
3299
+ }
3303
3300
  hasSaved.current = true;
3304
3301
  return saving ? false : await updatingCells({ selectedRows, field }, saveFn, setSaving, tabDirection);
3305
3302
  }, [field, saving, selectedRows, updatingCells]);
@@ -3310,10 +3307,11 @@ const GridPopoverContextProvider = (props2) => {
3310
3307
  selectedRows,
3311
3308
  colId,
3312
3309
  field,
3313
- data: props.data,
3314
- value: props.value,
3310
+ data,
3311
+ value,
3315
3312
  updateValue,
3316
- formatValue: props.formatValue,
3313
+ formatValue,
3314
+ stopEditing,
3317
3315
  }, children: children }));
3318
3316
  };
3319
3317
 
@@ -3379,16 +3377,18 @@ const defaultValueFormatter = ({ value }) => {
3379
3377
  ? String(value)
3380
3378
  : JSON.stringify(value);
3381
3379
  };
3380
+ // ag-grid doesn't understand the custom editor, when it stops editing it thinks
3381
+ // that _it_ was editing and overwrites the user updated data
3382
+ // _but_, it only does this if the initial value of the cell was null!?
3383
+ const blockValueSetter = () => true;
3382
3384
  /*
3383
3385
  * All cells should use this.
3384
3386
  */
3385
3387
  const GridCell = (props, custom) => {
3386
- // props.field = ;
3387
3388
  // Generate a default filter value getter which uses the formatted value plus
3388
3389
  // the editable value if it's a string and different from the formatted value.
3389
3390
  // This is so that e.g. bearings can be searched for by DMS or raw number.
3390
3391
  const valueFormatter = props.valueFormatter ?? defaultValueFormatter;
3391
- // FIXME
3392
3392
  const filterValueGetter = props.filterValueGetter ?? generateFilterGetter(valueFormatter);
3393
3393
  const exportable = props.exportable;
3394
3394
  // Can't leave this here ag-grid will complain
@@ -3399,6 +3399,7 @@ const GridCell = (props, custom) => {
3399
3399
  headerTooltip: props.headerName,
3400
3400
  sortable: true,
3401
3401
  resizable: true,
3402
+ valueSetter: custom?.editor ? blockValueSetter : undefined,
3402
3403
  editable: props.editable ?? false,
3403
3404
  ...(custom?.editor && {
3404
3405
  cellClassRules: GridCellMultiSelectClassRules,
@@ -3463,7 +3464,7 @@ const GridCellMultiEditor = (props, cellEditorSelector) => GridCell({
3463
3464
  });
3464
3465
 
3465
3466
  const useGridFilter = (filter) => {
3466
- const { addExternalFilter, removeExternalFilter } = React.useContext(GridContext);
3467
+ const { addExternalFilter, removeExternalFilter } = useGridContext();
3467
3468
  React.useEffect(() => {
3468
3469
  const thisFilter = filter;
3469
3470
  thisFilter && addExternalFilter(thisFilter);
@@ -3938,8 +3939,8 @@ const textMatch = (text, filter) => {
3938
3939
  };
3939
3940
 
3940
3941
  const useGridPopoverHook = (props) => {
3941
- const { stopEditing, cancelEdit } = React.useContext(GridContext);
3942
- const { anchorRef, saving, updateValue } = useGridPopoverContext();
3942
+ const { onBulkEditingComplete } = useGridContext();
3943
+ const { anchorRef, saving, updateValue, stopEditing } = useGridPopoverContext();
3943
3944
  const saveButtonRef = React.useRef(null);
3944
3945
  const [isOpen, setOpen] = React.useState(false);
3945
3946
  React.useEffect(() => {
@@ -3947,30 +3948,30 @@ const useGridPopoverHook = (props) => {
3947
3948
  }, []);
3948
3949
  const triggerSave = React.useCallback(async (reason) => {
3949
3950
  if (reason == CloseReason.CANCEL) {
3950
- cancelEdit();
3951
+ stopEditing();
3952
+ onBulkEditingComplete();
3951
3953
  return;
3952
3954
  }
3953
- if (props.invalid && props.invalid()) {
3955
+ if (props?.invalid?.()) {
3956
+ // Don't close, don't do anything it's invalid
3954
3957
  return;
3955
3958
  }
3956
3959
  if (!props.save) {
3957
- cancelEdit();
3958
- }
3959
- else if (props.save) {
3960
- // forms that don't provide an invalid fn must wait until they have saved to close
3961
- if (props.invalid)
3962
- stopEditing();
3963
- if (await updateValue(props.save, reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0)) {
3964
- if (!props.invalid)
3965
- stopEditing();
3966
- }
3960
+ // No save method so just close
3961
+ stopEditing();
3962
+ onBulkEditingComplete();
3963
+ return;
3967
3964
  }
3968
- }, [cancelEdit, props, stopEditing, updateValue]);
3965
+ if (await updateValue(props.save, reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0)) {
3966
+ stopEditing();
3967
+ }
3968
+ }, [onBulkEditingComplete, props, stopEditing, updateValue]);
3969
3969
  const popoverWrapper = React.useCallback((children) => {
3970
3970
  return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: anchorRef.current && (jsxRuntime.jsxs(ControlledMenu, { state: isOpen ? 'open' : 'closed', portal: true, unmountOnClose: true, anchorRef: anchorRef, saveButtonRef: saveButtonRef, menuClassName: 'step-ag-grid-react-menu', onClose: (event) => {
3971
3971
  // Prevent menu from closing when modals are invoked
3972
- if (event.reason === CloseReason.BLUR)
3972
+ if (event.reason === CloseReason.BLUR) {
3973
3973
  return;
3974
+ }
3974
3975
  void triggerSave(event.reason);
3975
3976
  }, viewScroll: 'auto', dontShrinkIfDirectionIsTop: true, className: props.className, children: [saving && ( // This is the overlay that prevents editing when the editor is saving
3976
3977
  jsxRuntime.jsx("div", { className: 'ComponentLoadingWrapper-saveOverlay' })), children, jsxRuntime.jsx("button", { ref: saveButtonRef, "data-reason": '', onClick: (e) => {
@@ -4838,12 +4839,14 @@ const GridFormTextInput = (props) => {
4838
4839
  const [value, setValue] = React.useState(initValue);
4839
4840
  const invalid = React.useCallback(() => TextInputValidator(props, value, data, {}), [data, props, value]);
4840
4841
  const save = React.useCallback(async (selectedRows) => {
4841
- if (invalid())
4842
+ if (invalid()) {
4842
4843
  return false;
4844
+ }
4843
4845
  const trimmedValue = value.trim();
4844
4846
  // No change, so don't save
4845
- if (initValue === trimmedValue)
4847
+ if (initValue === trimmedValue) {
4846
4848
  return true;
4849
+ }
4847
4850
  if (props.onSave) {
4848
4851
  return await props.onSave({ selectedRows, value: trimmedValue });
4849
4852
  }
@@ -5064,7 +5067,7 @@ const isFlexColumn = (colDef) => !!colDef.flex && !isGridCellFiller(colDef);
5064
5067
  * Also, make sure the provider is created in a separate component, otherwise it won't be found.
5065
5068
  */
5066
5069
  const GridContextProvider = (props) => {
5067
- const { modifyUpdating, checkUpdating } = React.useContext(GridUpdatingContext);
5070
+ const { modifyUpdating, anyUpdating } = React.useContext(GridUpdatingContext);
5068
5071
  const [gridApi, setGridApi] = React.useState();
5069
5072
  const [gridReady, setGridReady] = React.useState(false);
5070
5073
  const [quickFilter, _setQuickFilter] = React.useState('');
@@ -5083,7 +5086,7 @@ const GridContextProvider = (props) => {
5083
5086
  /**
5084
5087
  * Make extra sure the GridCellFillerColId never gets added to invisibleColumnIds as it's dynamically determined
5085
5088
  */
5086
- const setInvisibleColumnIds = (invisibleColumnIds) => _setInvisibleColumnIds(lodashEs.pull(invisibleColumnIds, GridCellFillerColId));
5089
+ const setInvisibleColumnIds = React.useCallback((invisibleColumnIds) => _setInvisibleColumnIds(lodashEs.pull(invisibleColumnIds, GridCellFillerColId)), []);
5087
5090
  /**
5088
5091
  * Set quick filter directly on grid, based on previously save quickFilter state.
5089
5092
  */
@@ -5178,17 +5181,6 @@ const GridContextProvider = (props) => {
5178
5181
  }
5179
5182
  prePopupFocusedCell.current = gridApi.getFocusedCell() ?? undefined;
5180
5183
  }, [gridApi]);
5181
- /**
5182
- * After a popup refocus the cell.
5183
- */
5184
- const postPopupOps = React.useCallback(() => {
5185
- if (!gridApi || gridApi.isDestroyed()) {
5186
- return;
5187
- }
5188
- if (prePopupFocusedCell.current) {
5189
- gridApi.setFocusedCell(prePopupFocusedCell.current.rowIndex, prePopupFocusedCell.current.column);
5190
- }
5191
- }, [gridApi]);
5192
5184
  /**
5193
5185
  * Get all row id's in grid.
5194
5186
  */
@@ -5376,13 +5368,16 @@ const GridContextProvider = (props) => {
5376
5368
  gridApi.sizeColumnsToFit();
5377
5369
  }
5378
5370
  }, [gridApi]);
5379
- const stopEditing = React.useCallback(() => {
5380
- if (!gridApi || gridApi.isDestroyed()) {
5371
+ /**
5372
+ *
5373
+ */
5374
+ const resetFocusedCellAfterCellEditing = React.useCallback(() => {
5375
+ if (!gridApi || gridApi.isDestroyed() || startCellEditingInProgressRef.current) {
5381
5376
  return;
5382
5377
  }
5383
- gridApi.stopEditing();
5384
5378
  if (prePopupFocusedCell.current) {
5385
5379
  gridApi.setFocusedCell(prePopupFocusedCell.current.rowIndex, prePopupFocusedCell.current.column);
5380
+ prePopupFocusedCell.current = undefined;
5386
5381
  }
5387
5382
  }, [gridApi]);
5388
5383
  // waitForExternallySelectedItemsToBeInSync can't use the state as it won't be updated during function execution
@@ -5403,58 +5398,65 @@ const GridContextProvider = (props) => {
5403
5398
  console.error('externallySelectedItems did not sync with ag-grid selection');
5404
5399
  }
5405
5400
  }, []);
5401
+ const startCellEditingInProgressRef = React.useRef(false);
5406
5402
  const startCellEditing = React.useCallback(async ({ rowId, colId }) => {
5407
- if (!gridApi)
5408
- return;
5409
- const colDef = gridApi.getColumnDef(colId);
5410
- if (!colDef) {
5411
- return;
5412
- }
5413
- const rowNode = gridApi.getRowNode(`${rowId}`);
5414
- if (!rowNode) {
5403
+ if (!gridApi || startCellEditingInProgressRef.current) {
5415
5404
  return;
5416
5405
  }
5417
- prePopupOps();
5418
- const shouldSelectNode = !rowNode.isSelected();
5419
- if (shouldSelectNode) {
5420
- externallySelectedItemsAreInSyncRef.current = false;
5421
- setExternallySelectedItemsAreInSync(false);
5422
- rowNode.setSelected(true, true);
5423
- await waitForExternallySelectedItemsToBeInSync();
5424
- }
5425
- // Cell already being edited, so don't re-edit until finished
5426
- if (checkUpdating([colDef.field ?? colDef.colId ?? ''], rowId)) {
5427
- return;
5428
- }
5429
- const rowIndex = rowNode.rowIndex;
5430
- if (rowIndex != null) {
5431
- lodashEs.defer(() => {
5432
- !gridApi.isDestroyed() &&
5433
- gridApi.startEditingCell({
5434
- rowIndex,
5435
- colKey: colId,
5436
- });
5437
- });
5406
+ startCellEditingInProgressRef.current = true;
5407
+ try {
5408
+ // Edit in progress so don't edit until finished, timeout waiting after 5s
5409
+ if (!(await waitForCondition(() => !anyUpdating(), 5000))) {
5410
+ console.error("Could not start edit because previous edit hasn't finished after 5 seconds");
5411
+ return;
5412
+ }
5413
+ const colDef = gridApi.getColumnDef(colId);
5414
+ if (!colDef) {
5415
+ return;
5416
+ }
5417
+ const rowNode = gridApi.getRowNode(`${rowId}`);
5418
+ if (!rowNode) {
5419
+ return;
5420
+ }
5421
+ prePopupOps();
5422
+ const shouldSelectNode = !rowNode.isSelected();
5423
+ if (shouldSelectNode) {
5424
+ externallySelectedItemsAreInSyncRef.current = false;
5425
+ setExternallySelectedItemsAreInSync(false);
5426
+ rowNode.setSelected(true, true);
5427
+ await waitForExternallySelectedItemsToBeInSync();
5428
+ }
5429
+ const rowIndex = rowNode.rowIndex;
5430
+ if (rowIndex != null) {
5431
+ lodashEs.defer(() => {
5432
+ !gridApi.isDestroyed() &&
5433
+ gridApi.startEditingCell({
5434
+ rowIndex,
5435
+ colKey: colId,
5436
+ });
5437
+ });
5438
+ }
5438
5439
  }
5439
- }, [checkUpdating, gridApi, prePopupOps, waitForExternallySelectedItemsToBeInSync]);
5440
- /**
5441
- * This differs from stopEdit in that it will also invoke cellEditingCompleteCallback
5442
- */
5443
- const cancelEdit = React.useCallback(() => {
5444
- stopEditing();
5445
- cellEditingCompleteCallbackRef.current?.();
5446
- }, [stopEditing]);
5447
- const cellEditingCompleteCallbackRef = React.useRef();
5448
- const setOnCellEditingComplete = React.useCallback((cellEditingCompleteCallback) => {
5449
- cellEditingCompleteCallbackRef.current = cellEditingCompleteCallback;
5440
+ finally {
5441
+ startCellEditingInProgressRef.current = false;
5442
+ }
5443
+ }, [anyUpdating, gridApi, prePopupOps, waitForExternallySelectedItemsToBeInSync]);
5444
+ const bulkEditingCompleteCallbackRef = React.useRef();
5445
+ const onBulkEditingComplete = React.useCallback(() => {
5446
+ resetFocusedCellAfterCellEditing();
5447
+ bulkEditingCompleteCallbackRef.current?.();
5448
+ }, [resetFocusedCellAfterCellEditing]);
5449
+ const setOnBulkEditingComplete = React.useCallback((cellEditingCompleteCallback) => {
5450
+ bulkEditingCompleteCallbackRef.current = cellEditingCompleteCallback;
5450
5451
  }, []);
5451
5452
  /**
5452
5453
  * Returns true if an editable cell on same row was selected, else false.
5453
5454
  */
5454
5455
  const selectNextEditableCell = React.useCallback(async (tabDirection) => {
5455
5456
  // Pretend it succeeded to prevent unwanted cellEditingCompleteCallback
5456
- if (!gridApi)
5457
+ if (!gridApi) {
5457
5458
  return true;
5459
+ }
5458
5460
  const focusedCellIsEditable = () => {
5459
5461
  const focusedCell = gridApi.isDestroyed() ? null : gridApi.getFocusedCell();
5460
5462
  const nextColumn = focusedCell?.column;
@@ -5470,16 +5472,16 @@ const GridContextProvider = (props) => {
5470
5472
  if (gridApi.isDestroyed()) {
5471
5473
  return true;
5472
5474
  }
5475
+ // Prevent resetting focus to original editing cell
5476
+ prePopupFocusedCell.current = undefined;
5473
5477
  const preRow = gridApi.getFocusedCell();
5474
5478
  if (tabDirection === 1) {
5479
+ gridApi.stopEditing();
5475
5480
  gridApi.tabToNextCell();
5476
5481
  }
5477
5482
  else {
5478
5483
  gridApi.tabToPreviousCell();
5479
5484
  }
5480
- // If we don't wait the tab to next element won't work
5481
- // I think this is due to the grid resizing
5482
- await wait(150);
5483
5485
  if (gridApi.isDestroyed()) {
5484
5486
  return true;
5485
5487
  }
@@ -5492,64 +5494,71 @@ const GridContextProvider = (props) => {
5492
5494
  if (focusedCellIsEditable()) {
5493
5495
  const focusedCell = gridApi.getFocusedCell();
5494
5496
  if (focusedCell) {
5495
- prePopupOps();
5496
- gridApi.startEditingCell({
5497
- rowIndex: focusedCell.rowIndex,
5498
- colKey: focusedCell.column.getColId(),
5499
- });
5497
+ const rowNode = gridApi.getDisplayedRowAtIndex(focusedCell.rowIndex);
5498
+ const rowId = rowNode?.data?.id;
5499
+ if (rowId == null) {
5500
+ return false;
5501
+ }
5502
+ await startCellEditing({ rowId, colId: focusedCell.column.getColId() });
5500
5503
  return true;
5501
5504
  }
5502
5505
  }
5503
5506
  }
5504
5507
  return false;
5505
- }, [gridApi, prePopupOps]);
5508
+ }, [gridApi, startCellEditing]);
5506
5509
  const updatingCells = React.useCallback(async (props, fnUpdate, setSaving, tabDirection) => {
5507
- setSaving && setSaving(true);
5508
- return await gridApiOp(async (gridApi) => {
5509
- const selectedRows = props.selectedRows;
5510
- let ok = false;
5511
- await modifyUpdating(props.field ?? '', selectedRows.map((data) => data.id), async () => {
5510
+ try {
5511
+ setSaving?.(true);
5512
+ return await gridApiOp(async (gridApi) => {
5513
+ const selectedRows = props.selectedRows;
5514
+ let ok = false;
5515
+ await modifyUpdating(props.field ?? '', selectedRows.map((data) => data.id), async () => {
5516
+ // MATT Disabled I don't believe these are needed anymore
5517
+ // I've left them here just in case they are
5518
+ // Need to refresh to get spinners to work on all rows
5519
+ // gridApi.refreshCells({ rowNodes: props.selectedRows as RowNode[], force: true });
5520
+ ok = await fnUpdate(selectedRows).catch((ex) => {
5521
+ console.error('Exception during modifyUpdating', ex);
5522
+ return false;
5523
+ });
5524
+ });
5512
5525
  // MATT Disabled I don't believe these are needed anymore
5513
5526
  // I've left them here just in case they are
5514
- // Need to refresh to get spinners to work on all rows
5515
- // gridApi.refreshCells({ rowNodes: props.selectedRows as RowNode[], force: true });
5516
- ok = await fnUpdate(selectedRows).catch((ex) => {
5517
- console.error('Exception during modifyUpdating', ex);
5518
- return false;
5519
- });
5520
- });
5521
- // MATT Disabled I don't believe these are needed anymore
5522
- // I've left them here just in case they are
5523
- // async processes need to refresh their own rows
5524
- // gridApi.refreshCells({ rowNodes: selectedRows as RowNode[], force: true });
5525
- if (gridApi.isDestroyed()) {
5526
- return ok;
5527
- }
5528
- if (ok) {
5529
- const cell = gridApi.getFocusedCell();
5530
- if (cell && gridApi.getFocusedCell() == null) {
5531
- !gridApi.isDestroyed && gridApi.setFocusedCell(cell.rowIndex, cell.column);
5527
+ // async processes need to refresh their own rows
5528
+ // gridApi.refreshCells({ rowNodes: selectedRows as RowNode[], force: true });
5529
+ if (gridApi.isDestroyed()) {
5530
+ return ok;
5532
5531
  }
5533
- // This is needed to trigger postSortRowsHook
5534
- gridApi.refreshClientSideRowModel();
5535
- }
5536
- else {
5537
- // Don't set saving if ok as the form has already closed
5538
- setSaving?.(false);
5539
- }
5540
- // Only focus next cell if user hasn't already manually changed focus
5541
- const postPopupFocusedCell = gridApi.getFocusedCell();
5542
- if (prePopupFocusedCell.current &&
5543
- postPopupFocusedCell &&
5544
- prePopupFocusedCell.current.rowIndex == postPopupFocusedCell.rowIndex &&
5545
- prePopupFocusedCell.current.column.getColId() == postPopupFocusedCell.column.getColId()) {
5546
- if (!tabDirection || !(await selectNextEditableCell(tabDirection))) {
5547
- cellEditingCompleteCallbackRef.current && cellEditingCompleteCallbackRef.current();
5532
+ if (ok) {
5533
+ const cell = gridApi.getFocusedCell();
5534
+ if (cell && gridApi.getFocusedCell() == null) {
5535
+ !gridApi.isDestroyed && gridApi.setFocusedCell(cell.rowIndex, cell.column);
5536
+ }
5537
+ // This is needed to trigger postSortRowsHook
5538
+ gridApi.refreshClientSideRowModel();
5548
5539
  }
5549
- }
5550
- return ok;
5551
- });
5552
- }, [gridApiOp, modifyUpdating, selectNextEditableCell]);
5540
+ void (async () => {
5541
+ // Only focus next cell if user hasn't already manually changed focus
5542
+ const postPopupFocusedCell = gridApi.getFocusedCell();
5543
+ if (prePopupFocusedCell.current &&
5544
+ postPopupFocusedCell &&
5545
+ prePopupFocusedCell.current.rowIndex == postPopupFocusedCell.rowIndex &&
5546
+ prePopupFocusedCell.current.column.getColId() == postPopupFocusedCell.column.getColId()) {
5547
+ if (!tabDirection || !(await selectNextEditableCell(tabDirection))) {
5548
+ onBulkEditingComplete();
5549
+ }
5550
+ }
5551
+ else {
5552
+ onBulkEditingComplete();
5553
+ }
5554
+ })();
5555
+ return ok;
5556
+ });
5557
+ }
5558
+ finally {
5559
+ setSaving?.(false);
5560
+ }
5561
+ }, [gridApiOp, modifyUpdating, onBulkEditingComplete, selectNextEditableCell]);
5553
5562
  const redrawRows = React.useMemo(() => debounce((rowNodes) => {
5554
5563
  try {
5555
5564
  gridApi && gridApi.redrawRows(rowNodes ? { rowNodes } : undefined);
@@ -5580,16 +5589,16 @@ const GridContextProvider = (props) => {
5580
5589
  preHash !== postHash && gridApi.clearFocusedCell();
5581
5590
  }
5582
5591
  }, 200), [gridApi]);
5583
- const addExternalFilter = (filter) => {
5592
+ const addExternalFilter = React.useCallback((filter) => {
5584
5593
  externalFilters.current.push(filter);
5585
5594
  void onFilterChanged();
5586
- };
5587
- const removeExternalFilter = (filter) => {
5595
+ }, [onFilterChanged]);
5596
+ const removeExternalFilter = React.useCallback((filter) => {
5588
5597
  lodashEs.remove(externalFilters.current, (v) => v === filter);
5589
5598
  void onFilterChanged();
5590
- };
5591
- const isExternalFilterPresent = () => !lodashEs.isEmpty(externalFilters.current);
5592
- const doesExternalFilterPass = (node) => externalFilters.current.every((filter) => filter(node.data, node));
5599
+ }, [onFilterChanged]);
5600
+ const isExternalFilterPresent = React.useCallback(() => !lodashEs.isEmpty(externalFilters.current), []);
5601
+ const doesExternalFilterPass = React.useCallback((node) => externalFilters.current.every((filter) => filter(node.data, node)), []);
5593
5602
  const getColDef = React.useCallback((colId) => (!!colId && gridApi?.getColumnDef(colId)) || undefined, [gridApi]);
5594
5603
  const showNoRowsOverlay = React.useCallback(() => {
5595
5604
  gridApi?.showNoRowsOverlay();
@@ -5645,7 +5654,6 @@ const GridContextProvider = (props) => {
5645
5654
  setInvisibleColumnIds,
5646
5655
  gridReady,
5647
5656
  prePopupOps,
5648
- postPopupOps,
5649
5657
  setApis,
5650
5658
  setQuickFilter,
5651
5659
  selectRowsById,
@@ -5666,8 +5674,7 @@ const GridContextProvider = (props) => {
5666
5674
  sizeColumnsToFit,
5667
5675
  autoSizeColumns,
5668
5676
  startCellEditing,
5669
- stopEditing,
5670
- cancelEdit,
5677
+ resetFocusedCellAfterCellEditing,
5671
5678
  updatingCells,
5672
5679
  redrawRows,
5673
5680
  externallySelectedItemsAreInSync,
@@ -5678,7 +5685,8 @@ const GridContextProvider = (props) => {
5678
5685
  isExternalFilterPresent,
5679
5686
  doesExternalFilterPass,
5680
5687
  downloadCsv,
5681
- setOnCellEditingComplete,
5688
+ onBulkEditingComplete,
5689
+ setOnBulkEditingComplete,
5682
5690
  showNoRowsOverlay,
5683
5691
  }, children: props.children }));
5684
5692
  };
@@ -5725,12 +5733,23 @@ const downloadCsvUseValueFormattersProcessCellCallback = (params) => {
5725
5733
  // We add an extra encodeToString here just in case valueFormatter is returning non string values
5726
5734
  return encodeToString(result);
5727
5735
  };
5736
+ const waitForCondition = async (condition, timeoutMs) => {
5737
+ const endTime = Date.now() + timeoutMs;
5738
+ while (Date.now() < endTime) {
5739
+ if (condition()) {
5740
+ return true;
5741
+ }
5742
+ await wait(100);
5743
+ }
5744
+ console.warn('waitForCondition failed');
5745
+ return false;
5746
+ };
5728
5747
 
5729
5748
  const GridUpdatingContextProvider = (props) => {
5730
5749
  const updatingBlocks = React.useRef({});
5731
5750
  const updating = React.useRef({});
5732
5751
  const [updatedDep, setUpdatedDep] = React.useState(0);
5733
- const resetUpdating = () => {
5752
+ const resetUpdating = React.useCallback(() => {
5734
5753
  const mergedUpdatingBlocks = {};
5735
5754
  for (const key in updatingBlocks.current) {
5736
5755
  const arr = lodashEs.flatten(updatingBlocks.current[key]);
@@ -5740,9 +5759,9 @@ const GridUpdatingContextProvider = (props) => {
5740
5759
  }
5741
5760
  updating.current = mergedUpdatingBlocks;
5742
5761
  setUpdatedDep((updatedDep) => updatedDep + 1);
5743
- };
5744
- const updatingCols = () => Object.keys(updating.current);
5745
- const modifyUpdating = async (fields, ids, fn) => {
5762
+ }, []);
5763
+ const updatingCols = React.useCallback(() => Object.keys(updating.current), []);
5764
+ const modifyUpdating = React.useCallback(async (fields, ids, fn) => {
5746
5765
  const idRef = [...ids];
5747
5766
  lodashEs.castArray(fields).forEach((field) => {
5748
5767
  const fieldUpdatingIds = updatingBlocks.current[field] ?? (updatingBlocks.current[field] = []);
@@ -5755,9 +5774,12 @@ const GridUpdatingContextProvider = (props) => {
5755
5774
  lodashEs.remove(fieldUpdatingIds, (idList) => idList === idRef);
5756
5775
  });
5757
5776
  resetUpdating();
5758
- };
5759
- const checkUpdating = (fields, id) => lodashEs.castArray(fields).some((f) => updating.current[f]?.includes(id));
5760
- return (jsxRuntime.jsx(GridUpdatingContext.Provider, { value: { modifyUpdating, checkUpdating, updatingCols, updatedDep }, children: props.children }));
5777
+ }, [resetUpdating]);
5778
+ const anyUpdating = React.useCallback(() => {
5779
+ return !lodashEs.isEmpty(updating.current);
5780
+ }, []);
5781
+ const checkUpdating = React.useCallback((fields, id) => lodashEs.castArray(fields).some((f) => updating.current[f]?.includes(id)), []);
5782
+ return (jsxRuntime.jsx(GridUpdatingContext.Provider, { value: { modifyUpdating, anyUpdating, checkUpdating, updatingCols, updatedDep }, children: props.children }));
5761
5783
  };
5762
5784
 
5763
5785
  var css_248z = ".ActionButton{align-items:center;display:flex}.ActionButton-inProgress .LuiIcon{visibility:hidden}.ActionButton-minimal.lui-button-lg.lui-button-icon-right{padding-right:38px}.ActionButton.lui-button-lg.lui-button-icon-right:not(.ActionButton-tight) .LuiIcon{margin:0 4px}.ActionButton.ActionButton-tight.lui-button-lg.lui-button-icon-right .LuiIcon{margin:0}.ActionButton-iconOnly.lui-button-lg.lui-button-icon-right:not(.ActionButton-tight){padding:8px 5px}.ActionButton-iconOnly.lui-button-lg.lui-button-icon-right.ActionButton-tight{padding:0}.ActionButton-minimalArea{position:relative}.ActionButton-minimalAreaDisplay{position:absolute}.ActionButton-minimalAreaExpand{visibility:hidden}.ActionButton-inProgress.lui-button-lg.lui-button-icon-right{background-color:#e2f3f7;color:#007198;cursor:progress}.ActionButton-fill{justify-content:center;width:100%}";