@linzjs/step-ag-grid 2.5.0 → 3.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 +361 -282
- package/dist/index.js.map +1 -1
- package/dist/src/components/GridCell.d.ts +4 -7
- package/dist/src/components/gridForm/GridFormDropDown.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormEditBearing.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormMessage.d.ts +3 -3
- package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +2 -2
- package/dist/src/components/gridForm/GridFormPopoverMenu.d.ts +8 -3
- package/dist/src/components/gridForm/GridFormSubComponentTextInput.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormTextArea.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormTextInput.d.ts +1 -1
- package/dist/src/contexts/GridPopoverContext.d.ts +10 -6
- package/dist/src/contexts/GridPopoverContextProvider.d.ts +3 -1
- package/dist/src/lui/TextInputFormatted.d.ts +0 -1
- package/dist/step-ag-grid.esm.js +362 -283
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/GridCell.tsx +9 -33
- package/src/components/GridPopoverHook.tsx +4 -8
- package/src/components/GridSubComponentTextArea.tsx +0 -6
- package/src/components/gridForm/GridFormDropDown.tsx +35 -37
- package/src/components/gridForm/GridFormEditBearing.tsx +12 -8
- package/src/components/gridForm/GridFormMessage.tsx +8 -6
- package/src/components/gridForm/GridFormMultiSelect.tsx +56 -48
- package/src/components/gridForm/GridFormPopoverMenu.tsx +112 -29
- package/src/components/gridForm/GridFormSubComponentTextInput.tsx +2 -1
- package/src/components/gridForm/GridFormTextArea.tsx +11 -9
- package/src/components/gridForm/GridFormTextInput.tsx +13 -16
- package/src/contexts/GridPopoverContext.tsx +18 -9
- package/src/contexts/GridPopoverContextProvider.tsx +24 -27
- package/src/lui/TextAreaInput.tsx +13 -1
- package/src/lui/TextInputFormatted.tsx +3 -2
- package/src/react-menu3/components/MenuList.tsx +42 -10
- package/src/react-menu3/contexts/SettingsContext.ts +1 -1
- package/src/stories/grid/FormTest.tsx +17 -15
- package/src/stories/grid/GridPopoutEditDropDown.stories.tsx +3 -1
- package/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx +0 -1
- package/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx +3 -3
- package/src/stories/grid/GridReadOnly.stories.tsx +15 -5
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { GridBaseRow } from "../Grid";
|
|
2
|
-
import { useCallback,
|
|
3
|
-
import { GridContext } from "../../contexts/GridContext";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
3
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
5
|
-
import { MenuDivider, MenuItem } from "../../react-menu3";
|
|
4
|
+
import { FocusableItem, MenuDivider, MenuItem } from "../../react-menu3";
|
|
6
5
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
7
|
-
import { CellEditorCommon
|
|
6
|
+
import { CellEditorCommon } from "../GridCell";
|
|
7
|
+
import { GridSubComponentContext } from "../../contexts/GridSubComponentContext";
|
|
8
|
+
import { ClickEvent } from "../../react-menu3/types";
|
|
9
|
+
import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
8
10
|
|
|
9
11
|
export interface GridFormPopoutMenuProps<RowType extends GridBaseRow> extends CellEditorCommon {
|
|
10
12
|
options: (selectedRows: RowType[]) => Promise<MenuOption<RowType>[]>;
|
|
13
|
+
defaultAction?: (selectedRows: RowType[], menuOption: SelectedMenuOptionResult<RowType>) => Promise<void>;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
/** Menu configuration types **/
|
|
@@ -17,24 +20,43 @@ interface MenuSeparatorType {
|
|
|
17
20
|
__isMenuSeparator__: boolean;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
export interface MenuOption<RowType> {
|
|
23
|
+
export interface SelectedMenuOptionResult<RowType extends GridBaseRow> extends MenuOption<RowType> {
|
|
24
|
+
subValue: any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MenuOption<RowType extends GridBaseRow> {
|
|
21
28
|
label: JSX.Element | string | MenuSeparatorType;
|
|
22
|
-
action?: (selectedRows: RowType[]) => Promise<
|
|
29
|
+
action?: (selectedRows: RowType[], menuOption: SelectedMenuOptionResult<RowType>) => Promise<void>;
|
|
23
30
|
disabled?: string | boolean;
|
|
24
31
|
supportsMultiEdit: boolean;
|
|
25
32
|
hidden?: boolean;
|
|
33
|
+
subComponent?: (props: any) => JSX.Element;
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
/**
|
|
29
37
|
* NOTE: If the popout menu doesn't appear on single click when also selecting row it's because
|
|
30
38
|
* you need a useMemo around your columnDefs
|
|
31
39
|
*/
|
|
32
|
-
export const GridFormPopoverMenu = <RowType extends GridBaseRow>(
|
|
33
|
-
const
|
|
34
|
-
|
|
40
|
+
export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridFormPopoutMenuProps<RowType>) => {
|
|
41
|
+
const { selectedRows, updateValue } = useGridPopoverContext<RowType>();
|
|
42
|
+
|
|
35
43
|
const optionsInitialising = useRef(false);
|
|
36
44
|
const [options, setOptions] = useState<MenuOption<RowType>[]>();
|
|
37
45
|
|
|
46
|
+
// Save triggers during async action processing which triggers another action(), this ref blocks that
|
|
47
|
+
const actionProcessing = useRef(false);
|
|
48
|
+
const [subComponentSelected, setSubComponentSelected] = useState<MenuOption<RowType> | null>(null);
|
|
49
|
+
const subComponentIsValid = useRef(false);
|
|
50
|
+
const [subSelectedValue, setSubSelectedValue] = useState<any>();
|
|
51
|
+
|
|
52
|
+
const defaultAction = useCallback(
|
|
53
|
+
async (selectedRows: RowType[], menuOption: SelectedMenuOptionResult<RowType>) => {
|
|
54
|
+
if (props.defaultAction) await props.defaultAction(selectedRows, menuOption);
|
|
55
|
+
else console.error(`No action specified for ${menuOption.label} menu options`);
|
|
56
|
+
},
|
|
57
|
+
[props],
|
|
58
|
+
);
|
|
59
|
+
|
|
38
60
|
// Load up options list if it's async function
|
|
39
61
|
useEffect(() => {
|
|
40
62
|
if (options || optionsInitialising.current) return;
|
|
@@ -42,33 +64,70 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(_props: GridFor
|
|
|
42
64
|
const optionsConf = props.options ?? [];
|
|
43
65
|
|
|
44
66
|
(async () => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
const newOptions = typeof optionsConf == "function" ? await optionsConf(selectedRows) : optionsConf;
|
|
68
|
+
setOptions(newOptions);
|
|
69
|
+
if (!props.defaultAction) {
|
|
70
|
+
const anyOptionsAreMissingAction = newOptions.some((option) => !option.action);
|
|
71
|
+
if (anyOptionsAreMissingAction) {
|
|
72
|
+
console.error("There's no default action handler and some Menu options are missing an action handler", {
|
|
73
|
+
invalidMenuOptions: newOptions.filter((option) => !option.action),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
49
76
|
}
|
|
50
|
-
|
|
51
77
|
optionsInitialising.current = false;
|
|
52
78
|
})();
|
|
53
|
-
}, [options, props.
|
|
79
|
+
}, [options, props.defaultAction, props.options, selectedRows]);
|
|
54
80
|
|
|
55
81
|
const actionClick = useCallback(
|
|
56
|
-
async (menuOption: MenuOption<
|
|
57
|
-
|
|
58
|
-
|
|
82
|
+
async (menuOption: MenuOption<RowType>) => {
|
|
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;
|
|
59
88
|
return true;
|
|
60
89
|
});
|
|
61
90
|
},
|
|
62
|
-
[
|
|
91
|
+
[defaultAction, selectedRows, subSelectedValue, updateValue],
|
|
63
92
|
);
|
|
64
93
|
|
|
65
|
-
const
|
|
94
|
+
const onMenuItemClick = useCallback(
|
|
95
|
+
(e: ClickEvent, item: MenuOption<RowType>) => {
|
|
96
|
+
if (item.subComponent) {
|
|
97
|
+
subComponentIsValid.current = false;
|
|
98
|
+
setSubSelectedValue(null);
|
|
99
|
+
setSubComponentSelected(subComponentSelected === item ? null : item);
|
|
100
|
+
e.keepOpen = true;
|
|
101
|
+
} else {
|
|
102
|
+
setSubComponentSelected(null);
|
|
103
|
+
actionClick(item).then();
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
[actionClick, subComponentSelected],
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const selectedRowCount = selectedRows.length;
|
|
66
110
|
|
|
67
111
|
const filteredOptions = options?.filter((menuOption) => {
|
|
68
112
|
return menuOption.label === PopoutMenuSeparator || selectedRowCount === 1 || menuOption.supportsMultiEdit;
|
|
69
113
|
});
|
|
70
114
|
|
|
71
|
-
const
|
|
115
|
+
const save = useCallback(async () => {
|
|
116
|
+
// if a subcomponent is open we assume that it's meant to be saved.
|
|
117
|
+
if (!actionProcessing.current && subComponentSelected) {
|
|
118
|
+
if (!subComponentIsValid.current) return false;
|
|
119
|
+
await actionClick(subComponentSelected);
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}, [actionClick, subComponentSelected]);
|
|
123
|
+
|
|
124
|
+
const { popoverWrapper, triggerSave } = useGridPopoverHook({ className: props.className, save });
|
|
125
|
+
|
|
126
|
+
const localTriggerSave = async (reason?: string) => {
|
|
127
|
+
if (!subComponentIsValid.current) return;
|
|
128
|
+
return triggerSave(reason);
|
|
129
|
+
};
|
|
130
|
+
|
|
72
131
|
return popoverWrapper(
|
|
73
132
|
<ComponentLoadingWrapper loading={!filteredOptions} className={"GridFormPopupMenu"}>
|
|
74
133
|
<>
|
|
@@ -77,14 +136,38 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(_props: GridFor
|
|
|
77
136
|
<MenuDivider key={`$$divider_${index}`} />
|
|
78
137
|
) : (
|
|
79
138
|
!item.hidden && (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
139
|
+
<>
|
|
140
|
+
<MenuItem
|
|
141
|
+
key={`${item.label}`}
|
|
142
|
+
onClick={(e: ClickEvent) => onMenuItemClick(e, item)}
|
|
143
|
+
disabled={!!item.disabled || !filteredOptions?.includes(item)}
|
|
144
|
+
title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
|
|
145
|
+
>
|
|
146
|
+
{item.label as JSX.Element | string}
|
|
147
|
+
</MenuItem>
|
|
148
|
+
{item.subComponent && subComponentSelected === item && (
|
|
149
|
+
<FocusableItem className={"LuiDeprecatedForms"} key={`${item.label}_subcomponent`}>
|
|
150
|
+
{(_: any) =>
|
|
151
|
+
item.subComponent && (
|
|
152
|
+
<GridSubComponentContext.Provider
|
|
153
|
+
value={{
|
|
154
|
+
value: subSelectedValue,
|
|
155
|
+
setValue: (value: any) => {
|
|
156
|
+
setSubSelectedValue(value);
|
|
157
|
+
},
|
|
158
|
+
setValid: (valid: boolean) => {
|
|
159
|
+
subComponentIsValid.current = valid;
|
|
160
|
+
},
|
|
161
|
+
triggerSave: localTriggerSave,
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
<item.subComponent />
|
|
165
|
+
</GridSubComponentContext.Provider>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
</FocusableItem>
|
|
169
|
+
)}
|
|
170
|
+
</>
|
|
88
171
|
)
|
|
89
172
|
),
|
|
90
173
|
)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import "../../styles/GridFormSubComponentTextInput.scss";
|
|
2
|
+
|
|
1
3
|
import { useState, KeyboardEvent } from "react";
|
|
2
4
|
import { TextInputFormatted } from "../../lui/TextInputFormatted";
|
|
3
|
-
import "../../styles/GridFormSubComponentTextInput.scss";
|
|
4
5
|
|
|
5
6
|
export interface GridFormSubComponentTextInput {
|
|
6
7
|
setValue: (value: string) => void;
|
|
@@ -2,7 +2,8 @@ import { useCallback, useState } from "react";
|
|
|
2
2
|
import { TextAreaInput } from "../../lui/TextAreaInput";
|
|
3
3
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
4
4
|
import { GridBaseRow } from "../Grid";
|
|
5
|
-
import { CellEditorCommon
|
|
5
|
+
import { CellEditorCommon } from "../GridCell";
|
|
6
|
+
import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
6
7
|
|
|
7
8
|
export interface GridFormTextAreaProps<RowType extends GridBaseRow> extends CellEditorCommon {
|
|
8
9
|
placeholder?: string;
|
|
@@ -13,9 +14,9 @@ export interface GridFormTextAreaProps<RowType extends GridBaseRow> extends Cell
|
|
|
13
14
|
onSave?: (selectedRows: RowType[], value: string) => Promise<boolean>;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export const GridFormTextArea = <RowType extends GridBaseRow>(
|
|
17
|
-
const
|
|
18
|
-
const [value, setValue] = useState(
|
|
17
|
+
export const GridFormTextArea = <RowType extends GridBaseRow>(props: GridFormTextAreaProps<RowType>) => {
|
|
18
|
+
const { field, value: initialVale } = useGridPopoverContext<RowType>();
|
|
19
|
+
const [value, setValue] = useState(initialVale != null ? `${initialVale}` : "");
|
|
19
20
|
|
|
20
21
|
const invalid = useCallback(() => {
|
|
21
22
|
if (props.required && value.length == 0) {
|
|
@@ -31,24 +32,25 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(_props: GridFormTe
|
|
|
31
32
|
}, [props, value]);
|
|
32
33
|
|
|
33
34
|
const save = useCallback(
|
|
34
|
-
async (selectedRows:
|
|
35
|
+
async (selectedRows: RowType[]): Promise<boolean> => {
|
|
35
36
|
if (invalid()) return false;
|
|
36
37
|
|
|
37
|
-
if (
|
|
38
|
+
if (initialVale === (value ?? "")) return true;
|
|
38
39
|
|
|
39
40
|
if (props.onSave) {
|
|
40
41
|
return await props.onSave(selectedRows, value);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
const field = props.field;
|
|
44
44
|
if (field == null) {
|
|
45
45
|
console.error("ColDef has no field set");
|
|
46
46
|
return false;
|
|
47
47
|
}
|
|
48
|
-
selectedRows.forEach((row) =>
|
|
48
|
+
selectedRows.forEach((row) => {
|
|
49
|
+
row[field] = value as any;
|
|
50
|
+
});
|
|
49
51
|
return true;
|
|
50
52
|
},
|
|
51
|
-
[
|
|
53
|
+
[invalid, initialVale, value, props, field],
|
|
52
54
|
);
|
|
53
55
|
const { popoverWrapper } = useGridPopoverHook({ className: props.className, save });
|
|
54
56
|
return popoverWrapper(
|
|
@@ -2,7 +2,8 @@ import { useCallback, useState } from "react";
|
|
|
2
2
|
import { TextInputFormatted } from "../../lui/TextInputFormatted";
|
|
3
3
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
4
4
|
import { GridBaseRow } from "../Grid";
|
|
5
|
-
import { CellEditorCommon
|
|
5
|
+
import { CellEditorCommon } from "../GridCell";
|
|
6
|
+
import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
6
7
|
|
|
7
8
|
export interface GridFormTextInputProps<RowType extends GridBaseRow> extends CellEditorCommon {
|
|
8
9
|
placeholder?: string;
|
|
@@ -15,9 +16,10 @@ export interface GridFormTextInputProps<RowType extends GridBaseRow> extends Cel
|
|
|
15
16
|
onSave?: (selectedRows: RowType[], value: string) => Promise<boolean>;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export const GridFormTextInput = <RowType extends GridBaseRow>(
|
|
19
|
-
const
|
|
20
|
-
|
|
19
|
+
export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormTextInputProps<RowType>) => {
|
|
20
|
+
const { field, data, value: initialVale } = useGridPopoverContext<RowType>();
|
|
21
|
+
|
|
22
|
+
const initValue = initialVale == null ? "" : `${initialVale}`;
|
|
21
23
|
const [value, setValue] = useState(initValue);
|
|
22
24
|
|
|
23
25
|
const invalid = useCallback(() => {
|
|
@@ -29,13 +31,13 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(_props: GridFormT
|
|
|
29
31
|
return `Text must be no longer than ${props.maxLength} characters`;
|
|
30
32
|
}
|
|
31
33
|
if (props.validate) {
|
|
32
|
-
return props.validate(trimmedValue,
|
|
34
|
+
return props.validate(trimmedValue, data);
|
|
33
35
|
}
|
|
34
36
|
return null;
|
|
35
|
-
}, [props, value]);
|
|
37
|
+
}, [data, props, value]);
|
|
36
38
|
|
|
37
39
|
const save = useCallback(
|
|
38
|
-
async (selectedRows:
|
|
40
|
+
async (selectedRows: RowType[]): Promise<boolean> => {
|
|
39
41
|
if (invalid()) return false;
|
|
40
42
|
const trimmedValue = value.trim();
|
|
41
43
|
if (initValue === trimmedValue) return true;
|
|
@@ -44,15 +46,16 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(_props: GridFormT
|
|
|
44
46
|
return await props.onSave(selectedRows, trimmedValue);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
const field = props.field;
|
|
48
49
|
if (field == null) {
|
|
49
50
|
console.error("ColDef has no field set");
|
|
50
51
|
return false;
|
|
51
52
|
}
|
|
52
|
-
selectedRows.forEach((row) =>
|
|
53
|
+
selectedRows.forEach((row) => {
|
|
54
|
+
row[field] = trimmedValue as any;
|
|
55
|
+
});
|
|
53
56
|
return true;
|
|
54
57
|
},
|
|
55
|
-
[invalid, value, initValue, props],
|
|
58
|
+
[invalid, value, initValue, props, field],
|
|
56
59
|
);
|
|
57
60
|
const { popoverWrapper, triggerSave } = useGridPopoverHook({ className: props.className, save });
|
|
58
61
|
|
|
@@ -63,12 +66,6 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(_props: GridFormT
|
|
|
63
66
|
onChange={(e) => setValue(e.target.value)}
|
|
64
67
|
error={invalid()}
|
|
65
68
|
formatted={props.units}
|
|
66
|
-
onMouseEnter={(e) => {
|
|
67
|
-
if (document.activeElement != e.currentTarget) {
|
|
68
|
-
e.currentTarget.focus();
|
|
69
|
-
e.currentTarget.selectionStart = e.currentTarget.value.length;
|
|
70
|
-
}
|
|
71
|
-
}}
|
|
72
69
|
inputProps={{
|
|
73
70
|
style: { width: "100%" },
|
|
74
71
|
placeholder: props.placeholder,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createContext, RefObject } from "react";
|
|
2
|
-
import {
|
|
1
|
+
import { createContext, RefObject, useContext } from "react";
|
|
2
|
+
import { GridBaseRow } from "../components/Grid";
|
|
3
3
|
|
|
4
4
|
export interface PropsType {
|
|
5
5
|
value: any;
|
|
@@ -9,18 +9,27 @@ export interface PropsType {
|
|
|
9
9
|
updateValue: (saveFn: (selectedRows: any[]) => Promise<boolean>) => Promise<boolean>;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export interface GridPopoverContextType<RowType extends GridBaseRow> {
|
|
13
13
|
anchorRef: RefObject<Element>;
|
|
14
14
|
saving: boolean;
|
|
15
15
|
setSaving: (saving: boolean) => void;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
field: keyof RowType;
|
|
17
|
+
value: any;
|
|
18
|
+
data: RowType;
|
|
19
|
+
selectedRows: RowType[];
|
|
20
|
+
updateValue: (saveFn: (selectedRows: any[]) => Promise<boolean>) => Promise<boolean>;
|
|
21
|
+
}
|
|
19
22
|
|
|
20
|
-
export const GridPopoverContext = createContext<GridPopoverContextType
|
|
23
|
+
export const GridPopoverContext = createContext<GridPopoverContextType<any>>({
|
|
21
24
|
anchorRef: { current: null },
|
|
22
25
|
saving: false,
|
|
23
26
|
setSaving: () => {},
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
field: "",
|
|
28
|
+
value: null,
|
|
29
|
+
data: {} as GridBaseRow,
|
|
30
|
+
selectedRows: [],
|
|
31
|
+
updateValue: async () => false,
|
|
26
32
|
});
|
|
33
|
+
|
|
34
|
+
export const useGridPopoverContext = <RowType extends GridBaseRow>() =>
|
|
35
|
+
useContext<GridPopoverContextType<RowType>>(GridPopoverContext);
|
|
@@ -1,41 +1,35 @@
|
|
|
1
|
-
import { ReactNode, RefObject, useCallback, useContext, useRef, useState } from "react";
|
|
2
|
-
import { GridPopoverContext
|
|
1
|
+
import { ReactNode, RefObject, useCallback, useContext, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { GridPopoverContext } from "./GridPopoverContext";
|
|
3
3
|
import { ICellEditorParams } from "ag-grid-community";
|
|
4
4
|
import { GridContext } from "./GridContext";
|
|
5
5
|
import { sortBy } from "lodash-es";
|
|
6
6
|
import { GridBaseRow } from "../components/Grid";
|
|
7
7
|
|
|
8
8
|
interface GridPopoverContextProps {
|
|
9
|
+
props: ICellEditorParams;
|
|
9
10
|
children: ReactNode;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export const GridPopoverContextProvider = (props: GridPopoverContextProps) => {
|
|
13
|
+
export const GridPopoverContextProvider = ({ props, children }: GridPopoverContextProps) => {
|
|
13
14
|
const { getSelectedRows, updatingCells } = useContext(GridContext);
|
|
14
|
-
const anchorRef = useRef<Element>();
|
|
15
|
-
const propsRef = useRef<PropsType>({} as PropsType);
|
|
15
|
+
const anchorRef = useRef<Element>(props.eGridCell);
|
|
16
16
|
|
|
17
17
|
const [saving, setSaving] = useState(false);
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
const { colDef } = props;
|
|
20
|
+
const { cellEditorParams } = colDef;
|
|
21
|
+
const multiEdit = cellEditorParams?.multiEdit ?? false;
|
|
22
|
+
// Then item that is clicked on will always be first in the list
|
|
23
|
+
const selectedRows = useMemo(
|
|
24
|
+
() => (multiEdit ? sortBy(getSelectedRows(), (row) => row.id !== props.data.id) : [props.data as GridBaseRow]),
|
|
25
|
+
[getSelectedRows, multiEdit, props.data],
|
|
26
|
+
);
|
|
27
|
+
const field = props.colDef?.field ?? "";
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
field,
|
|
32
|
-
selectedRows,
|
|
33
|
-
updateValue: async (saveFn: (selectedRows: any[]) => Promise<boolean>): Promise<boolean> => {
|
|
34
|
-
return !saving && (await updatingCells({ selectedRows, field }, saveFn, setSaving));
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
},
|
|
38
|
-
[getSelectedRows, saving, updatingCells],
|
|
29
|
+
const updateValue = useCallback(
|
|
30
|
+
async (saveFn: (selectedRows: any[]) => Promise<boolean>): Promise<boolean> =>
|
|
31
|
+
!saving && (await updatingCells({ selectedRows, field }, saveFn, setSaving)),
|
|
32
|
+
[field, saving, selectedRows, updatingCells],
|
|
39
33
|
);
|
|
40
34
|
|
|
41
35
|
return (
|
|
@@ -44,11 +38,14 @@ export const GridPopoverContextProvider = (props: GridPopoverContextProps) => {
|
|
|
44
38
|
anchorRef: anchorRef as any as RefObject<Element>,
|
|
45
39
|
saving,
|
|
46
40
|
setSaving,
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
selectedRows,
|
|
42
|
+
field,
|
|
43
|
+
data: props.data,
|
|
44
|
+
value: props.value,
|
|
45
|
+
updateValue,
|
|
49
46
|
}}
|
|
50
47
|
>
|
|
51
|
-
{
|
|
48
|
+
{children}
|
|
52
49
|
</GridPopoverContext.Provider>
|
|
53
50
|
);
|
|
54
51
|
};
|
|
@@ -34,7 +34,19 @@ export const TextAreaInput = (props: LuiTextAreaInputProps) => {
|
|
|
34
34
|
<div className="LuiTextAreaInput-wrapper">
|
|
35
35
|
{" "}
|
|
36
36
|
{/* wrapper div used for error styling */}
|
|
37
|
-
<textarea
|
|
37
|
+
<textarea
|
|
38
|
+
id={id}
|
|
39
|
+
value={props.value}
|
|
40
|
+
onChange={props.onChange}
|
|
41
|
+
rows={5}
|
|
42
|
+
{...props.inputProps}
|
|
43
|
+
onMouseEnter={(e) => {
|
|
44
|
+
if (document.activeElement != e.currentTarget) {
|
|
45
|
+
e.currentTarget.focus();
|
|
46
|
+
e.currentTarget.selectionStart = e.currentTarget.value.length;
|
|
47
|
+
}
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
38
50
|
</div>
|
|
39
51
|
</label>
|
|
40
52
|
|
|
@@ -24,8 +24,6 @@ export interface LuiTextInputProps {
|
|
|
24
24
|
|
|
25
25
|
placeholder?: string;
|
|
26
26
|
formatted?: string;
|
|
27
|
-
|
|
28
|
-
onMouseEnter?: (e: React.MouseEvent<HTMLTextAreaElement>) => void;
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
|
|
@@ -48,6 +46,9 @@ export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
|
|
|
48
46
|
onChange={props.onChange}
|
|
49
47
|
onFocus={props.onFocus}
|
|
50
48
|
onClick={props.onClick}
|
|
49
|
+
onMouseEnter={(e) => {
|
|
50
|
+
e.currentTarget.focus();
|
|
51
|
+
}}
|
|
51
52
|
{...props.inputProps}
|
|
52
53
|
/>
|
|
53
54
|
<span className={"LuiTextInput-formatted"}>{props.formatted}</span>
|
|
@@ -1,28 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
2
2
|
import { flushSync } from "react-dom";
|
|
3
|
-
import { useBEM, useCombinedRef,
|
|
4
|
-
import { getPositionHelpers,
|
|
3
|
+
import { useBEM, useCombinedRef, useItems, useLayoutEffect } from "../hooks";
|
|
4
|
+
import { getPositionHelpers, positionContextMenu, positionMenu } from "../positionUtils";
|
|
5
5
|
import {
|
|
6
|
-
mergeProps,
|
|
7
6
|
batchedUpdates,
|
|
7
|
+
CloseReason,
|
|
8
8
|
commonProps,
|
|
9
9
|
floatEqual,
|
|
10
|
+
FocusPositions,
|
|
10
11
|
getScrollAncestor,
|
|
11
12
|
getTransition,
|
|
12
|
-
|
|
13
|
+
HoverActionTypes,
|
|
13
14
|
isMenuOpen,
|
|
14
|
-
menuClass,
|
|
15
|
-
menuArrowClass,
|
|
16
|
-
CloseReason,
|
|
17
15
|
Keys,
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
menuArrowClass,
|
|
17
|
+
menuClass,
|
|
18
|
+
mergeProps,
|
|
19
|
+
safeCall,
|
|
20
20
|
} from "../utils";
|
|
21
21
|
import { ControlledMenuProps, MenuDirection } from "../types";
|
|
22
22
|
import { MenuListItemContext } from "../contexts/MenuListItemContext";
|
|
23
23
|
import { HoverItemContext } from "../contexts/HoverItemContext";
|
|
24
24
|
import { MenuListContext } from "../contexts/MenuListContext";
|
|
25
25
|
import { SettingsContext } from "../contexts/SettingsContext";
|
|
26
|
+
import { debounce } from "lodash-es";
|
|
26
27
|
|
|
27
28
|
export const MenuList = ({
|
|
28
29
|
ariaLabel,
|
|
@@ -75,6 +76,7 @@ export const MenuList = ({
|
|
|
75
76
|
const focusRef = useRef<HTMLDivElement>(null);
|
|
76
77
|
const arrowRef = useRef<HTMLDivElement>(null);
|
|
77
78
|
const prevOpen = useRef(false);
|
|
79
|
+
const latestWindowSize = useRef({ width: 0, height: 0 });
|
|
78
80
|
const latestMenuSize = useRef({ width: 0, height: 0 });
|
|
79
81
|
const latestHandlePosition = useRef(() => {});
|
|
80
82
|
const { hoverItem, dispatch, updateItems } = useItems(menuRef, focusRef);
|
|
@@ -320,6 +322,36 @@ export const MenuList = ({
|
|
|
320
322
|
return () => resizeObserver.unobserve(observeTarget);
|
|
321
323
|
}, [reposition]);
|
|
322
324
|
|
|
325
|
+
// Matt added window resize observer
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
if (typeof ResizeObserver !== "function" || reposition === "initial") return;
|
|
328
|
+
|
|
329
|
+
const callback = debounce(() => {
|
|
330
|
+
const { width, height } = menuRef.current.ownerDocument.body.getBoundingClientRect();
|
|
331
|
+
if (width === 0 || height === 0) return;
|
|
332
|
+
if (
|
|
333
|
+
floatEqual(width, latestWindowSize.current.width, 1) &&
|
|
334
|
+
floatEqual(height, latestWindowSize.current.height, 1)
|
|
335
|
+
) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
latestWindowSize.current = { width, height };
|
|
339
|
+
flushSync(() => {
|
|
340
|
+
latestHandlePosition.current();
|
|
341
|
+
forceReposSubmenu();
|
|
342
|
+
});
|
|
343
|
+
}, 250);
|
|
344
|
+
|
|
345
|
+
const resizeObserver = new ResizeObserver(callback);
|
|
346
|
+
|
|
347
|
+
const observeTarget = menuRef.current.ownerDocument.body;
|
|
348
|
+
resizeObserver.observe(observeTarget, { box: "border-box" });
|
|
349
|
+
return () => {
|
|
350
|
+
callback.cancel();
|
|
351
|
+
resizeObserver.unobserve(observeTarget);
|
|
352
|
+
};
|
|
353
|
+
}, [reposition]);
|
|
354
|
+
|
|
323
355
|
useEffect(() => {
|
|
324
356
|
if (!isOpen) {
|
|
325
357
|
dispatch(HoverActionTypes.RESET, undefined, 0);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// FIXME hacking a default context in here is probably bad
|
|
2
1
|
import { createContext, MutableRefObject, RefObject } from "react";
|
|
3
2
|
import { ControlledMenuProps, MenuReposition, MenuViewScroll, RectElement, TransitionFieldType } from "../types";
|
|
4
3
|
|
|
@@ -17,4 +16,5 @@ interface SettingsContextType extends ControlledMenuProps {
|
|
|
17
16
|
viewScroll?: MenuViewScroll;
|
|
18
17
|
}
|
|
19
18
|
|
|
19
|
+
// FIXME hacking a default context in here is probably bad, but the context is mess
|
|
20
20
|
export const SettingsContext = createContext<SettingsContextType>({} as SettingsContextType);
|
|
@@ -3,9 +3,9 @@ import "./FormTest.scss";
|
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
4
|
import { LuiAlertModal, LuiAlertModalButtons, LuiButton, LuiTextInput } from "@linzjs/lui";
|
|
5
5
|
import { wait } from "../../utils/util";
|
|
6
|
-
import { CellEditorCommon
|
|
6
|
+
import { CellEditorCommon } from "../../components/GridCell";
|
|
7
7
|
import { useGridPopoverHook } from "../../components/GridPopoverHook";
|
|
8
|
-
import {
|
|
8
|
+
import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
9
9
|
|
|
10
10
|
export interface IFormTestRow {
|
|
11
11
|
id: number;
|
|
@@ -16,28 +16,30 @@ export interface IFormTestRow {
|
|
|
16
16
|
distance: number | null;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export const FormTest =
|
|
20
|
-
const
|
|
21
|
-
const [v1, ...v2] =
|
|
19
|
+
export const FormTest = (props: CellEditorCommon): JSX.Element => {
|
|
20
|
+
const { value } = useGridPopoverContext<IFormTestRow>();
|
|
21
|
+
const [v1, ...v2] = value.split(" ");
|
|
22
22
|
|
|
23
23
|
const [nameType, setNameType] = useState(v1);
|
|
24
24
|
const [numba, setNumba] = useState(v2.join(" "));
|
|
25
25
|
|
|
26
|
-
const save = useCallback(
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const save = useCallback(
|
|
27
|
+
async (selectedRows: IFormTestRow[]): Promise<boolean> => {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log("onSave", selectedRows, nameType, numba);
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
await wait(1000);
|
|
31
|
+
selectedRows.forEach((row) => (row.name = [nameType, numba].join(" ")));
|
|
32
|
+
await wait(1000);
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// Close form
|
|
35
|
+
return true;
|
|
36
|
+
},
|
|
37
|
+
[nameType, numba],
|
|
38
|
+
);
|
|
37
39
|
|
|
38
40
|
const [showModal, setShowModal] = useState<boolean>(false);
|
|
39
41
|
|
|
40
|
-
const { popoverWrapper } = useGridPopoverHook({ className:
|
|
42
|
+
const { popoverWrapper } = useGridPopoverHook({ className: props.className, save });
|
|
41
43
|
|
|
42
44
|
return popoverWrapper(
|
|
43
45
|
<>
|