@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.
- package/dist/index.js +278 -239
- 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 +279 -240
- 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 +121 -13
- 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
|
@@ -2,7 +2,12 @@ import { createContext } from "react";
|
|
|
2
2
|
|
|
3
3
|
export type GridUpdatingContextType = {
|
|
4
4
|
checkUpdating: (fields: string | string[], id: number | string) => boolean;
|
|
5
|
-
modifyUpdating: (
|
|
5
|
+
modifyUpdating: (
|
|
6
|
+
fields: string | string[],
|
|
7
|
+
ids: (number | string)[],
|
|
8
|
+
fn: () => void | Promise<void>,
|
|
9
|
+
) => Promise<void>;
|
|
10
|
+
updatedDep: number;
|
|
6
11
|
};
|
|
7
12
|
|
|
8
13
|
export const GridUpdatingContext = createContext<GridUpdatingContextType>({
|
|
@@ -13,4 +18,5 @@ export const GridUpdatingContext = createContext<GridUpdatingContextType>({
|
|
|
13
18
|
modifyUpdating: async () => {
|
|
14
19
|
console.error("Missing GridUpdatingContext");
|
|
15
20
|
},
|
|
21
|
+
updatedDep: 0,
|
|
16
22
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode, useRef } from "react";
|
|
1
|
+
import { ReactNode, useRef, useState } from "react";
|
|
2
2
|
import { castArray, flatten, remove } from "lodash-es";
|
|
3
3
|
import { GridUpdatingContext } from "./GridUpdatingContext";
|
|
4
4
|
|
|
@@ -15,6 +15,7 @@ type UpdatingBlock = Record<FieldName, IdList[]>;
|
|
|
15
15
|
export const GridUpdatingContextProvider = (props: UpdatingContextProviderProps) => {
|
|
16
16
|
const updatingBlocks = useRef<UpdatingBlock>({});
|
|
17
17
|
const updating = useRef<GridUpdatingContextStatus>({});
|
|
18
|
+
const [updatedDep, setUpdatedDep] = useState(0);
|
|
18
19
|
|
|
19
20
|
const resetUpdating = () => {
|
|
20
21
|
const mergedUpdatingBlocks: GridUpdatingContextStatus = {};
|
|
@@ -22,15 +23,25 @@ export const GridUpdatingContextProvider = (props: UpdatingContextProviderProps)
|
|
|
22
23
|
mergedUpdatingBlocks[key] = flatten(updatingBlocks.current[key]);
|
|
23
24
|
}
|
|
24
25
|
updating.current = mergedUpdatingBlocks;
|
|
26
|
+
setUpdatedDep((updatedDep) => updatedDep + 1);
|
|
25
27
|
};
|
|
26
28
|
|
|
27
|
-
const modifyUpdating = async (
|
|
28
|
-
|
|
29
|
+
const modifyUpdating = async (
|
|
30
|
+
fields: string | string[],
|
|
31
|
+
ids: (number | string)[],
|
|
32
|
+
fn: () => void | Promise<void>,
|
|
33
|
+
) => {
|
|
29
34
|
const idRef = [...ids];
|
|
30
|
-
|
|
35
|
+
castArray(fields).forEach((field) => {
|
|
36
|
+
const fieldUpdatingIds = updatingBlocks.current[field] ?? (updatingBlocks.current[field] = []);
|
|
37
|
+
fieldUpdatingIds.push(idRef);
|
|
38
|
+
});
|
|
31
39
|
resetUpdating();
|
|
32
40
|
await fn();
|
|
33
|
-
|
|
41
|
+
castArray(fields).forEach((field) => {
|
|
42
|
+
const fieldUpdatingIds = updatingBlocks.current[field];
|
|
43
|
+
remove(fieldUpdatingIds, (idList) => idList === idRef);
|
|
44
|
+
});
|
|
34
45
|
resetUpdating();
|
|
35
46
|
};
|
|
36
47
|
|
|
@@ -38,7 +49,7 @@ export const GridUpdatingContextProvider = (props: UpdatingContextProviderProps)
|
|
|
38
49
|
castArray(fields).some((f) => updating.current[f]?.includes(id));
|
|
39
50
|
|
|
40
51
|
return (
|
|
41
|
-
<GridUpdatingContext.Provider value={{ modifyUpdating, checkUpdating }}>
|
|
52
|
+
<GridUpdatingContext.Provider value={{ modifyUpdating, checkUpdating, updatedDep }}>
|
|
42
53
|
{props.children}
|
|
43
54
|
</GridUpdatingContext.Provider>
|
|
44
55
|
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { LuiIcon } from "@linzjs/lui";
|
|
2
|
+
|
|
3
|
+
export interface FormErrorProps {
|
|
4
|
+
helpText?: string;
|
|
5
|
+
error?: string | boolean | null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const FormError = (props: FormErrorProps) => {
|
|
9
|
+
return (
|
|
10
|
+
<>
|
|
11
|
+
{props.error && (
|
|
12
|
+
<span className="LuiTextInput-error">
|
|
13
|
+
<LuiIcon alt="error" name="ic_error" className="LuiTextInput-error-icon" size="sm" status="error" />
|
|
14
|
+
{props.error}
|
|
15
|
+
</span>
|
|
16
|
+
)}
|
|
17
|
+
|
|
18
|
+
{props.helpText && !props.error && (
|
|
19
|
+
<span
|
|
20
|
+
style={{
|
|
21
|
+
fontSize: "0.7rem",
|
|
22
|
+
}}
|
|
23
|
+
>
|
|
24
|
+
{props.helpText}
|
|
25
|
+
</span>
|
|
26
|
+
)}
|
|
27
|
+
</>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { InputHTMLAttributes, useState } from "react";
|
|
2
2
|
import clsx from "clsx";
|
|
3
|
-
import { LuiIcon } from "@linzjs/lui";
|
|
4
3
|
import { v4 as uuidVersion4 } from "uuid";
|
|
5
4
|
import { omit } from "lodash-es";
|
|
5
|
+
import { FormError } from "./FormError";
|
|
6
6
|
|
|
7
7
|
export const useGenerateOrDefaultId = (idFromProps?: string) => {
|
|
8
8
|
const [id] = useState(idFromProps ? idFromProps : uuidVersion4());
|
|
@@ -56,23 +56,7 @@ export const TextAreaInput = (props: LuiTextAreaInputProps) => {
|
|
|
56
56
|
</div>
|
|
57
57
|
</label>
|
|
58
58
|
|
|
59
|
-
{
|
|
60
|
-
{props.error && (
|
|
61
|
-
<span className="LuiTextAreaInput-error">
|
|
62
|
-
<LuiIcon alt="error" name="ic_error" className="LuiTextAreaInput-error-icon" size="sm" status="error" />
|
|
63
|
-
{props.error}
|
|
64
|
-
</span>
|
|
65
|
-
)}
|
|
66
|
-
|
|
67
|
-
{props.helpText && !props.error && (
|
|
68
|
-
<span
|
|
69
|
-
style={{
|
|
70
|
-
fontSize: "0.7rem",
|
|
71
|
-
}}
|
|
72
|
-
>
|
|
73
|
-
{props.helpText}
|
|
74
|
-
</span>
|
|
75
|
-
)}
|
|
59
|
+
<FormError error={props.error} helpText={props.helpText} />
|
|
76
60
|
</div>
|
|
77
61
|
);
|
|
78
62
|
};
|
|
@@ -3,8 +3,8 @@ import "./TextInputFormatted.scss";
|
|
|
3
3
|
import { DetailedHTMLProps, InputHTMLAttributes } from "react";
|
|
4
4
|
|
|
5
5
|
import clsx from "clsx";
|
|
6
|
-
import { LuiIcon } from "@linzjs/lui";
|
|
7
6
|
import { omit } from "lodash-es";
|
|
7
|
+
import { FormError } from "./FormError";
|
|
8
8
|
|
|
9
9
|
export interface LuiTextInputProps extends DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
|
|
10
10
|
// overrides value in base class to be string type only
|
|
@@ -35,22 +35,7 @@ export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
|
|
|
35
35
|
<span className={"LuiTextInput-formatted"}>{props.formatted}</span>
|
|
36
36
|
</span>
|
|
37
37
|
|
|
38
|
-
{props.error
|
|
39
|
-
<span className="LuiTextInput-error">
|
|
40
|
-
<LuiIcon alt="error" name="ic_error" className="LuiTextInput-error-icon" size="sm" status="error" />
|
|
41
|
-
{props.error}
|
|
42
|
-
</span>
|
|
43
|
-
)}
|
|
44
|
-
|
|
45
|
-
{props.helpText && !props.error && (
|
|
46
|
-
<span
|
|
47
|
-
style={{
|
|
48
|
-
fontSize: "0.7rem",
|
|
49
|
-
}}
|
|
50
|
-
>
|
|
51
|
-
{props.helpText}
|
|
52
|
-
</span>
|
|
53
|
-
)}
|
|
38
|
+
<FormError error={props.error} helpText={props.helpText} />
|
|
54
39
|
</div>
|
|
55
40
|
);
|
|
56
41
|
};
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
forwardRef,
|
|
3
|
-
useRef,
|
|
4
|
-
useMemo,
|
|
5
|
-
useCallback,
|
|
6
|
-
useEffect,
|
|
7
|
-
KeyboardEvent,
|
|
8
|
-
FocusEvent,
|
|
9
|
-
MutableRefObject,
|
|
10
|
-
ForwardedRef,
|
|
11
|
-
} from "react";
|
|
1
|
+
import { forwardRef, useRef, useMemo, useCallback, useEffect, FocusEvent, MutableRefObject, ForwardedRef } from "react";
|
|
12
2
|
import { createPortal } from "react-dom";
|
|
13
3
|
import { MenuList } from "./MenuList";
|
|
14
4
|
import { useBEM } from "../hooks";
|
|
@@ -120,14 +110,119 @@ export const ControlledMenuFr = (
|
|
|
120
110
|
[isWithinMenu],
|
|
121
111
|
);
|
|
122
112
|
|
|
113
|
+
const lastTabDownEl = useRef<Element>();
|
|
114
|
+
const lastEnterDownEl = useRef<Element>();
|
|
115
|
+
const handleKeyboardTabAndEnter = useCallback(
|
|
116
|
+
(isDown: boolean) => (ev: KeyboardEvent) => {
|
|
117
|
+
const thisDocument = anchorRef?.current ? anchorRef?.current.ownerDocument : document;
|
|
118
|
+
const activeElement = thisDocument.activeElement;
|
|
119
|
+
if (!anchorRef?.current || !activeElement) return;
|
|
120
|
+
if (ev.key !== "Tab" && ev.key !== "Enter") return;
|
|
121
|
+
|
|
122
|
+
if (ev.repeat) {
|
|
123
|
+
ev.preventDefault();
|
|
124
|
+
ev.stopPropagation();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const inputElsIterator = thisDocument.querySelectorAll<HTMLElement>(".szh-menu--state-open input,textarea");
|
|
129
|
+
let inputEls: HTMLElement[] = [];
|
|
130
|
+
inputElsIterator.forEach((el) => inputEls.push(el));
|
|
131
|
+
inputEls = inputEls.filter((el) => !(el as any).disabled);
|
|
132
|
+
|
|
133
|
+
if (inputEls.length === 0) return;
|
|
134
|
+
const firstInputEl = inputEls[0];
|
|
135
|
+
const lastInputEl = inputEls[inputEls.length - 1];
|
|
136
|
+
if (activeElement !== firstInputEl && activeElement !== lastInputEl) return;
|
|
137
|
+
|
|
138
|
+
const suppressAutoSaveEvents = activeElement.getAttribute("data-suppressAutoSaveEvents");
|
|
139
|
+
const invokeSave = (reason: string) => {
|
|
140
|
+
if (!saveButtonRef?.current || suppressAutoSaveEvents) return;
|
|
141
|
+
saveButtonRef.current?.setAttribute("data-reason", reason);
|
|
142
|
+
saveButtonRef?.current?.click();
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const isTextArea = activeElement.nodeName === "TEXTAREA";
|
|
146
|
+
switch (activeElement.nodeName) {
|
|
147
|
+
case "TEXTAREA":
|
|
148
|
+
case "INPUT": {
|
|
149
|
+
if (activeElement === lastInputEl && activeElement === firstInputEl) {
|
|
150
|
+
if (ev.key === "Tab") {
|
|
151
|
+
// Can't forward/backwards tab out of popup
|
|
152
|
+
ev.preventDefault();
|
|
153
|
+
ev.stopPropagation();
|
|
154
|
+
if (isDown) {
|
|
155
|
+
lastTabDownEl.current = activeElement;
|
|
156
|
+
} else {
|
|
157
|
+
lastTabDownEl.current == activeElement &&
|
|
158
|
+
invokeSave(ev.shiftKey ? CloseReason.TAB_BACKWARD : CloseReason.TAB_FORWARD);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (ev.key === "Enter" && !isTextArea) {
|
|
162
|
+
ev.preventDefault();
|
|
163
|
+
ev.stopPropagation();
|
|
164
|
+
if (isDown) {
|
|
165
|
+
lastEnterDownEl.current = activeElement;
|
|
166
|
+
} else {
|
|
167
|
+
lastEnterDownEl.current == activeElement && invokeSave(CloseReason.CLICK);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} else if (activeElement === lastInputEl) {
|
|
171
|
+
if (ev.key === "Tab" && !ev.shiftKey) {
|
|
172
|
+
// Can't backward tab out of popup
|
|
173
|
+
ev.preventDefault();
|
|
174
|
+
ev.stopPropagation();
|
|
175
|
+
|
|
176
|
+
if (isDown) {
|
|
177
|
+
lastTabDownEl.current = activeElement;
|
|
178
|
+
} else {
|
|
179
|
+
lastTabDownEl.current == activeElement && invokeSave(CloseReason.TAB_FORWARD);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (ev.key === "Enter" && !isTextArea) {
|
|
183
|
+
ev.preventDefault();
|
|
184
|
+
ev.stopPropagation();
|
|
185
|
+
if (isDown) {
|
|
186
|
+
lastEnterDownEl.current = activeElement;
|
|
187
|
+
} else {
|
|
188
|
+
lastEnterDownEl.current == activeElement && invokeSave(CloseReason.CLICK);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else if (activeElement === firstInputEl) {
|
|
192
|
+
if (ev.key === "Tab" && ev.shiftKey) {
|
|
193
|
+
// Can't backward tab out of popup
|
|
194
|
+
ev.preventDefault();
|
|
195
|
+
ev.stopPropagation();
|
|
196
|
+
|
|
197
|
+
if (isDown) {
|
|
198
|
+
lastTabDownEl.current = activeElement;
|
|
199
|
+
} else {
|
|
200
|
+
lastTabDownEl.current == activeElement && invokeSave(CloseReason.TAB_BACKWARD);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
[anchorRef, saveButtonRef],
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const handleKeydownTabAndEnter = useMemo(() => handleKeyboardTabAndEnter(true), [handleKeyboardTabAndEnter]);
|
|
212
|
+
const handleKeyupTabAndEnter = useMemo(() => handleKeyboardTabAndEnter(false), [handleKeyboardTabAndEnter]);
|
|
213
|
+
|
|
123
214
|
useEffect(() => {
|
|
124
215
|
if (isMenuOpen(state)) {
|
|
125
216
|
const thisDocument = anchorRef?.current ? anchorRef?.current.ownerDocument : document;
|
|
217
|
+
thisDocument.addEventListener("keydown", handleKeydownTabAndEnter, true);
|
|
218
|
+
thisDocument.addEventListener("keyup", handleKeyupTabAndEnter, true);
|
|
126
219
|
thisDocument.addEventListener("mousedown", handleScreenEventForSave, true);
|
|
127
220
|
thisDocument.addEventListener("mouseup", handleScreenEventForCancel, true);
|
|
128
221
|
thisDocument.addEventListener("click", handleScreenEventForCancel, true);
|
|
129
222
|
thisDocument.addEventListener("dblclick", handleScreenEventForCancel, true);
|
|
130
223
|
return () => {
|
|
224
|
+
thisDocument.addEventListener("keydown", handleKeydownTabAndEnter, true);
|
|
225
|
+
thisDocument.addEventListener("keyup", handleKeyupTabAndEnter, true);
|
|
131
226
|
thisDocument.removeEventListener("mousedown", handleScreenEventForSave, true);
|
|
132
227
|
thisDocument.removeEventListener("mouseup", handleScreenEventForCancel, true);
|
|
133
228
|
thisDocument.removeEventListener("click", handleScreenEventForCancel, true);
|
|
@@ -135,7 +230,14 @@ export const ControlledMenuFr = (
|
|
|
135
230
|
};
|
|
136
231
|
}
|
|
137
232
|
return () => {};
|
|
138
|
-
}, [
|
|
233
|
+
}, [
|
|
234
|
+
handleScreenEventForSave,
|
|
235
|
+
handleScreenEventForCancel,
|
|
236
|
+
state,
|
|
237
|
+
anchorRef,
|
|
238
|
+
handleKeydownTabAndEnter,
|
|
239
|
+
handleKeyupTabAndEnter,
|
|
240
|
+
]);
|
|
139
241
|
|
|
140
242
|
const itemSettings = useMemo(
|
|
141
243
|
() => ({
|
|
@@ -162,7 +264,13 @@ export const ControlledMenuFr = (
|
|
|
162
264
|
safeCall(onClose, {
|
|
163
265
|
value: event.value,
|
|
164
266
|
key: event.key,
|
|
165
|
-
|
|
267
|
+
shiftKey: event.shiftKey,
|
|
268
|
+
reason:
|
|
269
|
+
event.key === "Tab"
|
|
270
|
+
? event.shiftKey
|
|
271
|
+
? CloseReason.TAB_BACKWARD
|
|
272
|
+
: CloseReason.TAB_FORWARD
|
|
273
|
+
: CloseReason.CLICK,
|
|
166
274
|
});
|
|
167
275
|
}
|
|
168
276
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ref, useContext, useMemo } from "react";
|
|
1
|
+
import { KeyboardEvent, Ref, useContext, useMemo } from "react";
|
|
2
2
|
import { useBEM, useItemState, useCombinedRef } from "../hooks";
|
|
3
3
|
import { mergeProps, commonProps, safeCall, menuClass, menuItemClass, withHovering, Keys, RMEvent } from "../utils";
|
|
4
4
|
import { BaseProps, ClickEvent, EventHandler, Hoverable, MenuItemTypeProp, RenderProp } from "../types";
|
|
@@ -106,7 +106,12 @@ const MenuItemFr = ({
|
|
|
106
106
|
value,
|
|
107
107
|
syntheticEvent: e,
|
|
108
108
|
};
|
|
109
|
-
|
|
109
|
+
|
|
110
|
+
if (e.key !== undefined) {
|
|
111
|
+
const ke = e as KeyboardEvent;
|
|
112
|
+
event.key = ke.key;
|
|
113
|
+
event.shiftKey = ke.shiftKey;
|
|
114
|
+
}
|
|
110
115
|
if (isCheckBox) event.checked = !isChecked;
|
|
111
116
|
if (isRadio) event.name = radioGroup.name;
|
|
112
117
|
safeCall(onClick, event);
|
|
@@ -114,6 +119,14 @@ const MenuItemFr = ({
|
|
|
114
119
|
eventHandlers.handleClick(event, isCheckBox || isRadio);
|
|
115
120
|
};
|
|
116
121
|
|
|
122
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
123
|
+
// if tab is allowed the handleKeyUp event can't process the tab
|
|
124
|
+
if (e.key === "Tab") {
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
e.stopPropagation();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
117
130
|
/**
|
|
118
131
|
* Keyboard events are triggered on up, otherwise sub-components get spaces and enters typed in them
|
|
119
132
|
*/
|
|
@@ -122,7 +135,10 @@ const MenuItemFr = ({
|
|
|
122
135
|
|
|
123
136
|
switch (e.key) {
|
|
124
137
|
case Keys.ENTER:
|
|
138
|
+
case Keys.TAB:
|
|
125
139
|
case Keys.SPACE:
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
e.stopPropagation();
|
|
126
142
|
if (isAnchor) {
|
|
127
143
|
menuItemRef?.current && menuItemRef.current.click();
|
|
128
144
|
} else {
|
|
@@ -147,6 +163,7 @@ const MenuItemFr = ({
|
|
|
147
163
|
{
|
|
148
164
|
...restStateProps,
|
|
149
165
|
onPointerDown: setHover,
|
|
166
|
+
onKeyDown: handleKeyDown,
|
|
150
167
|
onKeyUp: handleKeyUp,
|
|
151
168
|
onClick: handleClick,
|
|
152
169
|
},
|
package/src/react-menu3/types.ts
CHANGED
|
@@ -17,10 +17,12 @@ export interface RMEvent {
|
|
|
17
17
|
checked?: boolean;
|
|
18
18
|
name?: string;
|
|
19
19
|
key?: string;
|
|
20
|
+
shiftKey?: boolean;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export const Keys = Object.freeze({
|
|
23
24
|
ENTER: "Enter",
|
|
25
|
+
TAB: "Tab",
|
|
24
26
|
ESC: "Escape",
|
|
25
27
|
SPACE: " ",
|
|
26
28
|
HOME: "Home",
|
|
@@ -47,6 +49,8 @@ export const CloseReason = Object.freeze({
|
|
|
47
49
|
CANCEL: "cancel",
|
|
48
50
|
BLUR: "blur",
|
|
49
51
|
SCROLL: "scroll",
|
|
52
|
+
TAB_FORWARD: "tab_forward",
|
|
53
|
+
TAB_BACKWARD: "tab_backward",
|
|
50
54
|
});
|
|
51
55
|
|
|
52
56
|
export const FocusPositions = Object.freeze({
|
|
@@ -6,6 +6,7 @@ import { wait } from "../../utils/util";
|
|
|
6
6
|
import { CellEditorCommon } from "../../components/GridCell";
|
|
7
7
|
import { useGridPopoverHook } from "../../components/GridPopoverHook";
|
|
8
8
|
import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
9
|
+
import { FormError } from "../../lui/FormError";
|
|
9
10
|
|
|
10
11
|
export interface IFormTestRow {
|
|
11
12
|
id: number;
|
|
@@ -37,14 +38,18 @@ export const FormTest = (props: CellEditorCommon): JSX.Element => {
|
|
|
37
38
|
[nameType, numba],
|
|
38
39
|
);
|
|
39
40
|
|
|
41
|
+
const invalid = useCallback(() => {
|
|
42
|
+
if (numba.length < 3) return "Number should be at least 3 characters";
|
|
43
|
+
return null;
|
|
44
|
+
}, [numba.length]);
|
|
45
|
+
|
|
40
46
|
const [showModal, setShowModal] = useState<boolean>(false);
|
|
41
47
|
|
|
42
|
-
const { popoverWrapper,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
+
const { popoverWrapper, triggerSave } = useGridPopoverHook({
|
|
49
|
+
className: props.className,
|
|
50
|
+
invalid,
|
|
51
|
+
save,
|
|
52
|
+
});
|
|
48
53
|
|
|
49
54
|
return popoverWrapper(
|
|
50
55
|
<>
|
|
@@ -84,26 +89,21 @@ export const FormTest = (props: CellEditorCommon): JSX.Element => {
|
|
|
84
89
|
</LuiAlertModalButtons>
|
|
85
90
|
</LuiAlertModal>
|
|
86
91
|
)}
|
|
87
|
-
<div
|
|
88
|
-
<div className=
|
|
89
|
-
<
|
|
90
|
-
label={"Name type"}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
onChange={(e) => setNumba(e.target.value)}
|
|
101
|
-
inputProps={lastInputKeyboardEventHandlers}
|
|
102
|
-
/>
|
|
103
|
-
</div>
|
|
104
|
-
<div className={"FormTest-textInput"}>
|
|
105
|
-
<LuiButton onClick={() => setShowModal(true)}>Show Modal</LuiButton>
|
|
92
|
+
<div className={"Grid-popoverContainer"}>
|
|
93
|
+
<div className="FormTest">
|
|
94
|
+
<div className={"FormTest-textInput"}>
|
|
95
|
+
<LuiTextInput label={"Name type"} value={nameType} onChange={(e) => setNameType(e.target.value)} />
|
|
96
|
+
</div>
|
|
97
|
+
<div className={"FormTest-textInput"}>
|
|
98
|
+
<LuiTextInput label={"Number"} value={numba} onChange={(e) => setNumba(e.target.value)} />
|
|
99
|
+
</div>
|
|
100
|
+
<div style={{ marginTop: 25 }}>
|
|
101
|
+
<LuiButton style={{ height: 48 }} onClick={() => setShowModal(true)}>
|
|
102
|
+
Show Modal
|
|
103
|
+
</LuiButton>
|
|
104
|
+
</div>
|
|
106
105
|
</div>
|
|
106
|
+
<FormError error={invalid()} />
|
|
107
107
|
</div>
|
|
108
108
|
</>,
|
|
109
109
|
);
|
|
@@ -13,6 +13,7 @@ import { wait } from "../../utils/util";
|
|
|
13
13
|
import { ColDefT, GridCell } from "../../components/GridCell";
|
|
14
14
|
import { GridPopoverEditDropDown } from "../../components/gridPopoverEdit/GridPopoverEditDropDown";
|
|
15
15
|
import { GridFormSubComponentTextInput } from "../../components/gridForm/GridFormSubComponentTextInput";
|
|
16
|
+
import { GridPopoverMenu } from "../../components/gridPopoverEdit/GridPopoverMenu";
|
|
16
17
|
|
|
17
18
|
export default {
|
|
18
19
|
title: "Components / Grids",
|
|
@@ -280,11 +281,20 @@ const GridEditDropDownTemplate: ComponentStory<typeof Grid> = (props: GridProps)
|
|
|
280
281
|
onSelectedItem: async (selected) => {
|
|
281
282
|
// eslint-disable-next-line no-console
|
|
282
283
|
console.log("onSelectedItem", selected);
|
|
284
|
+
await wait(500);
|
|
283
285
|
selected.selectedRows.forEach((row) => (row.sub = selected.subComponentValue ?? selected.value));
|
|
284
286
|
},
|
|
285
287
|
},
|
|
286
288
|
},
|
|
287
289
|
),
|
|
290
|
+
GridPopoverMenu(
|
|
291
|
+
{},
|
|
292
|
+
{
|
|
293
|
+
editorParams: {
|
|
294
|
+
options: async () => [{ label: "Hello", action: async () => {}, supportsMultiEdit: true }],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
),
|
|
288
298
|
],
|
|
289
299
|
[optionsFn, optionsObjects],
|
|
290
300
|
);
|
|
@@ -81,7 +81,7 @@ const GridEditMultiSelectTemplate: ComponentStory<typeof Grid> = (props: GridPro
|
|
|
81
81
|
{
|
|
82
82
|
value: "other",
|
|
83
83
|
label: "Other",
|
|
84
|
-
subComponent: () => <GridFormSubComponentTextArea maxLength={5} defaultValue={""} />,
|
|
84
|
+
subComponent: () => <GridFormSubComponentTextArea required={true} maxLength={5} defaultValue={""} />,
|
|
85
85
|
},
|
|
86
86
|
],
|
|
87
87
|
initialSelectedValues: () => ({
|
|
@@ -109,7 +109,7 @@ const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) =>
|
|
|
109
109
|
alert(`Single-edit: ${selectedRows.length} rows`);
|
|
110
110
|
await wait(1500);
|
|
111
111
|
},
|
|
112
|
-
|
|
112
|
+
disabled: selectedItems.length > 1,
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
label: "Multi-edit",
|
|
@@ -117,36 +117,36 @@ const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) =>
|
|
|
117
117
|
alert(`Multi-edit: ${selectedRows.length} rows`);
|
|
118
118
|
await wait(1500);
|
|
119
119
|
},
|
|
120
|
-
supportsMultiEdit: true,
|
|
121
120
|
},
|
|
122
121
|
{
|
|
123
122
|
label: "Disabled item",
|
|
124
123
|
disabled: "Disabled for test",
|
|
125
|
-
supportsMultiEdit: true,
|
|
126
124
|
},
|
|
127
125
|
{
|
|
128
126
|
label: "Developer Only",
|
|
129
127
|
hidden: selectedItems.some((x) => x.position != "Developer"),
|
|
130
|
-
supportsMultiEdit: true,
|
|
131
128
|
},
|
|
132
129
|
{
|
|
133
130
|
label: "Other (TextInput)",
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
action: async (_, menuOptionResult) => {
|
|
132
|
+
// eslint-disable-next-line no-console
|
|
133
|
+
console.log(`Sub selected value was ${JSON.stringify(menuOptionResult.subValue)}`);
|
|
134
|
+
await wait(500);
|
|
137
135
|
},
|
|
138
136
|
subComponent: () => (
|
|
139
|
-
<GridFormSubComponentTextInput placeholder={"Other"} maxLength={
|
|
137
|
+
<GridFormSubComponentTextInput placeholder={"Other"} maxLength={5} required defaultValue={""} />
|
|
140
138
|
),
|
|
141
139
|
},
|
|
142
140
|
{
|
|
143
141
|
label: "Other (TextArea)",
|
|
144
142
|
supportsMultiEdit: true,
|
|
145
|
-
action: (_, menuOptionResult) => {
|
|
146
|
-
|
|
143
|
+
action: async (_, menuOptionResult) => {
|
|
144
|
+
// eslint-disable-next-line no-console
|
|
145
|
+
console.log(`Sub selected value was ${JSON.stringify(menuOptionResult.subValue)}`);
|
|
146
|
+
await wait(500);
|
|
147
147
|
},
|
|
148
148
|
subComponent: () => (
|
|
149
|
-
<GridFormSubComponentTextArea placeholder={"Other"} maxLength={
|
|
149
|
+
<GridFormSubComponentTextArea placeholder={"Other"} maxLength={5} required defaultValue={""} />
|
|
150
150
|
),
|
|
151
151
|
},
|
|
152
152
|
] as MenuOption<ITestRow>[];
|