@linzjs/step-ag-grid 3.0.1 → 4.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 +390 -320
- package/dist/index.js.map +1 -1
- package/dist/src/components/GridPopoverHook.d.ts +13 -1
- package/dist/src/components/gridForm/GridFormSubComponentTextArea.d.ts +11 -0
- package/dist/src/components/gridForm/GridFormSubComponentTextInput.d.ts +8 -8
- package/dist/src/components/gridForm/GridFormTextArea.d.ts +3 -4
- package/dist/src/components/gridForm/GridFormTextInput.d.ts +3 -4
- package/dist/src/index.d.ts +6 -3
- package/dist/src/lui/TextAreaInput.d.ts +4 -5
- package/dist/src/lui/TextInputFormatted.d.ts +4 -10
- package/dist/src/utils/textValidator.d.ts +6 -0
- package/dist/step-ag-grid.esm.js +387 -321
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Grid.tsx +2 -2
- package/src/components/GridCell.tsx +5 -0
- package/src/components/GridPopoverHook.tsx +79 -1
- package/src/components/gridForm/GridFormDropDown.tsx +63 -56
- package/src/components/gridForm/GridFormEditBearing.tsx +7 -8
- package/src/components/gridForm/GridFormMultiSelect.tsx +6 -10
- package/src/components/gridForm/GridFormPopoverMenu.tsx +7 -10
- package/src/components/gridForm/GridFormSubComponentTextArea.tsx +58 -0
- package/src/components/gridForm/GridFormSubComponentTextInput.tsx +48 -55
- package/src/components/gridForm/GridFormTextArea.tsx +10 -18
- package/src/components/gridForm/GridFormTextInput.tsx +12 -25
- package/src/contexts/GridPopoverContextProvider.tsx +11 -4
- package/src/index.ts +6 -3
- package/src/lui/TextAreaInput.tsx +34 -18
- package/src/lui/TextInputFormatted.tsx +19 -35
- package/src/react-menu3/components/MenuItem.tsx +5 -2
- package/src/stories/grid/FormTest.tsx +16 -3
- package/src/stories/grid/GridPopoutEditDropDown.stories.tsx +20 -17
- package/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx +2 -2
- package/src/stories/grid/GridReadOnly.stories.tsx +15 -3
- package/src/utils/textValidator.ts +24 -0
- package/dist/src/components/GridSubComponentTextArea.d.ts +0 -10
- package/src/components/GridSubComponentTextArea.tsx +0 -62
package/package.json
CHANGED
package/src/components/Grid.tsx
CHANGED
|
@@ -202,7 +202,7 @@ export const Grid = (params: GridProps): JSX.Element => {
|
|
|
202
202
|
[startCellEditing],
|
|
203
203
|
);
|
|
204
204
|
|
|
205
|
-
const
|
|
205
|
+
const onCellKeyPress = useCallback(
|
|
206
206
|
(e: CellEvent) => {
|
|
207
207
|
if ((e.event as KeyboardEvent).key === "Enter") startCellEditing(e);
|
|
208
208
|
},
|
|
@@ -254,7 +254,7 @@ export const Grid = (params: GridProps): JSX.Element => {
|
|
|
254
254
|
onFirstDataRendered={sizeColumnsToFit}
|
|
255
255
|
onGridSizeChanged={sizeColumnsToFit}
|
|
256
256
|
suppressClickEdit={true}
|
|
257
|
-
|
|
257
|
+
onCellKeyPress={onCellKeyPress}
|
|
258
258
|
onCellClicked={onCellClicked}
|
|
259
259
|
onCellDoubleClicked={onCellDoubleClick}
|
|
260
260
|
onCellEditingStarted={refreshSelectedRows}
|
|
@@ -68,6 +68,11 @@ export const GridCell = <RowType extends GridBaseRow, Props extends CellEditorCo
|
|
|
68
68
|
editable: props.editable ?? true,
|
|
69
69
|
cellEditor: GenericCellEditorComponentWrapper(custom),
|
|
70
70
|
}),
|
|
71
|
+
suppressKeyboardEvent: (e) => {
|
|
72
|
+
// It's important that aggrid doesn't trigger edit on enter
|
|
73
|
+
// as the incorrect selected rows will be returned
|
|
74
|
+
return e.event.key === "Enter";
|
|
75
|
+
},
|
|
71
76
|
...(custom?.editorParams && {
|
|
72
77
|
cellEditorParams: { ...custom.editorParams, multiEdit: custom.multiEdit },
|
|
73
78
|
}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
1
|
+
import { KeyboardEvent, KeyboardEventHandler, 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";
|
|
@@ -30,6 +30,79 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
|
|
|
30
30
|
[props.save, stopEditing, updateValue],
|
|
31
31
|
);
|
|
32
32
|
|
|
33
|
+
const onlyInputKeyboardEventHandlers: {
|
|
34
|
+
onKeyUp?: KeyboardEventHandler<HTMLElement> | undefined;
|
|
35
|
+
onKeyDown?: KeyboardEventHandler<HTMLElement> | undefined;
|
|
36
|
+
} = {
|
|
37
|
+
onKeyUp: (e: KeyboardEvent) => {
|
|
38
|
+
const isTextArea = (e.currentTarget as any).type === "textarea";
|
|
39
|
+
if (e.key === "Enter" && !isTextArea) {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
e.stopPropagation();
|
|
42
|
+
triggerSave().then();
|
|
43
|
+
} else if (e.key === "Tab") {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
e.stopPropagation();
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
49
|
+
const isTextArea = (e.currentTarget as any).type === "textarea";
|
|
50
|
+
if (e.key === "Enter" && !isTextArea) {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
e.stopPropagation();
|
|
53
|
+
} else if (e.key === "Tab") {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
e.stopPropagation();
|
|
56
|
+
!e.shiftKey && triggerSave().then();
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
const firstInputKeyboardEventHandlers: {
|
|
61
|
+
onKeyUp?: KeyboardEventHandler<HTMLElement> | undefined;
|
|
62
|
+
onKeyDown?: KeyboardEventHandler<HTMLElement> | undefined;
|
|
63
|
+
} = {
|
|
64
|
+
onKeyUp: (e: KeyboardEvent) => {
|
|
65
|
+
if (e.key === "Tab" && e.shiftKey) {
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
e.stopPropagation();
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
71
|
+
if (e.key === "Tab" && e.shiftKey) {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
e.stopPropagation();
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const lastInputKeyboardEventHandlers: {
|
|
79
|
+
onKeyUp?: KeyboardEventHandler<HTMLElement> | undefined;
|
|
80
|
+
onKeyDown?: KeyboardEventHandler<HTMLElement> | undefined;
|
|
81
|
+
} = {
|
|
82
|
+
onKeyUp: (e: KeyboardEvent) => {
|
|
83
|
+
const isTextArea = (e.currentTarget as any).type === "textarea";
|
|
84
|
+
if (e.key === "Enter" && !isTextArea) {
|
|
85
|
+
e.preventDefault();
|
|
86
|
+
e.stopPropagation();
|
|
87
|
+
triggerSave().then();
|
|
88
|
+
} else if (e.key === "Tab" && !e.shiftKey) {
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
e.stopPropagation();
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
94
|
+
const isTextArea = (e.currentTarget as any).type === "textarea";
|
|
95
|
+
if (e.key === "Enter" && !isTextArea) {
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
e.stopPropagation();
|
|
98
|
+
} else if (e.key === "Tab" && !e.shiftKey) {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
e.stopPropagation();
|
|
101
|
+
triggerSave().then();
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
33
106
|
const popoverWrapper = useCallback(
|
|
34
107
|
(children: JSX.Element) => {
|
|
35
108
|
return (
|
|
@@ -43,6 +116,8 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
|
|
|
43
116
|
saveButtonRef={saveButtonRef}
|
|
44
117
|
menuClassName={"step-ag-grid-react-menu"}
|
|
45
118
|
onClose={(event: MenuCloseEvent) => {
|
|
119
|
+
// Prevent menu from closing when modals are invoked
|
|
120
|
+
if (event.reason === "blur") return;
|
|
46
121
|
triggerSave(event.reason).then();
|
|
47
122
|
}}
|
|
48
123
|
viewScroll={"auto"}
|
|
@@ -76,5 +151,8 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
|
|
|
76
151
|
return {
|
|
77
152
|
popoverWrapper,
|
|
78
153
|
triggerSave,
|
|
154
|
+
onlyInputKeyboardEventHandlers,
|
|
155
|
+
firstInputKeyboardEventHandlers,
|
|
156
|
+
lastInputKeyboardEventHandlers,
|
|
79
157
|
};
|
|
80
158
|
};
|
|
@@ -10,6 +10,8 @@ import debounce from "debounce-promise";
|
|
|
10
10
|
import { CellEditorCommon } from "../GridCell";
|
|
11
11
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
12
12
|
import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
13
|
+
import { GridSubComponentContext } from "contexts/GridSubComponentContext";
|
|
14
|
+
import { ClickEvent, MenuInstance } from "../../react-menu3/types";
|
|
13
15
|
|
|
14
16
|
export interface GridPopoutEditDropDownSelectedItem<RowType, ValueType> {
|
|
15
17
|
// Note the row that was clicked on will be first
|
|
@@ -69,11 +71,15 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
69
71
|
const [filteredValues, setFilteredValues] = useState<any[]>([]);
|
|
70
72
|
const optionsInitialising = useRef(false);
|
|
71
73
|
const [options, setOptions] = useState<FinalSelectOption<ValueType>[] | null>(null);
|
|
72
|
-
const
|
|
74
|
+
const subComponentIsValid = useRef(false);
|
|
75
|
+
const [subSelectedValue, setSubSelectedValue] = useState<any>();
|
|
76
|
+
const [selectedSubComponent, setSelectedSubComponent] = useState<any>();
|
|
73
77
|
|
|
74
78
|
const selectItemHandler = useCallback(
|
|
75
|
-
async (value: ValueType, subComponentValue?: ValueType): Promise<boolean> =>
|
|
76
|
-
|
|
79
|
+
async (value: ValueType, subComponentValue?: ValueType): Promise<boolean> => {
|
|
80
|
+
if (subComponentValue !== undefined && !subComponentIsValid.current) return false;
|
|
81
|
+
|
|
82
|
+
return updateValue(async (selectedRows) => {
|
|
77
83
|
const hasChanged = selectedRows.some((row) => row[field as keyof RowType] !== value);
|
|
78
84
|
if (hasChanged) {
|
|
79
85
|
if (props.onSelectedItem) {
|
|
@@ -83,7 +89,8 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
83
89
|
}
|
|
84
90
|
}
|
|
85
91
|
return true;
|
|
86
|
-
})
|
|
92
|
+
});
|
|
93
|
+
},
|
|
87
94
|
[field, props, updateValue],
|
|
88
95
|
);
|
|
89
96
|
|
|
@@ -185,7 +192,16 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
185
192
|
},
|
|
186
193
|
[filteredValues, options, selectItemHandler, selectFilterHandler, stopEditing, filter, props],
|
|
187
194
|
);
|
|
188
|
-
|
|
195
|
+
|
|
196
|
+
const save = useCallback(async () => {
|
|
197
|
+
// Handler for sub-selected value
|
|
198
|
+
if (!selectedSubComponent) return true;
|
|
199
|
+
if (!subComponentIsValid.current) return false;
|
|
200
|
+
await selectItemHandler(selectedSubComponent.value as ValueType, subSelectedValue);
|
|
201
|
+
return true;
|
|
202
|
+
}, [selectItemHandler, selectedSubComponent, subSelectedValue]);
|
|
203
|
+
|
|
204
|
+
const { popoverWrapper } = useGridPopoverHook({ className: props.className, save });
|
|
189
205
|
return popoverWrapper(
|
|
190
206
|
<>
|
|
191
207
|
{props.filtered && (
|
|
@@ -223,59 +239,50 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
223
239
|
<MenuHeader key={`$$header_${index}`}>{item.label}</MenuHeader>
|
|
224
240
|
) : filteredValues.includes(item.value) ? null : (
|
|
225
241
|
<div key={`menu-wrapper-${index}`}>
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
242
|
+
<MenuItem
|
|
243
|
+
key={`${fieldToString(field)}-${index}`}
|
|
244
|
+
disabled={!!item.disabled}
|
|
245
|
+
title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
|
|
246
|
+
value={item.value}
|
|
247
|
+
onClick={(e: ClickEvent) => {
|
|
248
|
+
if (item.subComponent) {
|
|
249
|
+
if (selectedSubComponent === item) {
|
|
250
|
+
setSelectedSubComponent(null);
|
|
251
|
+
subComponentIsValid.current = true;
|
|
252
|
+
} else {
|
|
253
|
+
setSelectedSubComponent(item);
|
|
254
|
+
}
|
|
255
|
+
e.keepOpen = true;
|
|
256
|
+
} else {
|
|
233
257
|
selectItemHandler(item.value as ValueType).then();
|
|
234
|
-
}}
|
|
235
|
-
>
|
|
236
|
-
{item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)}
|
|
237
|
-
</MenuItem>
|
|
238
|
-
) : (
|
|
239
|
-
<FocusableItem className={"LuiDeprecatedForms"} key={`${fieldToString(field)}-${index}_subcomponent`}>
|
|
240
|
-
{(ref: any) =>
|
|
241
|
-
item.subComponent && (
|
|
242
|
-
<item.subComponent
|
|
243
|
-
setValue={(value: any) => {
|
|
244
|
-
const localSubComponentValues = [...subComponentValues];
|
|
245
|
-
const subComponentValueIndex = localSubComponentValues.findIndex(
|
|
246
|
-
({ optionValue }) => optionValue === item.value,
|
|
247
|
-
);
|
|
248
|
-
if (subComponentValueIndex !== -1) {
|
|
249
|
-
localSubComponentValues[subComponentValueIndex].subComponentValue = value;
|
|
250
|
-
} else {
|
|
251
|
-
localSubComponentValues.push({
|
|
252
|
-
subComponentValue: value,
|
|
253
|
-
optionValue: item.value,
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
setSubComponentValues(localSubComponentValues);
|
|
257
|
-
}}
|
|
258
|
-
keyDown={(key: string, event: KeyboardEvent<HTMLInputElement>) => {
|
|
259
|
-
const subComponentItem = subComponentValues.find(
|
|
260
|
-
({ optionValue }) => optionValue === item.value,
|
|
261
|
-
);
|
|
262
|
-
if ((key === "Enter" || key === "Tab") && subComponentItem) {
|
|
263
|
-
event.preventDefault();
|
|
264
|
-
event.stopPropagation();
|
|
265
|
-
return selectItemHandler(
|
|
266
|
-
item.value as ValueType,
|
|
267
|
-
subComponentItem.subComponentValue,
|
|
268
|
-
).then(() => {
|
|
269
|
-
ref.closeMenu();
|
|
270
|
-
return true;
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
return false;
|
|
274
|
-
}}
|
|
275
|
-
key={`${fieldToString(field)}-${index}_subcomponent_inner`}
|
|
276
|
-
/>
|
|
277
|
-
)
|
|
278
258
|
}
|
|
259
|
+
}}
|
|
260
|
+
>
|
|
261
|
+
{item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)}
|
|
262
|
+
</MenuItem>
|
|
263
|
+
|
|
264
|
+
{item.subComponent && selectedSubComponent === item && (
|
|
265
|
+
<FocusableItem className={"LuiDeprecatedForms"} key={`${item.label}_subcomponent`}>
|
|
266
|
+
{(ref: MenuInstance) => (
|
|
267
|
+
<GridSubComponentContext.Provider
|
|
268
|
+
value={{
|
|
269
|
+
value: subSelectedValue,
|
|
270
|
+
setValue: (value: any) => {
|
|
271
|
+
setSubSelectedValue(value);
|
|
272
|
+
},
|
|
273
|
+
setValid: (valid: boolean) => {
|
|
274
|
+
subComponentIsValid.current = valid;
|
|
275
|
+
},
|
|
276
|
+
triggerSave: async () => {
|
|
277
|
+
ref.closeMenu();
|
|
278
|
+
},
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
{item.subComponent && (
|
|
282
|
+
<item.subComponent key={`${fieldToString(field)}-${index}_subcomponent_inner`} />
|
|
283
|
+
)}
|
|
284
|
+
</GridSubComponentContext.Provider>
|
|
285
|
+
)}
|
|
279
286
|
</FocusableItem>
|
|
280
287
|
)}
|
|
281
288
|
</div>
|
|
@@ -43,7 +43,10 @@ export const GridFormEditBearing = <RowType extends GridBaseRow>(props: GridForm
|
|
|
43
43
|
},
|
|
44
44
|
[field, initialValue, props, value],
|
|
45
45
|
);
|
|
46
|
-
const {
|
|
46
|
+
const { popoverWrapper, onlyInputKeyboardEventHandlers } = useGridPopoverHook({
|
|
47
|
+
className: props.className,
|
|
48
|
+
save,
|
|
49
|
+
});
|
|
47
50
|
|
|
48
51
|
return popoverWrapper(
|
|
49
52
|
<div className={"GridFormEditBearing-input Grid-popoverContainer"}>
|
|
@@ -52,13 +55,9 @@ export const GridFormEditBearing = <RowType extends GridBaseRow>(props: GridForm
|
|
|
52
55
|
onChange={(e) => {
|
|
53
56
|
setValue(e.target.value.trim());
|
|
54
57
|
}}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
disabled: false,
|
|
59
|
-
maxLength: 16,
|
|
60
|
-
onKeyDown: async (e) => e.key === "Enter" && triggerSave().then(),
|
|
61
|
-
}}
|
|
58
|
+
autoFocus={true}
|
|
59
|
+
placeholder={props.placeHolder}
|
|
60
|
+
{...onlyInputKeyboardEventHandlers}
|
|
62
61
|
formatted={bearingStringValidator(value, props.range) ? "?" : convertDDToDMS(bearingNumberParser(value))}
|
|
63
62
|
error={bearingStringValidator(value, props.range)}
|
|
64
63
|
/>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../../styles/GridFormMultiSelect.scss";
|
|
2
2
|
|
|
3
3
|
import { FocusableItem, MenuDivider, MenuItem } from "../../react-menu3";
|
|
4
|
-
import {
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { GridBaseRow } from "../Grid";
|
|
6
6
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
7
7
|
import { delay, fromPairs, isEqual, omit, pick, toPairs } from "lodash-es";
|
|
@@ -149,7 +149,10 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
|
|
|
149
149
|
[selectedValues],
|
|
150
150
|
);
|
|
151
151
|
|
|
152
|
-
const { popoverWrapper, triggerSave } = useGridPopoverHook({
|
|
152
|
+
const { popoverWrapper, lastInputKeyboardEventHandlers, triggerSave } = useGridPopoverHook({
|
|
153
|
+
className: props.className,
|
|
154
|
+
save,
|
|
155
|
+
});
|
|
153
156
|
return popoverWrapper(
|
|
154
157
|
<ComponentLoadingWrapper loading={!options} className={"GridFormMultiSelect-container"}>
|
|
155
158
|
<>
|
|
@@ -186,14 +189,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
|
|
|
186
189
|
e.keepOpen = true;
|
|
187
190
|
toggleValue(item);
|
|
188
191
|
}}
|
|
189
|
-
|
|
190
|
-
if (e.key === "Enter") triggerSave().then();
|
|
191
|
-
else if (e.key === " ") {
|
|
192
|
-
toggleValue(item);
|
|
193
|
-
e.preventDefault();
|
|
194
|
-
e.stopPropagation();
|
|
195
|
-
}
|
|
196
|
-
}}
|
|
192
|
+
{...lastInputKeyboardEventHandlers}
|
|
197
193
|
>
|
|
198
194
|
<LuiCheckboxInput
|
|
199
195
|
isChecked={`${item.value}` in selectedValues}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GridBaseRow } from "../Grid";
|
|
2
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Fragment, useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
4
4
|
import { FocusableItem, MenuDivider, MenuItem } from "../../react-menu3";
|
|
5
5
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
@@ -99,8 +99,8 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
|
|
|
99
99
|
setSubComponentSelected(subComponentSelected === item ? null : item);
|
|
100
100
|
e.keepOpen = true;
|
|
101
101
|
} else {
|
|
102
|
-
setSubComponentSelected(null);
|
|
103
102
|
actionClick(item).then();
|
|
103
|
+
e.keepOpen = true;
|
|
104
104
|
}
|
|
105
105
|
},
|
|
106
106
|
[actionClick, subComponentSelected],
|
|
@@ -117,17 +117,14 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
|
|
|
117
117
|
if (!actionProcessing.current && subComponentSelected) {
|
|
118
118
|
if (!subComponentIsValid.current) return false;
|
|
119
119
|
await actionClick(subComponentSelected);
|
|
120
|
+
return false;
|
|
120
121
|
}
|
|
122
|
+
// Otherwise assume it's a cancel, either way we close the menu
|
|
121
123
|
return true;
|
|
122
124
|
}, [actionClick, subComponentSelected]);
|
|
123
125
|
|
|
124
126
|
const { popoverWrapper, triggerSave } = useGridPopoverHook({ className: props.className, save });
|
|
125
127
|
|
|
126
|
-
const localTriggerSave = async (reason?: string) => {
|
|
127
|
-
if (!subComponentIsValid.current) return;
|
|
128
|
-
return triggerSave(reason);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
128
|
return popoverWrapper(
|
|
132
129
|
<ComponentLoadingWrapper loading={!filteredOptions} className={"GridFormPopupMenu"}>
|
|
133
130
|
<>
|
|
@@ -136,7 +133,7 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
|
|
|
136
133
|
<MenuDivider key={`$$divider_${index}`} />
|
|
137
134
|
) : (
|
|
138
135
|
!item.hidden && (
|
|
139
|
-
|
|
136
|
+
<Fragment key={`${item.label}`}>
|
|
140
137
|
<MenuItem
|
|
141
138
|
key={`${item.label}`}
|
|
142
139
|
onClick={(e: ClickEvent) => onMenuItemClick(e, item)}
|
|
@@ -158,7 +155,7 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
|
|
|
158
155
|
setValid: (valid: boolean) => {
|
|
159
156
|
subComponentIsValid.current = valid;
|
|
160
157
|
},
|
|
161
|
-
triggerSave
|
|
158
|
+
triggerSave,
|
|
162
159
|
}}
|
|
163
160
|
>
|
|
164
161
|
<item.subComponent />
|
|
@@ -167,7 +164,7 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridForm
|
|
|
167
164
|
}
|
|
168
165
|
</FocusableItem>
|
|
169
166
|
)}
|
|
170
|
-
|
|
167
|
+
</Fragment>
|
|
171
168
|
)
|
|
172
169
|
),
|
|
173
170
|
)}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useCallback, useContext, useEffect } from "react";
|
|
2
|
+
import { GridSubComponentContext } from "../../contexts/GridSubComponentContext";
|
|
3
|
+
import { CellEditorCommon } from "../GridCell";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
import { TextAreaInput } from "../../lui/TextAreaInput";
|
|
6
|
+
import { TextInputValidator, TextInputValidatorProps } from "../../utils/textValidator";
|
|
7
|
+
|
|
8
|
+
export interface GridSubComponentTextAreaProps extends TextInputValidatorProps, CellEditorCommon {
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
width?: string | number;
|
|
11
|
+
defaultValue: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
helpText?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const GridFormSubComponentTextArea = (props: GridSubComponentTextAreaProps): JSX.Element => {
|
|
17
|
+
const { value, setValue, setValid, triggerSave } = useContext(GridSubComponentContext);
|
|
18
|
+
|
|
19
|
+
const helpText = props.helpText ?? "Press tab to save";
|
|
20
|
+
|
|
21
|
+
// If is not initialised yet as it's just been created then set the default value
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (value == null) setValue(props.defaultValue);
|
|
24
|
+
}, [props.defaultValue, setValue, value]);
|
|
25
|
+
|
|
26
|
+
const invalid = useCallback(() => TextInputValidator(props, value), [props, value]);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
setValid(value != null && invalid() == null);
|
|
30
|
+
}, [setValid, invalid, value]);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className={clsx("FreeTextInput LuiDeprecatedForms", props.className)}>
|
|
34
|
+
<TextAreaInput
|
|
35
|
+
className={"free-text-input"}
|
|
36
|
+
value={value}
|
|
37
|
+
onChange={(e) => setValue(e.target.value)}
|
|
38
|
+
error={invalid()}
|
|
39
|
+
helpText={helpText}
|
|
40
|
+
autoFocus={true}
|
|
41
|
+
placeholder={props.placeholder}
|
|
42
|
+
onKeyDown={(e) => {
|
|
43
|
+
if (e.key === "Tab") {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
e.stopPropagation();
|
|
46
|
+
}
|
|
47
|
+
}}
|
|
48
|
+
onKeyUp={(e) => {
|
|
49
|
+
if (e.key === "Tab") {
|
|
50
|
+
!e.shiftKey && triggerSave().then();
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
e.stopPropagation();
|
|
53
|
+
}
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
@@ -1,67 +1,60 @@
|
|
|
1
|
-
import "
|
|
2
|
-
|
|
3
|
-
import { useState, KeyboardEvent } from "react";
|
|
1
|
+
import { useCallback, useContext, useEffect } from "react";
|
|
2
|
+
import { GridSubComponentContext } from "../../contexts/GridSubComponentContext";
|
|
4
3
|
import { TextInputFormatted } from "../../lui/TextInputFormatted";
|
|
4
|
+
import { TextInputValidator, TextInputValidatorProps } from "../../utils/textValidator";
|
|
5
|
+
import { CellEditorCommon } from "../GridCell";
|
|
5
6
|
|
|
6
|
-
export interface
|
|
7
|
-
setValue: (value: string) => void;
|
|
8
|
-
keyDown: (key: string, event: KeyboardEvent<HTMLInputElement>) => void;
|
|
7
|
+
export interface GridFormSubComponentTextInputProps extends TextInputValidatorProps, CellEditorCommon {
|
|
9
8
|
placeholder?: string;
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
width?: string | number;
|
|
10
|
+
defaultValue: string;
|
|
11
|
+
helpText?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export const GridFormSubComponentTextInput = ({
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
export const GridFormSubComponentTextInput = (props: GridFormSubComponentTextInputProps): JSX.Element => {
|
|
15
|
+
const { value, setValue, setValid, triggerSave } = useContext(GridSubComponentContext);
|
|
16
|
+
|
|
17
|
+
const helpText = props.helpText ?? "Press enter or tab to save";
|
|
18
|
+
|
|
19
|
+
// If is not initialised yet as it's just been created then set the default value
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (value == null) setValue(props.defaultValue);
|
|
22
|
+
}, [props.defaultValue, setValue, value]);
|
|
23
|
+
|
|
24
|
+
const invalid = useCallback(() => TextInputValidator(props, value), [props, value]);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
setValid(value != null && invalid() == null);
|
|
28
|
+
}, [setValid, invalid, value]);
|
|
23
29
|
|
|
24
|
-
const inputClass = className || "GridFormSubComponentTextInput-full-width-input";
|
|
25
|
-
const [inputValue, setInputValue] = useState("");
|
|
26
30
|
return (
|
|
27
|
-
<
|
|
31
|
+
<TextInputFormatted
|
|
32
|
+
value={value}
|
|
33
|
+
error={invalid()}
|
|
34
|
+
onChange={(e) => setValue(e.target.value)}
|
|
35
|
+
helpText={helpText}
|
|
36
|
+
autoFocus={true}
|
|
37
|
+
onKeyDown={(e) => {
|
|
38
|
+
if (e.key === "Tab" || e.key === "Enter") {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
e.stopPropagation();
|
|
41
|
+
}
|
|
42
|
+
}}
|
|
43
|
+
onKeyUp={(e) => {
|
|
44
|
+
if (e.key === "Tab") {
|
|
45
|
+
!e.shiftKey && triggerSave().then();
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
e.stopPropagation();
|
|
48
|
+
} else if (e.key === "Enter") {
|
|
49
|
+
triggerSave().then();
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
e.stopPropagation();
|
|
52
|
+
}
|
|
53
|
+
}}
|
|
54
|
+
placeholder={props.placeholder}
|
|
28
55
|
style={{
|
|
29
|
-
display: "flex",
|
|
30
|
-
flexDirection: "column",
|
|
31
56
|
width: "100%",
|
|
32
|
-
padding: 0,
|
|
33
57
|
}}
|
|
34
|
-
|
|
35
|
-
<TextInputFormatted
|
|
36
|
-
className={inputClass}
|
|
37
|
-
value={inputValue}
|
|
38
|
-
onChange={(e) => {
|
|
39
|
-
const value = e.target.value;
|
|
40
|
-
setValue(value);
|
|
41
|
-
setInputValue(value);
|
|
42
|
-
}}
|
|
43
|
-
inputProps={{
|
|
44
|
-
onKeyDown: (e) => {
|
|
45
|
-
return keyDown(e.key, e);
|
|
46
|
-
},
|
|
47
|
-
placeholder: placeholderText,
|
|
48
|
-
onMouseEnter: (e) => {
|
|
49
|
-
if (document.activeElement != e.currentTarget) {
|
|
50
|
-
e.currentTarget.focus();
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
style: {
|
|
54
|
-
width: "100%",
|
|
55
|
-
},
|
|
56
|
-
}}
|
|
57
|
-
/>
|
|
58
|
-
<span
|
|
59
|
-
style={{
|
|
60
|
-
fontSize: "0.7rem",
|
|
61
|
-
}}
|
|
62
|
-
>
|
|
63
|
-
{helpText}
|
|
64
|
-
</span>
|
|
65
|
-
</div>
|
|
58
|
+
/>
|
|
66
59
|
);
|
|
67
60
|
};
|
|
@@ -4,32 +4,22 @@ import { useGridPopoverHook } from "../GridPopoverHook";
|
|
|
4
4
|
import { GridBaseRow } from "../Grid";
|
|
5
5
|
import { CellEditorCommon } from "../GridCell";
|
|
6
6
|
import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
7
|
+
import { TextInputValidator, TextInputValidatorProps } from "../../utils/textValidator";
|
|
7
8
|
|
|
8
|
-
export interface GridFormTextAreaProps<RowType extends GridBaseRow> extends CellEditorCommon {
|
|
9
|
+
export interface GridFormTextAreaProps<RowType extends GridBaseRow> extends TextInputValidatorProps, CellEditorCommon {
|
|
9
10
|
placeholder?: string;
|
|
10
|
-
required?: boolean;
|
|
11
|
-
maxLength?: number;
|
|
12
11
|
width?: string | number;
|
|
13
|
-
validate?: (value: string) => string | null;
|
|
14
12
|
onSave?: (selectedRows: RowType[], value: string) => Promise<boolean>;
|
|
13
|
+
helpText?: string;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
export const GridFormTextArea = <RowType extends GridBaseRow>(props: GridFormTextAreaProps<RowType>) => {
|
|
18
17
|
const { field, value: initialVale } = useGridPopoverContext<RowType>();
|
|
19
18
|
const [value, setValue] = useState(initialVale != null ? `${initialVale}` : "");
|
|
20
19
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
if (props.maxLength && value.length > props.maxLength) {
|
|
26
|
-
return `Text must be no longer than ${props.maxLength} characters`;
|
|
27
|
-
}
|
|
28
|
-
if (props.validate) {
|
|
29
|
-
return props.validate(value);
|
|
30
|
-
}
|
|
31
|
-
return null;
|
|
32
|
-
}, [props, value]);
|
|
20
|
+
const helpText = props.helpText ?? "Press tab to save";
|
|
21
|
+
|
|
22
|
+
const invalid = useCallback(() => TextInputValidator(props, value), [props, value]);
|
|
33
23
|
|
|
34
24
|
const save = useCallback(
|
|
35
25
|
async (selectedRows: RowType[]): Promise<boolean> => {
|
|
@@ -52,14 +42,16 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(props: GridFormTex
|
|
|
52
42
|
},
|
|
53
43
|
[invalid, initialVale, value, props, field],
|
|
54
44
|
);
|
|
55
|
-
const { popoverWrapper } = useGridPopoverHook({ className: props.className, save });
|
|
45
|
+
const { popoverWrapper, lastInputKeyboardEventHandlers } = useGridPopoverHook({ className: props.className, save });
|
|
56
46
|
return popoverWrapper(
|
|
57
47
|
<div style={{ display: "flex", flexDirection: "row", width: props.width ?? 240 }}>
|
|
58
48
|
<TextAreaInput
|
|
59
49
|
value={value}
|
|
60
50
|
onChange={(e) => setValue(e.target.value)}
|
|
61
51
|
error={invalid()}
|
|
62
|
-
|
|
52
|
+
placeholder={props.placeholder}
|
|
53
|
+
helpText={helpText}
|
|
54
|
+
{...lastInputKeyboardEventHandlers}
|
|
63
55
|
/>
|
|
64
56
|
</div>,
|
|
65
57
|
);
|