@linzjs/step-ag-grid 4.0.3 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/index.js +278 -239
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/components/GridPopoverHook.d.ts +1 -13
  4. package/dist/src/components/gridForm/GridFormDropDown.d.ts +1 -1
  5. package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +1 -1
  6. package/dist/src/components/gridForm/GridFormPopoverMenu.d.ts +0 -1
  7. package/dist/src/contexts/GridContext.d.ts +1 -0
  8. package/dist/src/contexts/GridPopoverContext.d.ts +1 -1
  9. package/dist/src/contexts/GridUpdatingContext.d.ts +3 -2
  10. package/dist/src/contexts/GridUpdatingContextProvider.d.ts +1 -1
  11. package/dist/src/lui/FormError.d.ts +6 -0
  12. package/dist/src/react-menu3/components/MenuItem.d.ts +1 -1
  13. package/dist/src/react-menu3/components/SubMenu.d.ts +1 -1
  14. package/dist/src/react-menu3/hooks/useBEM.d.ts +1 -1
  15. package/dist/src/react-menu3/types.d.ts +18 -17
  16. package/dist/src/react-menu3/utils/constants.d.ts +4 -0
  17. package/dist/src/react-menu3/utils/utils.d.ts +1 -1
  18. package/dist/step-ag-grid.esm.js +279 -240
  19. package/dist/step-ag-grid.esm.js.map +1 -1
  20. package/package.json +1 -1
  21. package/src/components/GridPopoverHook.tsx +11 -80
  22. package/src/components/gridForm/GridFormDropDown.tsx +49 -43
  23. package/src/components/gridForm/GridFormEditBearing.tsx +1 -2
  24. package/src/components/gridForm/GridFormMultiSelect.tsx +12 -4
  25. package/src/components/gridForm/GridFormPopoverMenu.tsx +25 -22
  26. package/src/components/gridForm/GridFormSubComponentTextArea.tsx +1 -14
  27. package/src/components/gridForm/GridFormSubComponentTextInput.tsx +1 -18
  28. package/src/components/gridForm/GridFormTextArea.tsx +3 -2
  29. package/src/components/gridForm/GridFormTextInput.tsx +4 -7
  30. package/src/contexts/GridContext.tsx +4 -0
  31. package/src/contexts/GridContextProvider.tsx +8 -0
  32. package/src/contexts/GridPopoverContext.tsx +1 -1
  33. package/src/contexts/GridPopoverContextProvider.tsx +7 -4
  34. package/src/contexts/GridUpdatingContext.tsx +7 -1
  35. package/src/contexts/GridUpdatingContextProvider.tsx +17 -6
  36. package/src/lui/FormError.tsx +29 -0
  37. package/src/lui/TextAreaInput.tsx +2 -18
  38. package/src/lui/TextInputFormatted.tsx +2 -17
  39. package/src/react-menu3/components/ControlledMenu.tsx +121 -13
  40. package/src/react-menu3/components/MenuItem.tsx +19 -2
  41. package/src/react-menu3/types.ts +2 -0
  42. package/src/react-menu3/utils/constants.ts +4 -0
  43. package/src/stories/grid/FormTest.tsx +25 -25
  44. package/src/stories/grid/GridPopoutEditDropDown.stories.tsx +10 -0
  45. package/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx +1 -1
  46. package/src/stories/grid/GridReadOnly.stories.tsx +11 -11
  47. package/src/styles/GridFormDropDown.scss +1 -1
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": "4.0.3",
5
+ "version": "5.0.1",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -1,9 +1,10 @@
1
- import { KeyboardEvent, KeyboardEventHandler, useCallback, useContext, useEffect, useRef, useState } from "react";
1
+ import { useCallback, useContext, useEffect, useRef, useState } from "react";
2
2
  import { GridContext } from "../contexts/GridContext";
3
3
  import { GridBaseRow } from "./Grid";
4
4
  import { ControlledMenu } from "../react-menu3";
5
5
  import { useGridPopoverContext } from "../contexts/GridPopoverContext";
6
6
  import { MenuCloseEvent } from "../react-menu3/types";
7
+ import { CloseReason } from "../react-menu3/utils";
7
8
 
8
9
  export interface GridPopoverHookProps<RowType> {
9
10
  className: string | undefined;
@@ -36,7 +37,12 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
36
37
  } else if (props.save) {
37
38
  // forms that don't provide an invalid fn must wait until they have saved to close
38
39
  if (props.invalid) stopEditing();
39
- if (await updateValue(props.save)) {
40
+ if (
41
+ await updateValue(
42
+ props.save,
43
+ reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
44
+ )
45
+ ) {
40
46
  if (!props.invalid) stopEditing();
41
47
  }
42
48
  }
@@ -44,79 +50,6 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
44
50
  [props, stopEditing, updateValue],
45
51
  );
46
52
 
47
- const onlyInputKeyboardEventHandlers: {
48
- onKeyUp?: KeyboardEventHandler<HTMLElement> | undefined;
49
- onKeyDown?: KeyboardEventHandler<HTMLElement> | undefined;
50
- } = {
51
- onKeyUp: (e: KeyboardEvent) => {
52
- const isTextArea = (e.currentTarget as any).type === "textarea";
53
- if (e.key === "Enter" && !isTextArea) {
54
- e.preventDefault();
55
- e.stopPropagation();
56
- triggerSave().then();
57
- } else if (e.key === "Tab") {
58
- e.preventDefault();
59
- e.stopPropagation();
60
- }
61
- },
62
- onKeyDown: (e: KeyboardEvent) => {
63
- const isTextArea = (e.currentTarget as any).type === "textarea";
64
- if (e.key === "Enter" && !isTextArea) {
65
- e.preventDefault();
66
- e.stopPropagation();
67
- } else if (e.key === "Tab") {
68
- e.preventDefault();
69
- e.stopPropagation();
70
- !e.shiftKey && triggerSave().then();
71
- }
72
- },
73
- };
74
- const firstInputKeyboardEventHandlers: {
75
- onKeyUp?: KeyboardEventHandler<HTMLElement> | undefined;
76
- onKeyDown?: KeyboardEventHandler<HTMLElement> | undefined;
77
- } = {
78
- onKeyUp: (e: KeyboardEvent) => {
79
- if (e.key === "Tab" && e.shiftKey) {
80
- e.preventDefault();
81
- e.stopPropagation();
82
- }
83
- },
84
- onKeyDown: (e: KeyboardEvent) => {
85
- if (e.key === "Tab" && e.shiftKey) {
86
- e.preventDefault();
87
- e.stopPropagation();
88
- }
89
- },
90
- };
91
-
92
- const lastInputKeyboardEventHandlers: {
93
- onKeyUp?: KeyboardEventHandler<HTMLElement> | undefined;
94
- onKeyDown?: KeyboardEventHandler<HTMLElement> | undefined;
95
- } = {
96
- onKeyUp: (e: KeyboardEvent) => {
97
- const isTextArea = (e.currentTarget as any).type === "textarea";
98
- if (e.key === "Enter" && !isTextArea) {
99
- e.preventDefault();
100
- e.stopPropagation();
101
- triggerSave().then();
102
- } else if (e.key === "Tab" && !e.shiftKey) {
103
- e.preventDefault();
104
- e.stopPropagation();
105
- }
106
- },
107
- onKeyDown: (e: KeyboardEvent) => {
108
- const isTextArea = (e.currentTarget as any).type === "textarea";
109
- if (e.key === "Enter" && !isTextArea) {
110
- e.preventDefault();
111
- e.stopPropagation();
112
- } else if (e.key === "Tab" && !e.shiftKey) {
113
- e.preventDefault();
114
- e.stopPropagation();
115
- triggerSave().then();
116
- }
117
- },
118
- };
119
-
120
53
  const popoverWrapper = useCallback(
121
54
  (children: JSX.Element) => {
122
55
  return (
@@ -155,8 +88,9 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
155
88
  {children}
156
89
  <button
157
90
  ref={saveButtonRef}
158
- onClick={() => {
159
- triggerSave().then();
91
+ data-reason={""}
92
+ onClick={(e) => {
93
+ triggerSave(e.currentTarget.getAttribute("data-reason") ?? undefined).then();
160
94
  }}
161
95
  style={{ display: "none" }}
162
96
  />
@@ -171,8 +105,5 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
171
105
  return {
172
106
  popoverWrapper,
173
107
  triggerSave,
174
- onlyInputKeyboardEventHandlers,
175
- firstInputKeyboardEventHandlers,
176
- lastInputKeyboardEventHandlers,
177
108
  };
178
109
  };
@@ -1,10 +1,9 @@
1
1
  import "../../styles/GridFormDropDown.scss";
2
2
 
3
3
  import { FocusableItem, MenuDivider, MenuHeader, MenuItem } from "../../react-menu3";
4
- import { KeyboardEvent, useCallback, useContext, useEffect, useRef, useState } from "react";
4
+ import { useCallback, useEffect, useRef, useState } from "react";
5
5
  import { GridBaseRow } from "../Grid";
6
6
  import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
7
- import { GridContext } from "../../contexts/GridContext";
8
7
  import { delay } from "lodash-es";
9
8
  import debounce from "debounce-promise";
10
9
  import { CellEditorCommon } from "../GridCell";
@@ -12,6 +11,7 @@ import { useGridPopoverHook } from "../GridPopoverHook";
12
11
  import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
13
12
  import { GridSubComponentContext } from "contexts/GridSubComponentContext";
14
13
  import { ClickEvent, MenuInstance } from "../../react-menu3/types";
14
+ import { CloseReason } from "../../react-menu3/utils";
15
15
 
16
16
  export interface GridPopoutEditDropDownSelectedItem<RowType, ValueType> {
17
17
  // Note the row that was clicked on will be first
@@ -65,7 +65,6 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
65
65
  props: GridFormPopoutDropDownProps<RowType, ValueType>,
66
66
  ) => {
67
67
  const { selectedRows, field, updateValue, data } = useGridPopoverContext<RowType>();
68
- const { stopEditing } = useContext(GridContext);
69
68
 
70
69
  // Save triggers during async action processing which triggers another selectItem(), this ref blocks that
71
70
  const hasSubmitted = useRef(false);
@@ -78,21 +77,23 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
78
77
  const [selectedSubComponent, setSelectedSubComponent] = useState<FinalSelectOption<any> | null>(null);
79
78
 
80
79
  const selectItemHandler = useCallback(
81
- async (value: ValueType, subComponentValue?: ValueType): Promise<boolean> => {
80
+ async (value: ValueType, subComponentValue?: ValueType, reason?: string): Promise<boolean> => {
82
81
  if (hasSubmitted.current || (subComponentValue !== undefined && !subComponentIsValid.current)) return false;
83
82
  hasSubmitted.current = true;
84
-
85
- return updateValue(async (selectedRows) => {
86
- const hasChanged = selectedRows.some((row) => row[field as keyof RowType] !== value);
87
- if (hasChanged) {
88
- if (props.onSelectedItem) {
89
- await props.onSelectedItem({ selectedRows, value, subComponentValue });
90
- } else {
91
- selectedRows.forEach((row) => (row[field as keyof RowType] = value));
83
+ return updateValue(
84
+ async (selectedRows) => {
85
+ const hasChanged = selectedRows.some((row) => row[field as keyof RowType] !== value);
86
+ if (hasChanged) {
87
+ if (props.onSelectedItem) {
88
+ await props.onSelectedItem({ selectedRows, value, subComponentValue });
89
+ } else {
90
+ selectedRows.forEach((row) => (row[field as keyof RowType] = value));
91
+ }
92
92
  }
93
- }
94
- return true;
95
- });
93
+ return true;
94
+ },
95
+ reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
96
+ );
96
97
  },
97
98
  [field, props, updateValue],
98
99
  );
@@ -102,7 +103,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
102
103
  updateValue(async (selectedRows) => {
103
104
  props.onSelectFilter && (await props.onSelectFilter({ selectedRows, value }));
104
105
  return true;
105
- }),
106
+ }, 0),
106
107
  [props, updateValue],
107
108
  );
108
109
 
@@ -176,39 +177,37 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
176
177
  }
177
178
  }, [filter, props, researchOnFilterChange]);
178
179
 
179
- const onFilterKeyDown = useCallback(
180
- async (e: KeyboardEvent) => {
181
- if (!options) return;
182
- if (e.key == "Enter" || e.key == "Tab") {
183
- const activeOptions = options.filter((option) => !filteredValues.includes(option.value));
184
- if (activeOptions.length == 1) {
185
- await selectItemHandler(activeOptions[0].value);
186
- stopEditing();
187
- } else if (props.onSelectFilter) {
188
- await selectFilterHandler(filter);
189
- stopEditing();
190
- } else {
191
- e.preventDefault();
192
- e.stopPropagation();
193
- }
194
- }
195
- },
196
- [filteredValues, options, selectItemHandler, selectFilterHandler, stopEditing, filter, props],
197
- );
198
-
199
180
  const save = useCallback(async () => {
200
- // Handler for sub-selected value
201
- if (!selectedSubComponent) return true;
202
- if (selectedSubComponent.subComponent && !subComponentIsValid.current) return false;
203
- await selectItemHandler(selectedSubComponent.value as ValueType, subSelectedValue);
181
+ if (!options) return true;
182
+ const activeOptions = options.filter((option) => !filteredValues.includes(option.value));
183
+ if (activeOptions.length === 1) {
184
+ await selectItemHandler(activeOptions[0].value);
185
+ } else if (activeOptions.length === 0 && props.onSelectFilter) {
186
+ await selectFilterHandler(filter);
187
+ } else {
188
+ // Handler for sub-selected value
189
+ if (!selectedSubComponent) return true;
190
+ if (selectedSubComponent.subComponent && !subComponentIsValid.current) return false;
191
+ await selectItemHandler(selectedSubComponent.value as ValueType, subSelectedValue);
192
+ }
204
193
  return true;
205
- }, [selectItemHandler, selectedSubComponent, subSelectedValue]);
194
+ }, [
195
+ filter,
196
+ filteredValues,
197
+ options,
198
+ props.onSelectFilter,
199
+ selectFilterHandler,
200
+ selectItemHandler,
201
+ selectedSubComponent,
202
+ subSelectedValue,
203
+ ]);
206
204
 
207
205
  const { popoverWrapper } = useGridPopoverHook({
208
206
  className: props.className,
209
207
  invalid: () => !!(selectedSubComponent && !subComponentIsValid.current),
210
208
  save,
211
209
  });
210
+
212
211
  return popoverWrapper(
213
212
  <>
214
213
  {props.filtered && (
@@ -226,7 +225,6 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
226
225
  data-testid={"filteredMenu-free-text-input"}
227
226
  defaultValue={filter}
228
227
  onChange={(e) => setFilter(e.target.value.toLowerCase())}
229
- onKeyDown={(e) => onFilterKeyDown(e)}
230
228
  />
231
229
  </div>
232
230
  )}
@@ -263,7 +261,15 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
263
261
  }
264
262
  e.keepOpen = true;
265
263
  } else {
266
- selectItemHandler(item.value as ValueType).then();
264
+ selectItemHandler(
265
+ item.value as ValueType,
266
+ undefined,
267
+ e.key === "Tab"
268
+ ? e.shiftKey
269
+ ? CloseReason.TAB_BACKWARD
270
+ : CloseReason.TAB_FORWARD
271
+ : CloseReason.CLICK,
272
+ ).then();
267
273
  }
268
274
  }}
269
275
  >
@@ -50,7 +50,7 @@ export const GridFormEditBearing = <RowType extends GridBaseRow>(props: GridForm
50
50
  },
51
51
  [defaultValue, field, props, value],
52
52
  );
53
- const { popoverWrapper, onlyInputKeyboardEventHandlers } = useGridPopoverHook({
53
+ const { popoverWrapper } = useGridPopoverHook({
54
54
  className: props.className,
55
55
  invalid,
56
56
  save,
@@ -65,7 +65,6 @@ export const GridFormEditBearing = <RowType extends GridBaseRow>(props: GridForm
65
65
  }}
66
66
  autoFocus={true}
67
67
  placeholder={props.placeHolder}
68
- {...onlyInputKeyboardEventHandlers}
69
68
  formatted={bearingStringValidator(value, props.range) ? "?" : convertDDToDMS(bearingNumberParser(value))}
70
69
  error={bearingStringValidator(value, props.range)}
71
70
  helpText={"Press enter or tab to save"}
@@ -61,6 +61,11 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
61
61
  const optionsInitialising = useRef(false);
62
62
  const [options, setOptions] = useState<MultiFinalSelectOption<ValueType>[]>();
63
63
 
64
+ const invalid = useCallback(() => {
65
+ const validations = pick(subComponentIsValid.current, Object.keys(selectedValues));
66
+ return Object.values(validations).some((v) => !v);
67
+ }, [selectedValues]);
68
+
64
69
  const save = useCallback(
65
70
  async (selectedRows: RowType[]): Promise<boolean> => {
66
71
  if (props.onSave) {
@@ -149,8 +154,9 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
149
154
  [selectedValues],
150
155
  );
151
156
 
152
- const { popoverWrapper, lastInputKeyboardEventHandlers, triggerSave } = useGridPopoverHook({
157
+ const { popoverWrapper, triggerSave } = useGridPopoverHook({
153
158
  className: props.className,
159
+ invalid,
154
160
  save,
155
161
  });
156
162
  return popoverWrapper(
@@ -186,10 +192,12 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
186
192
  <div key={`${index}`}>
187
193
  <MenuItem
188
194
  onClick={(e: ClickEvent) => {
189
- e.keepOpen = true;
190
- toggleValue(item);
195
+ // Global react-menu MenuItem handler handles tabs
196
+ if (e.key !== "Tab") {
197
+ e.keepOpen = true;
198
+ toggleValue(item);
199
+ }
191
200
  }}
192
- {...lastInputKeyboardEventHandlers}
193
201
  >
194
202
  <LuiCheckboxInput
195
203
  isChecked={`${item.value}` in selectedValues}
@@ -7,6 +7,7 @@ import { CellEditorCommon } from "../GridCell";
7
7
  import { GridSubComponentContext } from "../../contexts/GridSubComponentContext";
8
8
  import { ClickEvent } from "../../react-menu3/types";
9
9
  import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
10
+ import { CloseReason } from "../../react-menu3/utils";
10
11
 
11
12
  export interface GridFormPopoutMenuProps<RowType extends GridBaseRow> extends CellEditorCommon {
12
13
  options: (selectedRows: RowType[]) => Promise<MenuOption<RowType>[]>;
@@ -28,7 +29,6 @@ export interface MenuOption<RowType extends GridBaseRow> {
28
29
  label: JSX.Element | string | MenuSeparatorType;
29
30
  action?: (selectedRows: RowType[], menuOption: SelectedMenuOptionResult<RowType>) => Promise<void>;
30
31
  disabled?: string | boolean;
31
- supportsMultiEdit: boolean;
32
32
  hidden?: boolean;
33
33
  subComponent?: (props: any) => JSX.Element;
34
34
  }
@@ -79,54 +79,57 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
79
79
  }, [options, props.defaultAction, props.options, selectedRows]);
80
80
 
81
81
  const actionClick = useCallback(
82
- async (menuOption: MenuOption<RowType>) => {
82
+ async (menuOption: MenuOption<RowType>, reason: string) => {
83
83
  actionProcessing.current = true;
84
- return updateValue(async () => {
85
- const result = { ...menuOption, subValue: subSelectedValue };
86
- await (menuOption.action ?? defaultAction)(selectedRows, result);
87
- actionProcessing.current = false;
88
- return true;
89
- });
84
+ return updateValue(
85
+ async () => {
86
+ const result = { ...menuOption, subValue: subSelectedValue };
87
+ await (menuOption.action ?? defaultAction)(selectedRows, result);
88
+ actionProcessing.current = false;
89
+ return true;
90
+ },
91
+ reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
92
+ );
90
93
  },
91
94
  [defaultAction, selectedRows, subSelectedValue, updateValue],
92
95
  );
93
96
 
94
97
  const onMenuItemClick = useCallback(
95
- (e: ClickEvent, item: MenuOption<RowType>) => {
98
+ async (e: ClickEvent, item: MenuOption<RowType>) => {
96
99
  if (item.subComponent) {
97
100
  subComponentIsValid.current = false;
98
101
  setSubSelectedValue(null);
99
102
  setSubComponentSelected(subComponentSelected === item ? null : item);
100
103
  e.keepOpen = true;
101
104
  } else {
102
- actionClick(item).then();
103
- e.keepOpen = true;
105
+ await actionClick(
106
+ item,
107
+ e.key === "Tab" ? (e.shiftKey ? CloseReason.TAB_BACKWARD : CloseReason.TAB_FORWARD) : CloseReason.CLICK,
108
+ ).then();
104
109
  }
105
110
  },
106
111
  [actionClick, subComponentSelected],
107
112
  );
108
113
 
109
- const selectedRowCount = selectedRows.length;
110
-
111
- const filteredOptions = options?.filter((menuOption) => {
112
- return menuOption.label === PopoutMenuSeparator || selectedRowCount === 1 || menuOption.supportsMultiEdit;
113
- });
114
-
115
114
  const save = useCallback(async () => {
116
115
  // if a subcomponent is open we assume that it's meant to be saved.
117
116
  if (!actionProcessing.current && subComponentSelected) {
118
117
  if (!subComponentIsValid.current) return false;
119
- await actionClick(subComponentSelected);
120
- return false;
118
+ await actionClick(subComponentSelected, "click");
119
+ return true;
121
120
  }
122
121
  // Otherwise assume it's a cancel, either way we close the menu
123
122
  return true;
124
123
  }, [actionClick, subComponentSelected]);
125
124
 
126
- const { popoverWrapper, triggerSave } = useGridPopoverHook({ className: props.className, save });
125
+ const { popoverWrapper, triggerSave } = useGridPopoverHook({
126
+ className: props.className,
127
+ invalid: () => subComponentSelected && !subComponentIsValid.current,
128
+ save,
129
+ });
127
130
 
128
131
  return popoverWrapper(
129
- <ComponentLoadingWrapper loading={!filteredOptions} className={"GridFormPopupMenu"}>
132
+ <ComponentLoadingWrapper loading={!options} className={"GridFormPopupMenu"}>
130
133
  <>
131
134
  {options?.map((item, index) =>
132
135
  item.label === PopoutMenuSeparator ? (
@@ -137,7 +140,7 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
137
140
  <MenuItem
138
141
  key={`${item.label}`}
139
142
  onClick={(e: ClickEvent) => onMenuItemClick(e, item)}
140
- disabled={!!item.disabled || !filteredOptions?.includes(item)}
143
+ disabled={!!item.disabled}
141
144
  title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
142
145
  >
143
146
  {item.label as JSX.Element | string}
@@ -19,7 +19,7 @@ export interface GridSubComponentTextAreaProps<RowType extends GridBaseRow>
19
19
  export const GridFormSubComponentTextArea = <RowType extends GridBaseRow>(
20
20
  props: GridSubComponentTextAreaProps<RowType>,
21
21
  ): JSX.Element => {
22
- const { value, data, setValue, setValid, triggerSave } = useContext(GridSubComponentContext);
22
+ const { value, data, setValue, setValid } = useContext(GridSubComponentContext);
23
23
 
24
24
  const helpText = props.helpText ?? "Press tab to save";
25
25
 
@@ -44,19 +44,6 @@ export const GridFormSubComponentTextArea = <RowType extends GridBaseRow>(
44
44
  helpText={helpText}
45
45
  autoFocus={true}
46
46
  placeholder={props.placeholder}
47
- onKeyDown={(e) => {
48
- if (e.key === "Tab") {
49
- e.preventDefault();
50
- e.stopPropagation();
51
- }
52
- }}
53
- onKeyUp={(e) => {
54
- if (e.key === "Tab") {
55
- e.preventDefault();
56
- e.stopPropagation();
57
- !e.shiftKey && triggerSave().then();
58
- }
59
- }}
60
47
  />
61
48
  </div>
62
49
  );
@@ -17,7 +17,7 @@ export interface GridFormSubComponentTextInputProps<RowType extends GridBaseRow>
17
17
  export const GridFormSubComponentTextInput = <RowType extends GridBaseRow>(
18
18
  props: GridFormSubComponentTextInputProps<RowType>,
19
19
  ): JSX.Element => {
20
- const { value, setValue, setValid, triggerSave, data } = useContext(GridSubComponentContext);
20
+ const { value, setValue, setValid, data } = useContext(GridSubComponentContext);
21
21
 
22
22
  const helpText = props.helpText ?? "Press enter or tab to save";
23
23
 
@@ -39,23 +39,6 @@ export const GridFormSubComponentTextInput = <RowType extends GridBaseRow>(
39
39
  onChange={(e) => setValue(e.target.value)}
40
40
  helpText={helpText}
41
41
  autoFocus={true}
42
- onKeyDown={(e) => {
43
- if (e.key === "Tab" || e.key === "Enter") {
44
- e.preventDefault();
45
- e.stopPropagation();
46
- }
47
- }}
48
- onKeyUp={(e) => {
49
- if (e.key === "Tab") {
50
- e.preventDefault();
51
- e.stopPropagation();
52
- !e.shiftKey && triggerSave().then();
53
- } else if (e.key === "Enter") {
54
- triggerSave().then();
55
- e.preventDefault();
56
- e.stopPropagation();
57
- }
58
- }}
59
42
  placeholder={props.placeholder}
60
43
  style={{
61
44
  width: "100%",
@@ -42,11 +42,13 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(props: GridFormTex
42
42
  },
43
43
  [initialVale, value, props, field],
44
44
  );
45
- const { popoverWrapper, onlyInputKeyboardEventHandlers } = useGridPopoverHook({
45
+
46
+ const { popoverWrapper } = useGridPopoverHook({
46
47
  className: props.className,
47
48
  invalid,
48
49
  save,
49
50
  });
51
+
50
52
  return popoverWrapper(
51
53
  <div style={{ display: "flex", flexDirection: "row", width: props.width ?? 240 }}>
52
54
  <TextAreaInput
@@ -55,7 +57,6 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(props: GridFormTex
55
57
  error={invalid()}
56
58
  placeholder={props.placeholder}
57
59
  helpText={helpText}
58
- {...onlyInputKeyboardEventHandlers}
59
60
  />
60
61
  </div>,
61
62
  );
@@ -1,11 +1,10 @@
1
- import { useCallback, useContext, useMemo, useState } from "react";
1
+ import { useCallback, useMemo, useState } from "react";
2
2
  import { TextInputFormatted } from "../../lui/TextInputFormatted";
3
3
  import { useGridPopoverHook } from "../GridPopoverHook";
4
4
  import { GridBaseRow } from "../Grid";
5
5
  import { CellEditorCommon } from "../GridCell";
6
6
  import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
7
7
  import { TextInputValidator, TextInputValidatorProps } from "../../utils/textValidator";
8
- import { GridContext } from "../../contexts/GridContext";
9
8
 
10
9
  export interface GridFormTextInputProps<RowType extends GridBaseRow>
11
10
  extends TextInputValidatorProps<RowType>,
@@ -18,7 +17,6 @@ export interface GridFormTextInputProps<RowType extends GridBaseRow>
18
17
  }
19
18
 
20
19
  export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormTextInputProps<RowType>) => {
21
- const { stopEditing } = useContext(GridContext);
22
20
  const { field, value: initialVale, data } = useGridPopoverContext<RowType>();
23
21
 
24
22
  const helpText = props.helpText ?? "Press enter or tab to save";
@@ -31,7 +29,7 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormTe
31
29
  const save = useCallback(
32
30
  async (selectedRows: RowType[]): Promise<boolean> => {
33
31
  if (invalid()) return false;
34
- stopEditing();
32
+
35
33
  const trimmedValue = value.trim();
36
34
  if (initValue === trimmedValue) return true;
37
35
 
@@ -48,9 +46,9 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormTe
48
46
  });
49
47
  return true;
50
48
  },
51
- [invalid, stopEditing, value, initValue, props, field],
49
+ [invalid, value, initValue, props, field],
52
50
  );
53
- const { popoverWrapper, onlyInputKeyboardEventHandlers } = useGridPopoverHook({
51
+ const { popoverWrapper } = useGridPopoverHook({
54
52
  className: props.className,
55
53
  invalid,
56
54
  save,
@@ -65,7 +63,6 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormTe
65
63
  formatted={props.units}
66
64
  style={{ width: "100%" }}
67
65
  placeholder={props.placeholder}
68
- {...onlyInputKeyboardEventHandlers}
69
66
  helpText={helpText}
70
67
  />
71
68
  </div>,
@@ -25,6 +25,7 @@ export interface GridContextType {
25
25
  setSaving?: (saving: boolean) => void,
26
26
  ) => Promise<boolean>;
27
27
  redrawRows: (rowNodes?: RowNode[]) => void;
28
+ selectNextCell: (tabDirection: -1 | 0 | 1) => void;
28
29
  }
29
30
 
30
31
  export const GridContext = createContext<GridContextType>({
@@ -87,4 +88,7 @@ export const GridContext = createContext<GridContextType>({
87
88
  redrawRows: () => {
88
89
  console.error("no context provider for redrawRows");
89
90
  },
91
+ selectNextCell: () => {
92
+ console.error("no context provider for selectNextCell");
93
+ },
90
94
  });
@@ -275,6 +275,13 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
275
275
  });
276
276
  };
277
277
 
278
+ const selectNextCell = (tabDirection: -1 | 0 | 1 = 0) => {
279
+ gridApiOp((gridApi) => {
280
+ if (tabDirection == 1) gridApi.tabToNextCell();
281
+ if (tabDirection == -1) gridApi.tabToPreviousCell();
282
+ });
283
+ };
284
+
278
285
  return (
279
286
  <GridContext.Provider
280
287
  value={{
@@ -296,6 +303,7 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
296
303
  stopEditing,
297
304
  updatingCells,
298
305
  redrawRows,
306
+ selectNextCell,
299
307
  }}
300
308
  >
301
309
  {props.children}
@@ -17,7 +17,7 @@ export interface GridPopoverContextType<RowType extends GridBaseRow> {
17
17
  value: any;
18
18
  data: RowType;
19
19
  selectedRows: RowType[];
20
- updateValue: (saveFn: (selectedRows: any[]) => Promise<boolean>) => Promise<boolean>;
20
+ updateValue: (saveFn: (selectedRows: any[]) => Promise<boolean>, tabDirection: 1 | 0 | -1) => Promise<boolean>;
21
21
  }
22
22
 
23
23
  export const GridPopoverContext = createContext<GridPopoverContextType<any>>({
@@ -11,7 +11,7 @@ interface GridPopoverContextProps {
11
11
  }
12
12
 
13
13
  export const GridPopoverContextProvider = ({ props, children }: GridPopoverContextProps) => {
14
- const { getSelectedRows, updatingCells, stopEditing } = useContext(GridContext);
14
+ const { getSelectedRows, updatingCells, stopEditing, selectNextCell } = useContext(GridContext);
15
15
  const anchorRef = useRef<Element>(props.eGridCell);
16
16
 
17
17
  const [saving, setSaving] = useState(false);
@@ -27,16 +27,19 @@ export const GridPopoverContextProvider = ({ props, children }: GridPopoverConte
27
27
  const field = props.colDef?.field ?? "";
28
28
 
29
29
  const updateValue = useCallback(
30
- async (saveFn: (selectedRows: any[]) => Promise<boolean>): Promise<boolean> => {
30
+ async (saveFn: (selectedRows: any[]) => Promise<boolean>, tabDirection: 1 | 0 | -1): Promise<boolean> => {
31
31
  let result = false;
32
32
  if (!saving) {
33
33
  result = await updatingCells({ selectedRows, field }, saveFn, setSaving);
34
- if (result) stopEditing();
34
+ if (result) {
35
+ stopEditing();
36
+ tabDirection && selectNextCell(tabDirection);
37
+ }
35
38
  }
36
39
 
37
40
  return result;
38
41
  },
39
- [field, saving, selectedRows, stopEditing, updatingCells],
42
+ [field, saving, selectNextCell, selectedRows, stopEditing, updatingCells],
40
43
  );
41
44
 
42
45
  return (