@linzjs/step-ag-grid 29.1.4 → 29.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { LuiMiniSpinner, LuiStatusSpinner, LuiIcon, LuiButtonGroup, LuiButton, LuiCheckboxInput } from '@linzjs/lui';
3
3
  import { ModuleRegistry, AllCommunityModule } from 'ag-grid-community';
4
4
  import { AgGridReact } from 'ag-grid-react';
5
- import { negate, isEmpty, findIndex, defer, debounce as debounce$1, xorBy, last, difference, omit, sortBy, delay, partition, pick, groupBy, fromPairs, toPairs, isEqual, pull, compact, filter, sumBy, remove, flatten, castArray } from 'lodash-es';
5
+ import { negate, isEmpty, findIndex, defer, debounce as debounce$1, xorBy, last, difference, omit, sortBy, delay, partition, compact, pick, groupBy, fromPairs, toPairs, isEqual, pull, filter, sumBy, remove, flatten, castArray } from 'lodash-es';
6
6
  import React, { useRef, useLayoutEffect, useEffect, createContext, useContext, useState, useMemo, memo, forwardRef, useCallback, useReducer, cloneElement, useImperativeHandle, Fragment as Fragment$1, useId } from 'react';
7
7
  import { unstable_batchedUpdates, flushSync, createPortal } from 'react-dom';
8
8
 
@@ -2760,7 +2760,7 @@ ModuleRegistry.registerModules([AllCommunityModule]);
2760
2760
  /**
2761
2761
  * Wrapper for AgGrid to add commonly used functionality.
2762
2762
  */
2763
- 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 }) => {
2763
+ 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, onCellFocused: paramsOnCellFocused, ...params }) => {
2764
2764
  const { gridReady, gridRenderState, setApis, ensureRowVisible, getFirstRowId, selectRowsById, focusByRowById, ensureSelectedRowIsVisible, autoSizeColumns, sizeColumnsToFit, externallySelectedItemsAreInSync, setExternallySelectedItemsAreInSync, isExternalFilterPresent, doesExternalFilterPass, setOnBulkEditingComplete, getColDef, showNoRowsOverlay, prePopupOps, startCellEditing, } = useGridContext();
2765
2765
  const { updatedDep, updatingCols } = useContext(GridUpdatingContext);
2766
2766
  const gridDivRef = useRef(null);
@@ -3156,7 +3156,7 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3156
3156
  }
3157
3157
  }, [clearHighlightRowClasses]);
3158
3158
  const onCellFocused = useCallback((event) => {
3159
- if (!params.onCellFocused || event.rowIndex == null) {
3159
+ if (!paramsOnCellFocused || event.rowIndex == null) {
3160
3160
  return;
3161
3161
  }
3162
3162
  const api = event.api;
@@ -3170,8 +3170,16 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3170
3170
  if (!colDef || typeof colDef === 'string') {
3171
3171
  return;
3172
3172
  }
3173
- params.onCellFocused({ colDef, data });
3174
- }, [params]);
3173
+ // Prevent repeated callbacks to cell focus when it focus didn't change
3174
+ const { sourceEvent } = event;
3175
+ if (sourceEvent) {
3176
+ if (window.__stepaggrid_lastfocuseventtarget === sourceEvent.target) {
3177
+ return;
3178
+ }
3179
+ window.__stepaggrid_lastfocuseventtarget = sourceEvent.target;
3180
+ }
3181
+ paramsOnCellFocused({ colDef, data });
3182
+ }, [paramsOnCellFocused]);
3175
3183
  const onRowDragEnd = useCallback((event) => {
3176
3184
  clearHighlightRowClasses();
3177
3185
  gridElementRef.current = undefined;
@@ -3254,7 +3262,7 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
3254
3262
  enableClickSelection: params.enableClickSelection ?? false,
3255
3263
  mode: rowSelection == 'single' ? 'singleRow' : 'multiRow',
3256
3264
  }
3257
- : 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, onRowClicked: params.onRowClicked, onRowDoubleClicked: params.onRowDoubleClicked }) })] }));
3265
+ : 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, onRowClicked: params.onRowClicked, onRowDoubleClicked: params.onRowDoubleClicked, suppressStartEditOnTab: true }) })] }));
3258
3266
  };
3259
3267
  const quickFilterParser = (filterStr) => {
3260
3268
  // filter is exact matches exactly groups separated by commas
@@ -3399,14 +3407,10 @@ const GridCell = (props, custom) => {
3399
3407
  resizable: true,
3400
3408
  valueSetter: custom?.editor ? blockValueSetter : undefined,
3401
3409
  editable: props.editable ?? !!custom?.editor,
3402
- ...(custom?.editor
3403
- ? {
3404
- cellClassRules: GridCellMultiSelectClassRules,
3405
- cellEditor: GenericCellEditorComponentWrapper(custom?.editor),
3406
- }
3407
- : {
3408
- cellEditor: CellEditorToBlockEditing,
3409
- }),
3410
+ ...(custom?.editor && {
3411
+ cellClassRules: GridCellMultiSelectClassRules,
3412
+ cellEditor: GenericCellEditorComponentWrapper(custom?.editor),
3413
+ }),
3410
3414
  suppressKeyboardEvent: suppressCellKeyboardEvents,
3411
3415
  ...(custom?.editorParams
3412
3416
  ? {
@@ -3433,19 +3437,6 @@ const GridCell = (props, custom) => {
3433
3437
  },
3434
3438
  };
3435
3439
  };
3436
- /**
3437
- * Ag-grid will start its own editor if editable is true and there is no cell editor
3438
- * like in the case of a cell that is editable because it triggers a modal.
3439
- * This will block that editor.
3440
- */
3441
- const CellEditorToBlockEditing = ({ stopEditing }) => {
3442
- useEffect(() => {
3443
- defer(() => {
3444
- stopEditing();
3445
- });
3446
- }, [stopEditing]);
3447
- return jsx(Fragment, {});
3448
- };
3449
3440
  const GenericCellEditorComponentWrapper = (editor) => {
3450
3441
  const obj = { editor };
3451
3442
  return forwardRef(function GenericCellEditorComponentFr(cellEditorParams, _) {
@@ -4008,7 +3999,6 @@ const useGridPopoverHook = ({ className, save, invalid, dontSaveOnExternalClick,
4008
3999
  return {
4009
4000
  popoverWrapper,
4010
4001
  triggerSave,
4011
- gridPopoverOpen: isOpen,
4012
4002
  };
4013
4003
  };
4014
4004
 
@@ -4033,7 +4023,7 @@ const GridFormDropDown = (props) => {
4033
4023
  // Save triggers during async action processing which triggers another selectItem(), this ref blocks that
4034
4024
  const [filter, setFilter] = useState(props.filterDefaultValue ?? '');
4035
4025
  const [filteredValues, setFilteredValues] = useState();
4036
- const [options, setOptions] = useState(null);
4026
+ const [options, setOptions] = useState(!!propOptions && typeof propOptions !== 'function' ? propOptions : null);
4037
4027
  const subComponentIsValid = useRef(false);
4038
4028
  const subComponentInitialValue = useRef(null);
4039
4029
  const [subSelectedValue, setSubSelectedValue] = useState(null);
@@ -4100,55 +4090,58 @@ const GridFormDropDown = (props) => {
4100
4090
  selectedRows,
4101
4091
  subSelectedValue,
4102
4092
  ]);
4103
- const { popoverWrapper, gridPopoverOpen } = useGridPopoverHook({
4093
+ const { popoverWrapper } = useGridPopoverHook({
4104
4094
  className: props.className,
4105
4095
  invalid: () => !options || !!(selectedItem && !subComponentIsValid.current),
4106
4096
  save,
4107
4097
  dontSaveOnExternalClick: true,
4108
4098
  });
4109
4099
  // Load up options list if it's async function
4110
- const prevIsOpen = usePrevious(gridPopoverOpen);
4111
- const prevFilter = usePrevious(filter);
4112
4100
  useEffect(() => {
4113
- if ((gridPopoverOpen !== prevIsOpen || filter !== prevFilter) && gridPopoverOpen) {
4114
- let optionsConf = propOptions;
4115
- void (async () => {
4116
- if (typeof optionsConf === 'function') {
4117
- optionsConf = await optionsConf(selectedRows, filter);
4118
- }
4119
- if (optionsConf !== undefined) {
4120
- setOptions(optionsConf);
4121
- }
4122
- })();
4101
+ // If options is null then we need to load/reload
4102
+ // Options will be set to null during a reload based filter, or on open popup
4103
+ if (options !== null) {
4104
+ return;
4105
+ }
4106
+ // propOptions is a const list
4107
+ if (typeof propOptions !== 'function') {
4108
+ if (propOptions) {
4109
+ setOptions(propOptions);
4110
+ }
4111
+ return;
4123
4112
  }
4124
- }, [filter, gridPopoverOpen, options, prevFilter, prevIsOpen, propOptions, selectedRows]);
4113
+ // propOptions is function, probably loading from web
4114
+ void (async () => {
4115
+ const r = await propOptions(selectedRows, filter);
4116
+ setOptions(r ?? []);
4117
+ })();
4118
+ }, [filter, options, propOptions, selectedRows]);
4125
4119
  // Local filtering.
4126
4120
  useEffect(() => {
4127
- if (props.filtered == 'local') {
4128
- if (options == null)
4129
- return;
4130
- setFilteredValues(options
4131
- .map((option) => {
4132
- if (option.label != null && typeof option.label !== 'string') {
4133
- console.error('Cannot filter non-string labels', option);
4134
- return undefined;
4135
- }
4136
- return textMatch(option.label || '', filter) ? option : undefined;
4137
- })
4138
- .filter((r) => r !== undefined));
4121
+ if (props.filtered !== 'local') {
4122
+ return;
4123
+ }
4124
+ if (options == null) {
4125
+ setFilteredValues([]);
4126
+ return;
4139
4127
  }
4128
+ setFilteredValues(compact(options.map((option) => {
4129
+ if (option.label != null && typeof option.label !== 'string') {
4130
+ console.warn('GridFormDropDown: Cannot filter non-string labels', option);
4131
+ return undefined;
4132
+ }
4133
+ return textMatch(option.label || '', filter) ? option : undefined;
4134
+ })));
4140
4135
  }, [props.filtered, filter, options]);
4141
4136
  const reSearchOnFilterChange = useMemo(() => debounce(() => {
4142
4137
  setOptions(null);
4143
4138
  }, 500), []);
4144
- const previousFilter = useRef(filter);
4145
- // Reload filtering.
4139
+ const previousFilter = usePrevious(filter);
4146
4140
  useEffect(() => {
4147
- if (previousFilter.current != filter && props.filtered == 'reload') {
4148
- previousFilter.current = filter;
4141
+ if (previousFilter != null && previousFilter != filter && props.filtered === 'reload') {
4149
4142
  void reSearchOnFilterChange();
4150
4143
  }
4151
- }, [filter, props, reSearchOnFilterChange]);
4144
+ }, [filter, previousFilter, props.filtered, reSearchOnFilterChange]);
4152
4145
  let lastHeader = null;
4153
4146
  let showHeader = null;
4154
4147
  return popoverWrapper(jsxs(Fragment, { children: [props.filtered && (jsxs("div", { className: 'GridFormDropDown-filter', children: [jsx(FocusableItem, { className: 'filter-item', onFocus: () => {
@@ -5537,12 +5530,9 @@ const GridContextProvider = (props) => {
5537
5530
  const preRow = gridApi.getFocusedCell();
5538
5531
  // If we don't do this ag-grid will do its own continuation of an edit on tab, we don't want that as
5539
5532
  // we are managing it ourselves
5540
- gridApi.stopEditing();
5541
- if (tabDirection === 1) {
5542
- gridApi.tabToNextCell();
5543
- }
5544
- else {
5545
- gridApi.tabToPreviousCell();
5533
+ const didTab = tabDirection === 1 ? gridApi.tabToNextCell() : gridApi.tabToPreviousCell();
5534
+ if (!didTab) {
5535
+ break;
5546
5536
  }
5547
5537
  if (gridApi.isDestroyed()) {
5548
5538
  return true;