@linzjs/step-ag-grid 8.2.1 → 8.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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": "8.2.1",
5
+ "version": "8.3.0",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -10,7 +10,7 @@ import { usePostSortRowsHook } from "./PostSortRowsHook";
10
10
  import { fnOrVar, isNotEmpty } from "../utils/util";
11
11
  import { GridHeaderSelect } from "./gridHeader/GridHeaderSelect";
12
12
  import { GridUpdatingContext } from "../contexts/GridUpdatingContext";
13
- import { CellClassParams } from "ag-grid-community/dist/lib/entities/colDef";
13
+ import { CellClassParams, EditableCallback, EditableCallbackParams } from "ag-grid-community/dist/lib/entities/colDef";
14
14
 
15
15
  export interface GridBaseRow {
16
16
  id: string | number;
@@ -46,6 +46,7 @@ export const Grid = (params: GridProps): JSX.Element => {
46
46
  const {
47
47
  gridReady,
48
48
  setGridApi,
49
+ prePopupOps,
49
50
  setQuickFilter,
50
51
  ensureRowVisible,
51
52
  selectRowsById,
@@ -161,6 +162,16 @@ export const Grid = (params: GridProps): JSX.Element => {
161
162
  updateQuickFilter();
162
163
  }, [updateQuickFilter]);
163
164
 
165
+ const combineEditables =
166
+ (...editables: (boolean | EditableCallback | undefined)[]) =>
167
+ (params: EditableCallbackParams): boolean => {
168
+ const results = editables.map((editable) => fnOrVar(editable, params));
169
+ // If editable is not set anywhere then it's non-editable
170
+ if (results.every((v) => v == null)) return false;
171
+ // If any editable value is or returns false then it's non-editable
172
+ return !results.some((v) => v == false);
173
+ };
174
+
164
175
  /**
165
176
  * Synchronise externally selected items to grid on externalSelectedItems change
166
177
  */
@@ -170,14 +181,14 @@ export const Grid = (params: GridProps): JSX.Element => {
170
181
 
171
182
  const columnDefs = useMemo((): ColDef[] => {
172
183
  const adjustColDefs = params.columnDefs.map((colDef) => {
184
+ const colDefEditable = colDef.editable;
185
+ const editable = combineEditables(params.readOnly !== false, params.defaultColDef?.editable, colDefEditable);
173
186
  return {
174
187
  ...colDef,
175
- editable: params.readOnly ? false : params.defaultColDef?.editable ?? colDef.editable,
188
+ editable,
176
189
  cellClassRules: {
177
190
  ...colDef.cellClassRules,
178
- "GridCell-readonly": (ccp: CellClassParams) =>
179
- (params.readOnly != null || !params.readOnly) &&
180
- !fnOrVar(params.defaultColDef?.editable ?? colDef.editable, ccp),
191
+ "GridCell-readonly": (ccp: CellClassParams) => !editable(ccp as any as EditableCallbackParams),
181
192
  },
182
193
  };
183
194
  });
@@ -186,9 +197,8 @@ export const Grid = (params: GridProps): JSX.Element => {
186
197
  {
187
198
  colId: "selection",
188
199
  editable: false,
189
- initialWidth: 35,
190
- minWidth: 35,
191
- maxWidth: 35,
200
+ minWidth: 42,
201
+ maxWidth: 42,
192
202
  suppressSizeToFit: true,
193
203
  checkboxSelection: true,
194
204
  headerComponent: GridHeaderSelect,
@@ -241,6 +251,7 @@ export const Grid = (params: GridProps): JSX.Element => {
241
251
 
242
252
  const startCellEditing = useCallback(
243
253
  (event: CellEvent) => {
254
+ prePopupOps();
244
255
  if (!event.node.isSelected()) {
245
256
  event.node.setSelected(true, true);
246
257
  }
@@ -256,7 +267,7 @@ export const Grid = (params: GridProps): JSX.Element => {
256
267
  });
257
268
  }
258
269
  },
259
- [checkUpdating],
270
+ [checkUpdating, prePopupOps],
260
271
  );
261
272
 
262
273
  const onCellDoubleClick = useCallback(
@@ -278,6 +289,7 @@ export const Grid = (params: GridProps): JSX.Element => {
278
289
  const invokeEditAction = (e: CellEvent): boolean => {
279
290
  const editAction = e.colDef?.cellRendererParams?.editAction;
280
291
  if (!editAction) return false;
292
+
281
293
  const editable = fnOrVar(e.colDef?.editable, e);
282
294
  if (editable) {
283
295
  if (!e.node.isSelected()) {
@@ -67,7 +67,7 @@ export const suppressCellKeyboardEvents = (e: SuppressKeyboardEventParams) => {
67
67
  };
68
68
 
69
69
  /*
70
- * All cells should use this
70
+ * All cells should use this.
71
71
  */
72
72
  export const GridCell = <RowType extends GridBaseRow, Props extends CellEditorCommon>(
73
73
  props: GenericCellColDef<RowType>,
@@ -81,6 +81,7 @@ export const GridCell = <RowType extends GridBaseRow, Props extends CellEditorCo
81
81
  colId: props.field,
82
82
  sortable: !!(props?.field || props?.valueGetter),
83
83
  resizable: true,
84
+ editable: props.editable ?? false,
84
85
  ...(custom?.editor && {
85
86
  cellClassRules: GridCellMultiSelectClassRules,
86
87
  editable: props.editable ?? true,
@@ -188,6 +188,23 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormDro
188
188
  return true;
189
189
  }, [filter, filteredValues, options, props, selectItemHandler, selectedItem, selectedRows, subSelectedValue]);
190
190
 
191
+ const onMouseEnterFocus = useCallback((item: FinalSelectOption) => {
192
+ setSelectedItem(item);
193
+ if (item.subComponent) {
194
+ subComponentIsValid.current = true;
195
+ subComponentInitialValue.current = null;
196
+ } else {
197
+ setSubSelectedValue(null);
198
+ subComponentIsValid.current = true;
199
+ }
200
+ }, []);
201
+
202
+ const onMouseLeaveFocus = useCallback(() => {
203
+ setSelectedItem(null);
204
+ setSubSelectedValue(null);
205
+ subComponentIsValid.current = true;
206
+ }, []);
207
+
191
208
  const { popoverWrapper } = useGridPopoverHook({
192
209
  className: props.className,
193
210
  invalid: () => !!(selectedItem && !subComponentIsValid.current),
@@ -268,17 +285,9 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormDro
268
285
  disabled={!!item.disabled}
269
286
  title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
270
287
  value={item.value}
271
- onFocus={() => {
272
- setSelectedItem(item);
273
- if (item.subComponent) {
274
- setSelectedItem(item);
275
- subComponentIsValid.current = true;
276
- subComponentInitialValue.current = null;
277
- } else {
278
- setSubSelectedValue(null);
279
- subComponentIsValid.current = true;
280
- }
281
- }}
288
+ onFocus={() => onMouseEnterFocus(item)}
289
+ onMouseEnter={() => onMouseEnterFocus(item)}
290
+ onMouseLeave={onMouseLeaveFocus}
282
291
  onClick={(e: ClickEvent) => {
283
292
  if (item.subComponent) {
284
293
  e.keepOpen = true;
@@ -13,8 +13,8 @@ export const GridPopoverMenu = <RowType extends GridBaseRow>(
13
13
  ): ColDefT<RowType> =>
14
14
  GridCell<RowType, GridFormPopoverMenuProps<RowType>>(
15
15
  {
16
- minWidth: 40,
17
- maxWidth: 40,
16
+ minWidth: 48,
17
+ maxWidth: 48,
18
18
  width: 40,
19
19
  editable: colDef.editable != null ? colDef.editable : true,
20
20
  cellStyle: { justifyContent: "center" },
@@ -5,6 +5,7 @@ import { GridBaseRow } from "../components/Grid";
5
5
  export interface GridContextType {
6
6
  gridReady: boolean;
7
7
  setGridApi: (gridApi: GridApi | undefined) => void;
8
+ prePopupOps: () => void;
8
9
  setQuickFilter: (quickFilter: string) => void;
9
10
  editingCells: () => boolean;
10
11
  getSelectedRows: <T extends GridBaseRow>() => T[];
@@ -34,6 +35,9 @@ export interface GridContextType {
34
35
 
35
36
  export const GridContext = createContext<GridContextType>({
36
37
  gridReady: false,
38
+ prePopupOps: () => {
39
+ console.error("no context provider for prePopupOps");
40
+ },
37
41
  externallySelectedItemsAreInSync: false,
38
42
  setGridApi: () => {
39
43
  console.error("no context provider for setGridApi");
@@ -5,6 +5,7 @@ import { defer, delay, difference, isEmpty, last, sortBy } from "lodash-es";
5
5
  import { isNotEmpty, wait } from "../utils/util";
6
6
  import { GridUpdatingContext } from "./GridUpdatingContext";
7
7
  import { GridBaseRow } from "../components/Grid";
8
+ import { CellPosition } from "ag-grid-community/dist/lib/entities/cellPosition";
8
9
 
9
10
  interface GridContextProps {
10
11
  children: ReactNode;
@@ -20,6 +21,7 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
20
21
  const [gridApi, _setGridApi] = useState<GridApi>();
21
22
  const [gridReady, setGridReady] = useState(false);
22
23
  const idsBeforeUpdate = useRef<number[]>([]);
24
+ const prePopupFocusedCell = useRef<CellPosition>();
23
25
  const [externallySelectedItemsAreInSync, setExternallySelectedItemsAreInSync] = useState(false);
24
26
 
25
27
  const setGridApi = useCallback((gridApi: GridApi | undefined) => {
@@ -43,6 +45,13 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
43
45
  [gridApi],
44
46
  );
45
47
 
48
+ /**
49
+ * Before a popup record the currently focused cell.
50
+ */
51
+ const prePopupOps = useCallback(() => {
52
+ prePopupFocusedCell.current = gridApi?.getFocusedCell() ?? undefined;
53
+ }, [gridApi]);
54
+
46
55
  /**
47
56
  * Set the quick filter value to grid.
48
57
  */
@@ -276,7 +285,12 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
276
285
  });
277
286
  }, [gridApiOp]);
278
287
 
279
- const stopEditing = useCallback((): void => gridApiOp((gridApi) => gridApi.stopEditing()), [gridApiOp]);
288
+ const stopEditing = useCallback((): void => {
289
+ if (prePopupFocusedCell.current) {
290
+ gridApi?.setFocusedCell(prePopupFocusedCell.current.rowIndex, prePopupFocusedCell.current.column);
291
+ }
292
+ gridApiOp((gridApi) => gridApi.stopEditing());
293
+ }, [gridApi, gridApiOp]);
280
294
 
281
295
  const selectNextCell = useCallback(
282
296
  (tabDirection: -1 | 0 | 1 = 0) => {
@@ -297,8 +311,6 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
297
311
  ): Promise<boolean> => {
298
312
  setSaving && setSaving(true);
299
313
  return await gridApiOp(async (gridApi) => {
300
- const preOpCell = gridApi.getFocusedCell();
301
-
302
314
  const selectedRows = props.selectedRows;
303
315
 
304
316
  let ok = false;
@@ -331,13 +343,13 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
331
343
  }
332
344
 
333
345
  // Only focus next cell if user hasn't already manually changed focus
334
- const postOpCell = gridApi.getFocusedCell();
346
+ const postPopupFocusedCell = gridApi.getFocusedCell();
335
347
  if (
336
348
  tabDirection &&
337
- preOpCell &&
338
- postOpCell &&
339
- preOpCell.rowIndex == postOpCell.rowIndex &&
340
- preOpCell.column.getColId() == postOpCell.column.getColId()
349
+ prePopupFocusedCell.current &&
350
+ postPopupFocusedCell &&
351
+ prePopupFocusedCell.current.rowIndex == postPopupFocusedCell.rowIndex &&
352
+ prePopupFocusedCell.current.column.getColId() == postPopupFocusedCell.column.getColId()
341
353
  ) {
342
354
  selectNextCell(tabDirection);
343
355
  }
@@ -368,6 +380,7 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
368
380
  <GridContext.Provider
369
381
  value={{
370
382
  gridReady,
383
+ prePopupOps,
371
384
  setGridApi,
372
385
  setQuickFilter,
373
386
  selectRowsById,
@@ -168,17 +168,15 @@ const GridNonEditableRowTemplate: ComponentStory<typeof Grid> = (props: GridProp
168
168
  return (
169
169
  <Grid
170
170
  {...props}
171
- selectable={true}
172
171
  externalSelectedItems={externalSelectedItems}
173
172
  setExternalSelectedItems={setExternalSelectedItems}
174
173
  defaultColDef={defaultColDef}
175
174
  columnDefs={columnDefs}
176
175
  rowData={rowData}
177
176
  domLayout={"autoHeight"}
178
- autoSelectFirstRow={true}
179
- readOnly={false}
180
177
  />
181
178
  );
182
179
  };
183
180
 
184
181
  export const NonEditableRow = GridNonEditableRowTemplate.bind({});
182
+ NonEditableRow.args = { autoSelectFirstRow: true, selectable: true };