@linzjs/step-ag-grid 28.5.1 → 29.0.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.
Files changed (42) 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 +263 -246
  8. package/dist/step-ag-grid.cjs.map +1 -1
  9. package/dist/step-ag-grid.esm.js +264 -247
  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/GridPopoverHook.tsx +25 -20
  14. package/src/components/gridFilter/useGridFilter.ts +3 -3
  15. package/src/components/gridHook/useGridContextMenu.tsx +3 -3
  16. package/src/contexts/GridContext.tsx +11 -14
  17. package/src/contexts/GridContextProvider.tsx +164 -137
  18. package/src/contexts/GridPopoverContext.tsx +2 -0
  19. package/src/contexts/GridPopoverContextProvider.tsx +9 -5
  20. package/src/contexts/GridUpdatingContext.tsx +5 -0
  21. package/src/contexts/GridUpdatingContextProvider.tsx +35 -28
  22. package/src/lui/reactUtils.tsx +24 -0
  23. package/src/react-menu3/components/MenuList.tsx +3 -0
  24. package/src/stories/grid/GridFilterButtons.stories.tsx +1 -1
  25. package/src/stories/grid/GridNonEditableRow.stories.tsx +1 -1
  26. package/src/stories/grid/GridPinnedRow.stories.tsx +1 -1
  27. package/src/stories/grid/GridPopoutContextMenu.stories.tsx +3 -3
  28. package/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx +2 -2
  29. package/src/stories/grid/GridPopoverEditDropDown.stories.tsx +8 -4
  30. package/src/stories/grid/GridPopoverEditMultiSelect.stories.tsx +1 -0
  31. package/src/stories/grid/GridReadOnly.stories.tsx +1 -1
  32. package/src/stories/grid/GridViewList.stories.tsx +1 -1
  33. package/src/stories/grid/gridFormInteraction/GridFormDropDownInteraction.stories.tsx +5 -3
  34. package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +3 -2
  35. package/src/stories/grid/gridFormInteraction/GridFormEditBearingInteraction.stories.tsx +3 -2
  36. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectGridInteraction.stories.tsx +3 -2
  37. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectInteraction.stories.tsx +3 -2
  38. package/src/stories/grid/gridFormInteraction/GridFormPopoverMenuInteraction.stories.tsx +3 -2
  39. package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +3 -2
  40. package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +3 -2
  41. package/src/stories/grid/gridFormStatic/GridFormDropDown.stories.tsx +2 -2
  42. 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
 
@@ -3463,7 +3461,7 @@ const GridCellMultiEditor = (props, cellEditorSelector) => GridCell({
3463
3461
  });
3464
3462
 
3465
3463
  const useGridFilter = (filter) => {
3466
- const { addExternalFilter, removeExternalFilter } = React.useContext(GridContext);
3464
+ const { addExternalFilter, removeExternalFilter } = useGridContext();
3467
3465
  React.useEffect(() => {
3468
3466
  const thisFilter = filter;
3469
3467
  thisFilter && addExternalFilter(thisFilter);
@@ -3938,8 +3936,8 @@ const textMatch = (text, filter) => {
3938
3936
  };
3939
3937
 
3940
3938
  const useGridPopoverHook = (props) => {
3941
- const { stopEditing, cancelEdit } = React.useContext(GridContext);
3942
- const { anchorRef, saving, updateValue } = useGridPopoverContext();
3939
+ const { onBulkEditingComplete } = useGridContext();
3940
+ const { anchorRef, saving, updateValue, stopEditing } = useGridPopoverContext();
3943
3941
  const saveButtonRef = React.useRef(null);
3944
3942
  const [isOpen, setOpen] = React.useState(false);
3945
3943
  React.useEffect(() => {
@@ -3947,30 +3945,30 @@ const useGridPopoverHook = (props) => {
3947
3945
  }, []);
3948
3946
  const triggerSave = React.useCallback(async (reason) => {
3949
3947
  if (reason == CloseReason.CANCEL) {
3950
- cancelEdit();
3948
+ stopEditing();
3949
+ onBulkEditingComplete();
3951
3950
  return;
3952
3951
  }
3953
- if (props.invalid && props.invalid()) {
3952
+ if (props?.invalid?.()) {
3953
+ // Don't close, don't do anything it's invalid
3954
3954
  return;
3955
3955
  }
3956
3956
  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
- }
3957
+ // No save method so just close
3958
+ stopEditing();
3959
+ onBulkEditingComplete();
3960
+ return;
3967
3961
  }
3968
- }, [cancelEdit, props, stopEditing, updateValue]);
3962
+ if (await updateValue(props.save, reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0)) {
3963
+ stopEditing();
3964
+ }
3965
+ }, [onBulkEditingComplete, props, stopEditing, updateValue]);
3969
3966
  const popoverWrapper = React.useCallback((children) => {
3970
3967
  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
3968
  // Prevent menu from closing when modals are invoked
3972
- if (event.reason === CloseReason.BLUR)
3969
+ if (event.reason === CloseReason.BLUR) {
3973
3970
  return;
3971
+ }
3974
3972
  void triggerSave(event.reason);
3975
3973
  }, viewScroll: 'auto', dontShrinkIfDirectionIsTop: true, className: props.className, children: [saving && ( // This is the overlay that prevents editing when the editor is saving
3976
3974
  jsxRuntime.jsx("div", { className: 'ComponentLoadingWrapper-saveOverlay' })), children, jsxRuntime.jsx("button", { ref: saveButtonRef, "data-reason": '', onClick: (e) => {
@@ -5064,7 +5062,7 @@ const isFlexColumn = (colDef) => !!colDef.flex && !isGridCellFiller(colDef);
5064
5062
  * Also, make sure the provider is created in a separate component, otherwise it won't be found.
5065
5063
  */
5066
5064
  const GridContextProvider = (props) => {
5067
- const { modifyUpdating, checkUpdating } = React.useContext(GridUpdatingContext);
5065
+ const { modifyUpdating, anyUpdating } = React.useContext(GridUpdatingContext);
5068
5066
  const [gridApi, setGridApi] = React.useState();
5069
5067
  const [gridReady, setGridReady] = React.useState(false);
5070
5068
  const [quickFilter, _setQuickFilter] = React.useState('');
@@ -5083,7 +5081,7 @@ const GridContextProvider = (props) => {
5083
5081
  /**
5084
5082
  * Make extra sure the GridCellFillerColId never gets added to invisibleColumnIds as it's dynamically determined
5085
5083
  */
5086
- const setInvisibleColumnIds = (invisibleColumnIds) => _setInvisibleColumnIds(lodashEs.pull(invisibleColumnIds, GridCellFillerColId));
5084
+ const setInvisibleColumnIds = React.useCallback((invisibleColumnIds) => _setInvisibleColumnIds(lodashEs.pull(invisibleColumnIds, GridCellFillerColId)), []);
5087
5085
  /**
5088
5086
  * Set quick filter directly on grid, based on previously save quickFilter state.
5089
5087
  */
@@ -5178,17 +5176,6 @@ const GridContextProvider = (props) => {
5178
5176
  }
5179
5177
  prePopupFocusedCell.current = gridApi.getFocusedCell() ?? undefined;
5180
5178
  }, [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
5179
  /**
5193
5180
  * Get all row id's in grid.
5194
5181
  */
@@ -5376,13 +5363,16 @@ const GridContextProvider = (props) => {
5376
5363
  gridApi.sizeColumnsToFit();
5377
5364
  }
5378
5365
  }, [gridApi]);
5379
- const stopEditing = React.useCallback(() => {
5380
- if (!gridApi || gridApi.isDestroyed()) {
5366
+ /**
5367
+ *
5368
+ */
5369
+ const resetFocusedCellAfterCellEditing = React.useCallback(() => {
5370
+ if (!gridApi || gridApi.isDestroyed() || startCellEditingInProgressRef.current) {
5381
5371
  return;
5382
5372
  }
5383
- gridApi.stopEditing();
5384
5373
  if (prePopupFocusedCell.current) {
5385
5374
  gridApi.setFocusedCell(prePopupFocusedCell.current.rowIndex, prePopupFocusedCell.current.column);
5375
+ prePopupFocusedCell.current = undefined;
5386
5376
  }
5387
5377
  }, [gridApi]);
5388
5378
  // waitForExternallySelectedItemsToBeInSync can't use the state as it won't be updated during function execution
@@ -5403,58 +5393,65 @@ const GridContextProvider = (props) => {
5403
5393
  console.error('externallySelectedItems did not sync with ag-grid selection');
5404
5394
  }
5405
5395
  }, []);
5396
+ const startCellEditingInProgressRef = React.useRef(false);
5406
5397
  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) {
5415
- return;
5416
- }
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)) {
5398
+ if (!gridApi || startCellEditingInProgressRef.current) {
5427
5399
  return;
5428
5400
  }
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
- });
5401
+ startCellEditingInProgressRef.current = true;
5402
+ try {
5403
+ // Edit in progress so don't edit until finished, timeout waiting after 5s
5404
+ if (!(await waitForCondition(() => !anyUpdating(), 5000))) {
5405
+ console.error("Could not start edit because previous edit hasn't finished after 5 seconds");
5406
+ return;
5407
+ }
5408
+ const colDef = gridApi.getColumnDef(colId);
5409
+ if (!colDef) {
5410
+ return;
5411
+ }
5412
+ const rowNode = gridApi.getRowNode(`${rowId}`);
5413
+ if (!rowNode) {
5414
+ return;
5415
+ }
5416
+ prePopupOps();
5417
+ const shouldSelectNode = !rowNode.isSelected();
5418
+ if (shouldSelectNode) {
5419
+ externallySelectedItemsAreInSyncRef.current = false;
5420
+ setExternallySelectedItemsAreInSync(false);
5421
+ rowNode.setSelected(true, true);
5422
+ await waitForExternallySelectedItemsToBeInSync();
5423
+ }
5424
+ const rowIndex = rowNode.rowIndex;
5425
+ if (rowIndex != null) {
5426
+ lodashEs.defer(() => {
5427
+ !gridApi.isDestroyed() &&
5428
+ gridApi.startEditingCell({
5429
+ rowIndex,
5430
+ colKey: colId,
5431
+ });
5432
+ });
5433
+ }
5438
5434
  }
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;
5435
+ finally {
5436
+ startCellEditingInProgressRef.current = false;
5437
+ }
5438
+ }, [anyUpdating, gridApi, prePopupOps, waitForExternallySelectedItemsToBeInSync]);
5439
+ const bulkEditingCompleteCallbackRef = React.useRef();
5440
+ const onBulkEditingComplete = React.useCallback(() => {
5441
+ resetFocusedCellAfterCellEditing();
5442
+ bulkEditingCompleteCallbackRef.current?.();
5443
+ }, [resetFocusedCellAfterCellEditing]);
5444
+ const setOnBulkEditingComplete = React.useCallback((cellEditingCompleteCallback) => {
5445
+ bulkEditingCompleteCallbackRef.current = cellEditingCompleteCallback;
5450
5446
  }, []);
5451
5447
  /**
5452
5448
  * Returns true if an editable cell on same row was selected, else false.
5453
5449
  */
5454
5450
  const selectNextEditableCell = React.useCallback(async (tabDirection) => {
5455
5451
  // Pretend it succeeded to prevent unwanted cellEditingCompleteCallback
5456
- if (!gridApi)
5452
+ if (!gridApi) {
5457
5453
  return true;
5454
+ }
5458
5455
  const focusedCellIsEditable = () => {
5459
5456
  const focusedCell = gridApi.isDestroyed() ? null : gridApi.getFocusedCell();
5460
5457
  const nextColumn = focusedCell?.column;
@@ -5470,16 +5467,16 @@ const GridContextProvider = (props) => {
5470
5467
  if (gridApi.isDestroyed()) {
5471
5468
  return true;
5472
5469
  }
5470
+ // Prevent resetting focus to original editing cell
5471
+ prePopupFocusedCell.current = undefined;
5473
5472
  const preRow = gridApi.getFocusedCell();
5474
5473
  if (tabDirection === 1) {
5474
+ gridApi.stopEditing();
5475
5475
  gridApi.tabToNextCell();
5476
5476
  }
5477
5477
  else {
5478
5478
  gridApi.tabToPreviousCell();
5479
5479
  }
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
5480
  if (gridApi.isDestroyed()) {
5484
5481
  return true;
5485
5482
  }
@@ -5492,64 +5489,71 @@ const GridContextProvider = (props) => {
5492
5489
  if (focusedCellIsEditable()) {
5493
5490
  const focusedCell = gridApi.getFocusedCell();
5494
5491
  if (focusedCell) {
5495
- prePopupOps();
5496
- gridApi.startEditingCell({
5497
- rowIndex: focusedCell.rowIndex,
5498
- colKey: focusedCell.column.getColId(),
5499
- });
5492
+ const rowNode = gridApi.getDisplayedRowAtIndex(focusedCell.rowIndex);
5493
+ const rowId = rowNode?.data?.id;
5494
+ if (rowId == null) {
5495
+ return false;
5496
+ }
5497
+ await startCellEditing({ rowId, colId: focusedCell.column.getColId() });
5500
5498
  return true;
5501
5499
  }
5502
5500
  }
5503
5501
  }
5504
5502
  return false;
5505
- }, [gridApi, prePopupOps]);
5503
+ }, [gridApi, startCellEditing]);
5506
5504
  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 () => {
5505
+ try {
5506
+ setSaving?.(true);
5507
+ return await gridApiOp(async (gridApi) => {
5508
+ const selectedRows = props.selectedRows;
5509
+ let ok = false;
5510
+ await modifyUpdating(props.field ?? '', selectedRows.map((data) => data.id), async () => {
5511
+ // MATT Disabled I don't believe these are needed anymore
5512
+ // I've left them here just in case they are
5513
+ // Need to refresh to get spinners to work on all rows
5514
+ // gridApi.refreshCells({ rowNodes: props.selectedRows as RowNode[], force: true });
5515
+ ok = await fnUpdate(selectedRows).catch((ex) => {
5516
+ console.error('Exception during modifyUpdating', ex);
5517
+ return false;
5518
+ });
5519
+ });
5512
5520
  // MATT Disabled I don't believe these are needed anymore
5513
5521
  // 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);
5522
+ // async processes need to refresh their own rows
5523
+ // gridApi.refreshCells({ rowNodes: selectedRows as RowNode[], force: true });
5524
+ if (gridApi.isDestroyed()) {
5525
+ return ok;
5532
5526
  }
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();
5527
+ if (ok) {
5528
+ const cell = gridApi.getFocusedCell();
5529
+ if (cell && gridApi.getFocusedCell() == null) {
5530
+ !gridApi.isDestroyed && gridApi.setFocusedCell(cell.rowIndex, cell.column);
5531
+ }
5532
+ // This is needed to trigger postSortRowsHook
5533
+ gridApi.refreshClientSideRowModel();
5548
5534
  }
5549
- }
5550
- return ok;
5551
- });
5552
- }, [gridApiOp, modifyUpdating, selectNextEditableCell]);
5535
+ void (async () => {
5536
+ // Only focus next cell if user hasn't already manually changed focus
5537
+ const postPopupFocusedCell = gridApi.getFocusedCell();
5538
+ if (prePopupFocusedCell.current &&
5539
+ postPopupFocusedCell &&
5540
+ prePopupFocusedCell.current.rowIndex == postPopupFocusedCell.rowIndex &&
5541
+ prePopupFocusedCell.current.column.getColId() == postPopupFocusedCell.column.getColId()) {
5542
+ if (!tabDirection || !(await selectNextEditableCell(tabDirection))) {
5543
+ onBulkEditingComplete();
5544
+ }
5545
+ }
5546
+ else {
5547
+ onBulkEditingComplete();
5548
+ }
5549
+ })();
5550
+ return ok;
5551
+ });
5552
+ }
5553
+ finally {
5554
+ setSaving?.(false);
5555
+ }
5556
+ }, [gridApiOp, modifyUpdating, onBulkEditingComplete, selectNextEditableCell]);
5553
5557
  const redrawRows = React.useMemo(() => debounce((rowNodes) => {
5554
5558
  try {
5555
5559
  gridApi && gridApi.redrawRows(rowNodes ? { rowNodes } : undefined);
@@ -5580,16 +5584,16 @@ const GridContextProvider = (props) => {
5580
5584
  preHash !== postHash && gridApi.clearFocusedCell();
5581
5585
  }
5582
5586
  }, 200), [gridApi]);
5583
- const addExternalFilter = (filter) => {
5587
+ const addExternalFilter = React.useCallback((filter) => {
5584
5588
  externalFilters.current.push(filter);
5585
5589
  void onFilterChanged();
5586
- };
5587
- const removeExternalFilter = (filter) => {
5590
+ }, [onFilterChanged]);
5591
+ const removeExternalFilter = React.useCallback((filter) => {
5588
5592
  lodashEs.remove(externalFilters.current, (v) => v === filter);
5589
5593
  void onFilterChanged();
5590
- };
5591
- const isExternalFilterPresent = () => !lodashEs.isEmpty(externalFilters.current);
5592
- const doesExternalFilterPass = (node) => externalFilters.current.every((filter) => filter(node.data, node));
5594
+ }, [onFilterChanged]);
5595
+ const isExternalFilterPresent = React.useCallback(() => !lodashEs.isEmpty(externalFilters.current), []);
5596
+ const doesExternalFilterPass = React.useCallback((node) => externalFilters.current.every((filter) => filter(node.data, node)), []);
5593
5597
  const getColDef = React.useCallback((colId) => (!!colId && gridApi?.getColumnDef(colId)) || undefined, [gridApi]);
5594
5598
  const showNoRowsOverlay = React.useCallback(() => {
5595
5599
  gridApi?.showNoRowsOverlay();
@@ -5645,7 +5649,6 @@ const GridContextProvider = (props) => {
5645
5649
  setInvisibleColumnIds,
5646
5650
  gridReady,
5647
5651
  prePopupOps,
5648
- postPopupOps,
5649
5652
  setApis,
5650
5653
  setQuickFilter,
5651
5654
  selectRowsById,
@@ -5666,8 +5669,7 @@ const GridContextProvider = (props) => {
5666
5669
  sizeColumnsToFit,
5667
5670
  autoSizeColumns,
5668
5671
  startCellEditing,
5669
- stopEditing,
5670
- cancelEdit,
5672
+ resetFocusedCellAfterCellEditing,
5671
5673
  updatingCells,
5672
5674
  redrawRows,
5673
5675
  externallySelectedItemsAreInSync,
@@ -5678,7 +5680,8 @@ const GridContextProvider = (props) => {
5678
5680
  isExternalFilterPresent,
5679
5681
  doesExternalFilterPass,
5680
5682
  downloadCsv,
5681
- setOnCellEditingComplete,
5683
+ onBulkEditingComplete,
5684
+ setOnBulkEditingComplete,
5682
5685
  showNoRowsOverlay,
5683
5686
  }, children: props.children }));
5684
5687
  };
@@ -5725,12 +5728,23 @@ const downloadCsvUseValueFormattersProcessCellCallback = (params) => {
5725
5728
  // We add an extra encodeToString here just in case valueFormatter is returning non string values
5726
5729
  return encodeToString(result);
5727
5730
  };
5731
+ const waitForCondition = async (condition, timeoutMs) => {
5732
+ const endTime = Date.now() + timeoutMs;
5733
+ while (Date.now() < endTime) {
5734
+ if (condition()) {
5735
+ return true;
5736
+ }
5737
+ await wait(100);
5738
+ }
5739
+ console.warn('waitForCondition failed');
5740
+ return false;
5741
+ };
5728
5742
 
5729
5743
  const GridUpdatingContextProvider = (props) => {
5730
5744
  const updatingBlocks = React.useRef({});
5731
5745
  const updating = React.useRef({});
5732
5746
  const [updatedDep, setUpdatedDep] = React.useState(0);
5733
- const resetUpdating = () => {
5747
+ const resetUpdating = React.useCallback(() => {
5734
5748
  const mergedUpdatingBlocks = {};
5735
5749
  for (const key in updatingBlocks.current) {
5736
5750
  const arr = lodashEs.flatten(updatingBlocks.current[key]);
@@ -5740,9 +5754,9 @@ const GridUpdatingContextProvider = (props) => {
5740
5754
  }
5741
5755
  updating.current = mergedUpdatingBlocks;
5742
5756
  setUpdatedDep((updatedDep) => updatedDep + 1);
5743
- };
5744
- const updatingCols = () => Object.keys(updating.current);
5745
- const modifyUpdating = async (fields, ids, fn) => {
5757
+ }, []);
5758
+ const updatingCols = React.useCallback(() => Object.keys(updating.current), []);
5759
+ const modifyUpdating = React.useCallback(async (fields, ids, fn) => {
5746
5760
  const idRef = [...ids];
5747
5761
  lodashEs.castArray(fields).forEach((field) => {
5748
5762
  const fieldUpdatingIds = updatingBlocks.current[field] ?? (updatingBlocks.current[field] = []);
@@ -5755,9 +5769,12 @@ const GridUpdatingContextProvider = (props) => {
5755
5769
  lodashEs.remove(fieldUpdatingIds, (idList) => idList === idRef);
5756
5770
  });
5757
5771
  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 }));
5772
+ }, [resetUpdating]);
5773
+ const anyUpdating = React.useCallback(() => {
5774
+ return !lodashEs.isEmpty(updating.current);
5775
+ }, []);
5776
+ const checkUpdating = React.useCallback((fields, id) => lodashEs.castArray(fields).some((f) => updating.current[f]?.includes(id)), []);
5777
+ return (jsxRuntime.jsx(GridUpdatingContext.Provider, { value: { modifyUpdating, anyUpdating, checkUpdating, updatingCols, updatedDep }, children: props.children }));
5761
5778
  };
5762
5779
 
5763
5780
  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%}";