@linzjs/step-ag-grid 4.0.3 → 5.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.
- package/dist/index.js +272 -238
- package/dist/index.js.map +1 -1
- package/dist/src/components/GridPopoverHook.d.ts +1 -13
- package/dist/src/components/gridForm/GridFormDropDown.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormPopoverMenu.d.ts +0 -1
- package/dist/src/contexts/GridContext.d.ts +1 -0
- package/dist/src/contexts/GridPopoverContext.d.ts +1 -1
- package/dist/src/contexts/GridUpdatingContext.d.ts +3 -2
- package/dist/src/contexts/GridUpdatingContextProvider.d.ts +1 -1
- package/dist/src/lui/FormError.d.ts +6 -0
- package/dist/src/react-menu3/components/MenuItem.d.ts +1 -1
- package/dist/src/react-menu3/components/SubMenu.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useBEM.d.ts +1 -1
- package/dist/src/react-menu3/types.d.ts +18 -17
- package/dist/src/react-menu3/utils/constants.d.ts +4 -0
- package/dist/src/react-menu3/utils/utils.d.ts +1 -1
- package/dist/step-ag-grid.esm.js +273 -239
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/GridPopoverHook.tsx +11 -80
- package/src/components/gridForm/GridFormDropDown.tsx +49 -43
- package/src/components/gridForm/GridFormEditBearing.tsx +1 -2
- package/src/components/gridForm/GridFormMultiSelect.tsx +12 -4
- package/src/components/gridForm/GridFormPopoverMenu.tsx +25 -22
- package/src/components/gridForm/GridFormSubComponentTextArea.tsx +1 -14
- package/src/components/gridForm/GridFormSubComponentTextInput.tsx +1 -18
- package/src/components/gridForm/GridFormTextArea.tsx +3 -2
- package/src/components/gridForm/GridFormTextInput.tsx +4 -7
- package/src/contexts/GridContext.tsx +4 -0
- package/src/contexts/GridContextProvider.tsx +8 -0
- package/src/contexts/GridPopoverContext.tsx +1 -1
- package/src/contexts/GridPopoverContextProvider.tsx +7 -4
- package/src/contexts/GridUpdatingContext.tsx +7 -1
- package/src/contexts/GridUpdatingContextProvider.tsx +17 -6
- package/src/lui/FormError.tsx +29 -0
- package/src/lui/TextAreaInput.tsx +2 -18
- package/src/lui/TextInputFormatted.tsx +2 -17
- package/src/react-menu3/components/ControlledMenu.tsx +114 -12
- package/src/react-menu3/components/MenuItem.tsx +19 -2
- package/src/react-menu3/types.ts +2 -0
- package/src/react-menu3/utils/constants.ts +4 -0
- package/src/stories/grid/FormTest.tsx +25 -25
- package/src/stories/grid/GridPopoutEditDropDown.stories.tsx +10 -0
- package/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx +1 -1
- package/src/stories/grid/GridReadOnly.stories.tsx +11 -11
- package/src/styles/GridFormDropDown.scss +1 -1
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
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 (
|
|
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
|
-
|
|
159
|
-
|
|
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 {
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
if (
|
|
203
|
-
|
|
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
|
-
}, [
|
|
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(
|
|
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
|
|
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,
|
|
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
|
-
|
|
190
|
-
|
|
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(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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(
|
|
103
|
-
|
|
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
|
|
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({
|
|
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={!
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
49
|
+
[invalid, value, initValue, props, field],
|
|
52
50
|
);
|
|
53
|
-
const { popoverWrapper
|
|
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
|
|
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
|
|
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)
|
|
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 (
|