@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
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@linzjs/step-ag-grid",
3
3
  "repository": "github:linz/step-ag-grid.git",
4
4
  "license": "MIT",
5
- "version": "28.5.1",
5
+ "version": "29.0.0",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -28,8 +28,8 @@
28
28
  },
29
29
  "peerDependencies": {
30
30
  "@linzjs/lui": ">=21",
31
- "ag-grid-community": "^34.1.0",
32
- "ag-grid-react": "^34.1.0",
31
+ "ag-grid-community": "^34.2.0",
32
+ "ag-grid-react": "^34.2.0",
33
33
  "lodash-es": ">=4",
34
34
  "react": ">=18",
35
35
  "react-dom": ">=18"
@@ -76,21 +76,21 @@
76
76
  ]
77
77
  },
78
78
  "optionalDependencies": {
79
- "@rollup/rollup-linux-x64-gnu": "^4.46.2",
80
- "@swc/core-linux-x64-gnu": "^1.13.3"
79
+ "@rollup/rollup-linux-x64-gnu": "^4.52.3",
80
+ "@swc/core-linux-x64-gnu": "^1.13.20"
81
81
  },
82
82
  "devDependencies": {
83
- "@chromatic-com/storybook": "^4.0.1",
83
+ "@chromatic-com/storybook": "^4.1.1",
84
84
  "@linzjs/lui": "^23.11.1",
85
85
  "@linzjs/style": "^5.4.0",
86
86
  "@rollup/plugin-commonjs": "^28.0.6",
87
87
  "@rollup/plugin-json": "^6.1.0",
88
88
  "@rollup/plugin-node-resolve": "^16.0.1",
89
89
  "@semantic-release/git": "^10.0.1",
90
- "@storybook/addon-docs": "^9.1.0",
91
- "@storybook/addon-links": "^9.1.0",
92
- "@storybook/react": "^9.1.0",
93
- "@storybook/react-vite": "^9.1.0",
90
+ "@storybook/addon-docs": "^9.1.8",
91
+ "@storybook/addon-links": "^9.1.8",
92
+ "@storybook/react": "^9.1.8",
93
+ "@storybook/react-vite": "^9.1.8",
94
94
  "@testing-library/dom": "^10.4.1",
95
95
  "@testing-library/react": "^16.3.0",
96
96
  "@testing-library/user-event": "^14.6.1",
@@ -100,42 +100,41 @@
100
100
  "@types/node": "^22.17.0",
101
101
  "@types/react": "^18.3.23",
102
102
  "@types/react-dom": "^18.3.7",
103
- "@types/uuid": "^10.0.0",
104
103
  "@vitejs/plugin-react-swc": "^3.11.0",
105
104
  "@vitest/ui": "^3.2.4",
106
- "ag-grid-community": "^34.1.0",
107
- "ag-grid-react": "^34.1.0",
105
+ "ag-grid-community": "^34.2.0",
106
+ "ag-grid-react": "^34.2.0",
108
107
  "babel-preset-react-app": "^10.1.0",
109
108
  "canvas": "^3.2.0",
110
- "chromatic": "^13.1.3",
111
- "cross-env": "^10.0.0",
109
+ "chromatic": "^13.3.0",
110
+ "cross-env": "^10.1.0",
112
111
  "css-loader": "^7.1.2",
113
112
  "eslint-plugin-react": "^7.37.5",
114
- "eslint-plugin-storybook": "^9.1.0",
113
+ "eslint-plugin-storybook": "^9.1.8",
115
114
  "jsdom": "^26.1.0",
116
115
  "lodash-es": "^4.17.21",
117
116
  "mkdirp": "^3.0.1",
118
117
  "npm-run-all": "^4.1.5",
119
118
  "postcss": "^8.5.6",
120
- "postcss-loader": "^8.1.1",
119
+ "postcss-loader": "^8.2.0",
121
120
  "postcss-scss": "^4.0.9",
122
121
  "react": ">=18",
123
122
  "react-app-polyfill": "^3.0.0",
124
123
  "react-dom": "^18.3.1",
125
124
  "rimraf": "^6.0.1",
126
- "rollup": "^4.46.2",
125
+ "rollup": "^4.52.3",
127
126
  "rollup-plugin-copy": "^3.5.0",
128
127
  "rollup-plugin-peer-deps-external": "^2.2.4",
129
128
  "rollup-plugin-postcss": "^4.0.2",
130
129
  "rollup-plugin-typescript2": "^0.36.0",
131
- "sass": "^1.90.0",
130
+ "sass": "^1.93.2",
132
131
  "sass-loader": "^16.0.5",
133
- "semantic-release": "^24.2.7",
134
- "storybook": "^9.1.0",
132
+ "semantic-release": "^24.2.9",
133
+ "storybook": "^9.1.8",
135
134
  "storybook-css-modules-preset": "^1.1.1",
136
135
  "style-loader": "^4.0.0",
137
136
  "typescript": "^5.9.2",
138
- "vite": "^6.3.5",
137
+ "vite": "^7.1.7",
139
138
  "vite-plugin-html": "^3.2.2",
140
139
  "vite-tsconfig-paths": "^5.1.4",
141
140
  "vitest": "^3.2.4"
@@ -4,13 +4,14 @@ import {
4
4
  CellClassParams,
5
5
  CellClickedEvent,
6
6
  CellDoubleClickedEvent,
7
- CellEditingStartedEvent,
7
+ CellFocusedEvent,
8
8
  CellKeyDownEvent,
9
9
  ColDef,
10
10
  ColGroupDef,
11
11
  ColumnResizedEvent,
12
12
  EditableCallback,
13
13
  EditableCallbackParams,
14
+ GetRowIdParams,
14
15
  GridOptions,
15
16
  GridReadyEvent,
16
17
  ModelUpdatedEvent,
@@ -18,6 +19,7 @@ import {
18
19
  RowDragEndEvent,
19
20
  RowDragMoveEvent,
20
21
  SelectionChangedEvent,
22
+ SelectionColumnDef,
21
23
  } from 'ag-grid-community';
22
24
  import { AgGridReact } from 'ag-grid-react';
23
25
  import clsx from 'clsx';
@@ -25,7 +27,7 @@ import { defer, difference, isEmpty, last, omit, xorBy } from 'lodash-es';
25
27
  import { ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
26
28
  import { useInterval } from 'usehooks-ts';
27
29
 
28
- import { GridContext, useGridContext } from '../contexts/GridContext';
30
+ import { useGridContext } from '../contexts/GridContext';
29
31
  import { GridUpdatingContext } from '../contexts/GridUpdatingContext';
30
32
  import { fnOrVar, isNotEmpty } from '../utils/util';
31
33
  import { clickInputWhenContainingCellClicked } from './clickInputWhenContainingCellClicked';
@@ -36,36 +38,6 @@ import { usePostSortRowsHook } from './PostSortRowsHook';
36
38
 
37
39
  ModuleRegistry.registerModules([AllCommunityModule]);
38
40
 
39
- let timeOfLastSingleClick = 0;
40
- let lastClickColId: unknown;
41
- let lastClickRowIndex: unknown;
42
-
43
- const resetClickDebounce = () => {
44
- timeOfLastSingleClick = 0;
45
- lastClickColId = '';
46
- lastClickRowIndex = -1;
47
- };
48
-
49
- /**
50
- * If click is more than 200ms since last click return true.
51
- */
52
- const clickDebounceSkipClick = (colId: unknown, rowIndex: unknown): boolean => {
53
- const doubleClickMs = 200;
54
-
55
- if (
56
- Date.now() - timeOfLastSingleClick < doubleClickMs &&
57
- lastClickColId === colId &&
58
- lastClickRowIndex === rowIndex
59
- ) {
60
- // Skipping double click due to single click edit
61
- return true;
62
- }
63
- timeOfLastSingleClick = Date.now();
64
- lastClickColId = colId;
65
- lastClickRowIndex = rowIndex;
66
- return false;
67
- };
68
-
69
41
  export interface GridBaseRow {
70
42
  id: string | number;
71
43
  }
@@ -99,6 +71,7 @@ export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
99
71
  rowClassRules?: GridOptions['rowClassRules'];
100
72
  rowSelection?: 'single' | 'multiple';
101
73
  autoSelectFirstRow?: boolean;
74
+ onCellFocused?: (props: { colDef: ColDef<TData>; data: TData }) => void;
102
75
  onColumnMoved?: GridOptions['onColumnMoved'];
103
76
  rowDragText?: GridOptions['rowDragText'];
104
77
  onRowDragEnd?: (props: GridOnRowDragEndProps<TData>) => Promise<void> | void;
@@ -126,7 +99,7 @@ export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
126
99
  * When pressing tab whilst editing the grid will select and edit the next cell if available.
127
100
  * Once the last cell to edit closes this callback is called.
128
101
  */
129
- onCellEditingComplete?: () => void;
102
+ onBulkEditingComplete?: () => void;
130
103
 
131
104
  /**
132
105
  * Context menu definition if required.
@@ -182,13 +155,13 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
182
155
  setExternallySelectedItemsAreInSync,
183
156
  isExternalFilterPresent,
184
157
  doesExternalFilterPass,
185
- setOnCellEditingComplete,
158
+ setOnBulkEditingComplete,
186
159
  getColDef,
187
160
  showNoRowsOverlay,
188
161
  prePopupOps,
189
- stopEditing,
190
- } = useContext(GridContext);
191
- const { startCellEditing } = useGridContext();
162
+ startCellEditing,
163
+ } = useGridContext<TData>();
164
+
192
165
  const { updatedDep, updatingCols } = useContext(GridUpdatingContext);
193
166
 
194
167
  const gridDivRef = useRef<HTMLDivElement>(null);
@@ -441,28 +414,11 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
441
414
  event.api.showNoRowsOverlay();
442
415
  }, []);
443
416
 
444
- /**
445
- * Force-refresh all selected rows to re-run class function, to update selection highlighting
446
- */
447
- const refreshSelectedRows = useCallback((_event: CellEditingStartedEvent): void => {
448
- // MATT Disabled I don't believe these are needed anymore
449
- // I've left them here just in case they are
450
- /*event.api.refreshCells({
451
- force: true,
452
- rowNodes: event.api.getSelectedNodes(),
453
- });*/
454
- }, []);
455
-
456
417
  /**
457
418
  * Handle double click edit
458
419
  */
459
420
  const onCellDoubleClick = useCallback(
460
421
  (event: CellDoubleClickedEvent) => {
461
- if (clickDebounceSkipClick(event.colDef.colId, event.rowIndex)) {
462
- // the next click will be a single click, we want it to pass
463
- resetClickDebounce();
464
- return;
465
- }
466
422
  const editable = fnOrVar(event.colDef?.editable, event);
467
423
  if (editable && !invokeEditAction(event)) {
468
424
  void startCellEditing({ rowId: event.data.id, colId: event.column.getColId() });
@@ -478,9 +434,6 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
478
434
  (event: CellClickedEvent) => {
479
435
  const editable = fnOrVar(event.colDef?.editable, event);
480
436
  if ((editable && event.colDef.singleClickEdit) ?? singleClickEdit) {
481
- if (clickDebounceSkipClick(event.colDef.colId, event.rowIndex)) {
482
- return;
483
- }
484
437
  void startCellEditing({ rowId: event.data.id, colId: event.column.getColId() });
485
438
  }
486
439
  },
@@ -590,11 +543,10 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
590
543
  useEffect(() => {
591
544
  const newLoading = !rowData || params.loading === true;
592
545
  if (newLoading && !prevLoading.current) {
593
- stopEditing();
594
546
  showNoRowsOverlay();
595
547
  }
596
548
  prevLoading.current = newLoading;
597
- }, [params.loading, rowData, showNoRowsOverlay, stopEditing]);
549
+ }, [params.loading, rowData, showNoRowsOverlay]);
598
550
 
599
551
  /**
600
552
  * Resize columns to fit if required on window/container resize
@@ -667,6 +619,27 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
667
619
  [clearHighlightRowClasses],
668
620
  );
669
621
 
622
+ const onCellFocused = useCallback(
623
+ (event: CellFocusedEvent<TData>) => {
624
+ if (!params.onCellFocused || event.rowIndex == null) {
625
+ return;
626
+ }
627
+ const api = event.api;
628
+ const rowNode = api.getDisplayedRowAtIndex(event.rowIndex);
629
+ const data = rowNode?.data;
630
+ const column = event.column;
631
+ if (!data || !column || typeof column === 'string') {
632
+ return;
633
+ }
634
+ const colDef = column.getColDef();
635
+ if (!colDef || typeof colDef === 'string') {
636
+ return;
637
+ }
638
+ params.onCellFocused({ colDef, data });
639
+ },
640
+ [params],
641
+ );
642
+
670
643
  const onRowDragEnd = useCallback(
671
644
  (event: RowDragEndEvent<TData>) => {
672
645
  clearHighlightRowClasses();
@@ -698,10 +671,77 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
698
671
  }, [params.setExternalSelectedItems, selectable]);
699
672
 
700
673
  // This is setting a ref in the GridContext so won't be triggering an update loop
701
- setOnCellEditingComplete(params.onCellEditingComplete);
674
+ setOnBulkEditingComplete(params.onBulkEditingComplete);
675
+
676
+ const getRowId = useCallback((params: GetRowIdParams<TData, unknown>) => `${params.data.id}`, []);
677
+ const defaultColDef = useMemo(
678
+ (): ColDef<TData, unknown> => ({
679
+ minWidth: 48,
680
+ ...omit(params.defaultColDef, ['editable', 'field', 'colId', 'tooltipField']),
681
+ }),
682
+ [params.defaultColDef],
683
+ );
684
+
685
+ const noRowsOverlayComponent = useCallback(
686
+ (event: AgGridEvent) => {
687
+ const headerRowCount = columnDefs.some((c) => (c as any).children) ? 2 : 1;
688
+
689
+ let rowCount = 0;
690
+ event.api.forEachNode(() => rowCount++);
691
+ return (
692
+ <GridNoRowsOverlay
693
+ loading={!rowData || params.loading === true}
694
+ rowCount={rowCount}
695
+ headerRowHeight={headerRowCount * rowHeight}
696
+ filteredRowCount={event.api.getDisplayedRowCount()}
697
+ noRowsOverlayText={params.noRowsOverlayText}
698
+ noRowsMatchingOverlayText={params.noRowsMatchingOverlayText}
699
+ />
700
+ );
701
+ },
702
+ [columnDefs, params.loading, params.noRowsMatchingOverlayText, params.noRowsOverlayText, rowData, rowHeight],
703
+ );
704
+
705
+ const selectionColumnDef = useMemo((): SelectionColumnDef => {
706
+ const selectWidth = params.hideSelectColumn ? 0 : selectable && params.onRowDragEnd ? 76 : 48;
707
+ return {
708
+ suppressNavigable: params.hideSelectColumn,
709
+ rowDrag: !!params.onRowDragEnd,
710
+ minWidth: selectWidth,
711
+ maxWidth: selectWidth,
712
+ pinned: selectColumnPinned,
713
+ headerComponentParams: {
714
+ exportable: false,
715
+ },
716
+ headerClass: clsx('ag-header-hide-default-select', params.onRowDragEnd && 'ag-header-select-draggable'),
717
+ headerComponent: rowSelection == 'multiple' ? GridHeaderSelect : undefined,
718
+ suppressHeaderKeyboardEvent: (e) => {
719
+ if (!selectable) return false;
720
+ if ((e.event.key === 'Enter' || e.event.key === ' ') && !e.event.repeat) {
721
+ if (isEmpty(e.api.getSelectedRows())) {
722
+ e.api.selectAll('filtered');
723
+ } else {
724
+ e.api.deselectAll();
725
+ }
726
+ return true;
727
+ }
728
+ return false;
729
+ },
730
+ onCellClicked:
731
+ params.enableSelectionWithoutKeys || params.enableClickSelection
732
+ ? undefined
733
+ : clickInputWhenContainingCellClicked,
734
+ };
735
+ }, [
736
+ params.enableClickSelection,
737
+ params.enableSelectionWithoutKeys,
738
+ params.hideSelectColumn,
739
+ params.onRowDragEnd,
740
+ rowSelection,
741
+ selectColumnPinned,
742
+ selectable,
743
+ ]);
702
744
 
703
- const selectWidth = params.hideSelectColumn ? 0 : selectable && params.onRowDragEnd ? 76 : 48;
704
- const headerRowCount = columnDefs.some((c) => (c as any).children) ? 2 : 1;
705
745
  return (
706
746
  <div
707
747
  data-testid={dataTestId}
@@ -729,45 +769,30 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
729
769
  rowHeight={rowHeight}
730
770
  animateRows={params.animateRows ?? false}
731
771
  rowClassRules={params.rowClassRules}
732
- getRowId={(params) => `${params.data.id}`}
772
+ getRowId={getRowId}
733
773
  onGridSizeChanged={onGridSizeChanged}
734
774
  suppressColumnVirtualisation={suppressColumnVirtualization}
735
775
  suppressClickEdit={true}
736
776
  onColumnVisible={setInitialContentSize}
737
777
  onRowDataUpdated={onRowDataChanged}
778
+ onCellFocused={onCellFocused}
738
779
  onCellKeyDown={onCellKeyPress}
739
780
  onCellClicked={onCellClicked}
740
781
  onCellDoubleClicked={onCellDoubleClick}
741
- onCellEditingStarted={refreshSelectedRows}
742
782
  domLayout={params.domLayout}
743
783
  onColumnResized={onColumnResized}
744
- defaultColDef={{ minWidth: 48, ...omit(params.defaultColDef, ['editable']) }}
784
+ defaultColDef={defaultColDef}
745
785
  columnDefs={columnDefsAdjusted}
786
+ selectionColumnDef={selectionColumnDef}
746
787
  rowData={rowData}
747
- noRowsOverlayComponent={(event: AgGridEvent) => {
748
- let rowCount = 0;
749
- event.api.forEachNode(() => rowCount++);
750
- return (
751
- <GridNoRowsOverlay
752
- loading={!rowData || params.loading === true}
753
- rowCount={rowCount}
754
- headerRowHeight={headerRowCount * rowHeight}
755
- filteredRowCount={event.api.getDisplayedRowCount()}
756
- noRowsOverlayText={params.noRowsOverlayText}
757
- noRowsMatchingOverlayText={params.noRowsMatchingOverlayText}
758
- />
759
- );
760
- }}
761
- quickFilterParser={(filterStr) => {
762
- // filter is exact matches exactly groups separated by commas
763
- return filterStr.split(',').map((str) => str.trim());
764
- }}
788
+ postSortRows={params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows}
765
789
  onModelUpdated={onModelUpdated}
766
790
  onGridReady={onGridReady}
767
791
  onSortChanged={ensureSelectedRowIsVisible}
768
- postSortRows={params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows}
792
+ quickFilterParser={quickFilterParser}
769
793
  onSelectionChanged={synchroniseExternalStateToGridSelection}
770
794
  onColumnMoved={params.onColumnMoved}
795
+ noRowsOverlayComponent={noRowsOverlayComponent}
771
796
  alwaysShowVerticalScroll={params.alwaysShowVerticalScroll}
772
797
  isExternalFilterPresent={isExternalFilterPresent}
773
798
  doesExternalFilterPass={doesExternalFilterPass}
@@ -781,36 +806,13 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
781
806
  suppressCellFocus={params.suppressCellFocus}
782
807
  pinnedTopRowData={params.pinnedTopRowData}
783
808
  pinnedBottomRowData={params.pinnedBottomRowData}
784
- selectionColumnDef={{
785
- suppressNavigable: params.hideSelectColumn,
786
- rowDrag: !!params.onRowDragEnd,
787
- minWidth: selectWidth,
788
- maxWidth: selectWidth,
789
- pinned: selectColumnPinned,
790
- headerComponentParams: {
791
- exportable: false,
792
- },
793
- headerClass: clsx('ag-header-hide-default-select', params.onRowDragEnd && 'ag-header-select-draggable'),
794
- headerComponent: rowSelection == 'multiple' ? GridHeaderSelect : undefined,
795
- suppressHeaderKeyboardEvent: (e) => {
796
- if (!selectable) return false;
797
- if ((e.event.key === 'Enter' || e.event.key === ' ') && !e.event.repeat) {
798
- if (isEmpty(e.api.getSelectedRows())) {
799
- e.api.selectAll('filtered');
800
- } else {
801
- e.api.deselectAll();
802
- }
803
- return true;
804
- }
805
- return false;
806
- },
807
- onCellClicked:
808
- params.enableSelectionWithoutKeys || params.enableClickSelection
809
- ? undefined
810
- : clickInputWhenContainingCellClicked,
811
- }}
812
809
  />
813
810
  </div>
814
811
  </div>
815
812
  );
816
813
  };
814
+
815
+ const quickFilterParser = (filterStr: string) => {
816
+ // filter is exact matches exactly groups separated by commas
817
+ return filterStr.split(',').map((str) => str.trim());
818
+ };
@@ -1,6 +1,6 @@
1
- import { ReactElement, useCallback, useContext, useEffect, useRef, useState } from 'react';
1
+ import { ReactElement, useCallback, useEffect, useRef, useState } from 'react';
2
2
 
3
- import { GridContext } from '../contexts/GridContext';
3
+ import { useGridContext } from '../contexts/GridContext';
4
4
  import { useGridPopoverContext } from '../contexts/GridPopoverContext';
5
5
  import { ControlledMenu } from '../react-menu3';
6
6
  import { MenuCloseEvent } from '../react-menu3/types';
@@ -21,8 +21,8 @@ export interface GridPopoverHookProps<TData> {
21
21
  }
22
22
 
23
23
  export const useGridPopoverHook = <TData extends GridBaseRow>(props: GridPopoverHookProps<TData>) => {
24
- const { stopEditing, cancelEdit } = useContext(GridContext);
25
- const { anchorRef, saving, updateValue } = useGridPopoverContext<TData>();
24
+ const { onBulkEditingComplete } = useGridContext<TData>();
25
+ const { anchorRef, saving, updateValue, stopEditing } = useGridPopoverContext<TData>();
26
26
  const saveButtonRef = useRef<HTMLButtonElement>(null);
27
27
  const [isOpen, setOpen] = useState(false);
28
28
 
@@ -33,29 +33,32 @@ export const useGridPopoverHook = <TData extends GridBaseRow>(props: GridPopover
33
33
  const triggerSave = useCallback(
34
34
  async (reason?: string) => {
35
35
  if (reason == CloseReason.CANCEL) {
36
- cancelEdit();
36
+ stopEditing();
37
+ onBulkEditingComplete();
37
38
  return;
38
39
  }
39
- if (props.invalid && props.invalid()) {
40
+ if (props?.invalid?.()) {
41
+ // Don't close, don't do anything it's invalid
40
42
  return;
41
43
  }
42
44
 
43
45
  if (!props.save) {
44
- cancelEdit();
45
- } else if (props.save) {
46
- // forms that don't provide an invalid fn must wait until they have saved to close
47
- if (props.invalid) stopEditing();
48
- if (
49
- await updateValue(
50
- props.save,
51
- reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
52
- )
53
- ) {
54
- if (!props.invalid) stopEditing();
55
- }
46
+ // No save method so just close
47
+ stopEditing();
48
+ onBulkEditingComplete();
49
+ return;
50
+ }
51
+
52
+ if (
53
+ await updateValue(
54
+ props.save,
55
+ reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
56
+ )
57
+ ) {
58
+ stopEditing();
56
59
  }
57
60
  },
58
- [cancelEdit, props, stopEditing, updateValue],
61
+ [onBulkEditingComplete, props, stopEditing, updateValue],
59
62
  );
60
63
 
61
64
  const popoverWrapper = useCallback(
@@ -72,7 +75,9 @@ export const useGridPopoverHook = <TData extends GridBaseRow>(props: GridPopover
72
75
  menuClassName={'step-ag-grid-react-menu'}
73
76
  onClose={(event: MenuCloseEvent) => {
74
77
  // Prevent menu from closing when modals are invoked
75
- if (event.reason === CloseReason.BLUR) return;
78
+ if (event.reason === CloseReason.BLUR) {
79
+ return;
80
+ }
76
81
  void triggerSave(event.reason);
77
82
  }}
78
83
  viewScroll={'auto'}
@@ -1,10 +1,10 @@
1
- import { useContext, useEffect } from 'react';
1
+ import { useEffect } from 'react';
2
2
 
3
- import { GridContext, GridFilterExternal } from '../../contexts/GridContext';
3
+ import { GridFilterExternal, useGridContext } from '../../contexts/GridContext';
4
4
  import { GridBaseRow } from '../Grid';
5
5
 
6
6
  export const useGridFilter = <TData extends GridBaseRow>(filter: GridFilterExternal<TData> | undefined) => {
7
- const { addExternalFilter, removeExternalFilter } = useContext(GridContext);
7
+ const { addExternalFilter, removeExternalFilter } = useGridContext<TData>();
8
8
 
9
9
  useEffect(() => {
10
10
  const thisFilter = filter;
@@ -24,7 +24,7 @@ export const useGridContextMenu = <TData extends GridBaseRow>({
24
24
  contextMenuSelectRow: boolean;
25
25
  contextMenu?: GridContextMenuComponent<TData>;
26
26
  }) => {
27
- const { redrawRows, prePopupOps, postPopupOps, getSelectedRows } = useContext(GridContext);
27
+ const { redrawRows, prePopupOps, resetFocusedCellAfterCellEditing, getSelectedRows } = useContext(GridContext);
28
28
 
29
29
  const [isOpen, setOpen] = useState(false);
30
30
  const [anchorPoint, setAnchorPoint] = useState({ x: 0, y: 0 });
@@ -47,8 +47,8 @@ export const useGridContextMenu = <TData extends GridBaseRow>({
47
47
  const closeMenu = useCallback(() => {
48
48
  setOpen(false);
49
49
  redrawRows();
50
- postPopupOps();
51
- }, [postPopupOps, redrawRows]);
50
+ resetFocusedCellAfterCellEditing();
51
+ }, [resetFocusedCellAfterCellEditing, redrawRows]);
52
52
 
53
53
  const cellContextMenu = useCallback(
54
54
  (event: CellContextMenuEvent) => {
@@ -25,7 +25,6 @@ export interface GridContextType<TData extends GridBaseRow> {
25
25
  getColumnIds: (filter?: keyof ColDef | ((r: ColDef) => boolean | undefined | null | number | string)) => string[];
26
26
  setApis: (gridApi: GridApi | undefined, hasExternallySelectedItems: boolean, dataTestId?: string) => void;
27
27
  prePopupOps: () => void;
28
- postPopupOps: () => void;
29
28
  setQuickFilter: (quickFilter: string) => void;
30
29
  editingCells: () => boolean;
31
30
  getSelectedRows: <T extends GridBaseRow>() => T[];
@@ -44,9 +43,9 @@ export interface GridContextType<TData extends GridBaseRow> {
44
43
  getFirstRowId: () => number;
45
44
  autoSizeColumns: (props?: AutoSizeColumnsProps) => AutoSizeColumnsResult;
46
45
  sizeColumnsToFit: () => void;
47
- cancelEdit: () => void;
48
46
  startCellEditing: ({ rowId, colId }: { rowId: number; colId: string }) => Promise<void>;
49
- stopEditing: () => void;
47
+ // Restores the previous focus after cell editing
48
+ resetFocusedCellAfterCellEditing: () => void;
50
49
  updatingCells: (
51
50
  props: { selectedRows: GridBaseRow[]; field?: string },
52
51
  fnUpdate: (selectedRows: any[]) => Promise<boolean>,
@@ -64,7 +63,8 @@ export interface GridContextType<TData extends GridBaseRow> {
64
63
  invisibleColumnIds: string[] | undefined;
65
64
  setInvisibleColumnIds: (colIds: string[]) => void;
66
65
  downloadCsv: (csvExportParams?: CsvExportParams) => void;
67
- setOnCellEditingComplete: (callback: (() => void) | undefined) => void;
66
+ onBulkEditingComplete: () => void;
67
+ setOnBulkEditingComplete: (callback: (() => void) | undefined) => void;
68
68
  showNoRowsOverlay: () => void;
69
69
  }
70
70
 
@@ -90,9 +90,6 @@ export const GridContext = createContext<GridContextType<any>>({
90
90
  prePopupOps: () => {
91
91
  console.error('no context provider for prePopupOps');
92
92
  },
93
- postPopupOps: () => {
94
- console.error('no context provider for prePopupOps');
95
- },
96
93
  externallySelectedItemsAreInSync: false,
97
94
  setApis: () => {
98
95
  console.error('no context provider for setApis');
@@ -164,15 +161,12 @@ export const GridContext = createContext<GridContextType<any>>({
164
161
  console.error('no context provider for editingCells');
165
162
  return false;
166
163
  },
167
- cancelEdit: () => {
168
- console.error('no context provider for cancelEdit');
169
- },
170
164
  // eslint-disable-next-line @typescript-eslint/require-await
171
165
  startCellEditing: async () => {
172
166
  console.error('no context provider for startCellEditing');
173
167
  },
174
- stopEditing: () => {
175
- console.error('no context provider for stopEditing');
168
+ resetFocusedCellAfterCellEditing: () => {
169
+ console.error('no context provider for resetFocusedCellAfterCellEditing');
176
170
  },
177
171
  // eslint-disable-next-line @typescript-eslint/require-await
178
172
  updatingCells: async () => {
@@ -206,8 +200,11 @@ export const GridContext = createContext<GridContextType<any>>({
206
200
  downloadCsv: () => {
207
201
  console.error('no context provider for downloadCsv');
208
202
  },
209
- setOnCellEditingComplete: () => {
210
- console.error('no context provider for setOnCellEditingComplete');
203
+ onBulkEditingComplete: () => {
204
+ console.error('no context provider for onBulkEditingComplete');
205
+ },
206
+ setOnBulkEditingComplete: () => {
207
+ console.error('no context provider for setOnBulkEditingComplete');
211
208
  },
212
209
  showNoRowsOverlay: () => {
213
210
  console.error('no context provider for showLoadingOverlay');