@linzjs/step-ag-grid 2.4.11 → 3.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 +301 -249
- 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 +7 -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 +5 -3
- package/dist/src/contexts/GridPopoverContextProvider.d.ts +3 -1
- package/dist/src/lui/TextInputFormatted.d.ts +3 -2
- package/dist/step-ag-grid.esm.js +302 -250
- 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 +3 -7
- package/src/components/GridSubComponentTextArea.tsx +0 -6
- package/src/components/gridForm/GridFormDropDown.tsx +23 -30
- package/src/components/gridForm/GridFormEditBearing.tsx +10 -8
- package/src/components/gridForm/GridFormMessage.tsx +9 -7
- package/src/components/gridForm/GridFormMultiSelect.tsx +56 -48
- package/src/components/gridForm/GridFormPopoverMenu.tsx +94 -29
- package/src/components/gridForm/GridFormSubComponentTextInput.tsx +2 -1
- package/src/components/gridForm/GridFormTextArea.tsx +8 -8
- package/src/components/gridForm/GridFormTextInput.tsx +12 -16
- package/src/contexts/GridPopoverContext.tsx +10 -5
- package/src/contexts/GridPopoverContextProvider.tsx +24 -27
- package/src/lui/TextAreaInput.tsx +13 -1
- package/src/lui/TextInputFormatted.tsx +14 -3
- package/src/react-menu3/components/MenuList.tsx +42 -10
- package/src/react-menu3/contexts/SettingsContext.ts +1 -1
- package/src/stories/grid/FormTest.tsx +18 -16
- 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,10 +1,12 @@
|
|
|
1
1
|
import { GridBaseRow } from "../Grid";
|
|
2
2
|
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
3
|
-
import { GridContext } from "../../contexts/GridContext";
|
|
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 { GridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
8
10
|
|
|
9
11
|
export interface GridFormPopoutMenuProps<RowType extends GridBaseRow> extends CellEditorCommon {
|
|
10
12
|
options: (selectedRows: RowType[]) => Promise<MenuOption<RowType>[]>;
|
|
@@ -17,24 +19,35 @@ interface MenuSeparatorType {
|
|
|
17
19
|
__isMenuSeparator__: boolean;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export interface MenuOption<RowType> {
|
|
22
|
+
export interface SelectedMenuOptionResult<RowType extends GridBaseRow> extends MenuOption<RowType> {
|
|
23
|
+
subValue: any;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MenuOption<RowType extends GridBaseRow> {
|
|
21
27
|
label: JSX.Element | string | MenuSeparatorType;
|
|
22
|
-
action?: (selectedRows: RowType[]) => Promise<
|
|
28
|
+
action?: (selectedRows: RowType[], menuOption: SelectedMenuOptionResult<RowType>) => Promise<void>;
|
|
23
29
|
disabled?: string | boolean;
|
|
24
30
|
supportsMultiEdit: boolean;
|
|
25
31
|
hidden?: boolean;
|
|
32
|
+
subComponent?: (props: any) => JSX.Element;
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
/**
|
|
29
36
|
* NOTE: If the popout menu doesn't appear on single click when also selecting row it's because
|
|
30
37
|
* you need a useMemo around your columnDefs
|
|
31
38
|
*/
|
|
32
|
-
export const GridFormPopoverMenu = <RowType extends GridBaseRow>(
|
|
33
|
-
const
|
|
34
|
-
|
|
39
|
+
export const GridFormPopoverMenu = <RowType extends GridBaseRow>(props: GridFormPopoutMenuProps<RowType>) => {
|
|
40
|
+
const { selectedRows, updateValue } = useContext(GridPopoverContext);
|
|
41
|
+
|
|
35
42
|
const optionsInitialising = useRef(false);
|
|
36
43
|
const [options, setOptions] = useState<MenuOption<RowType>[]>();
|
|
37
44
|
|
|
45
|
+
// Save triggers during async action processing which triggers another action(), this ref blocks that
|
|
46
|
+
const actionProcessing = useRef(false);
|
|
47
|
+
const [subComponentSelected, setSubComponentSelected] = useState<MenuOption<RowType> | null>(null);
|
|
48
|
+
const subComponentIsValid = useRef(false);
|
|
49
|
+
const [subSelectedValue, setSubSelectedValue] = useState<any>();
|
|
50
|
+
|
|
38
51
|
// Load up options list if it's async function
|
|
39
52
|
useEffect(() => {
|
|
40
53
|
if (options || optionsInitialising.current) return;
|
|
@@ -42,33 +55,61 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(_props: GridFor
|
|
|
42
55
|
const optionsConf = props.options ?? [];
|
|
43
56
|
|
|
44
57
|
(async () => {
|
|
45
|
-
|
|
46
|
-
setOptions(await optionsConf(props.selectedRows));
|
|
47
|
-
} else {
|
|
48
|
-
setOptions(optionsConf);
|
|
49
|
-
}
|
|
50
|
-
|
|
58
|
+
setOptions(typeof optionsConf == "function" ? await optionsConf(selectedRows) : optionsConf);
|
|
51
59
|
optionsInitialising.current = false;
|
|
52
60
|
})();
|
|
53
|
-
}, [options, props.options,
|
|
61
|
+
}, [options, props.options, selectedRows]);
|
|
54
62
|
|
|
55
63
|
const actionClick = useCallback(
|
|
56
|
-
async (menuOption: MenuOption<
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
async (menuOption: MenuOption<RowType>) => {
|
|
65
|
+
actionProcessing.current = true;
|
|
66
|
+
return updateValue(async () => {
|
|
67
|
+
const result = { ...menuOption, subValue: subSelectedValue };
|
|
68
|
+
menuOption.action && (await menuOption.action(selectedRows, result));
|
|
69
|
+
actionProcessing.current = false;
|
|
59
70
|
return true;
|
|
60
71
|
});
|
|
61
72
|
},
|
|
62
|
-
[
|
|
73
|
+
[selectedRows, subSelectedValue, updateValue],
|
|
63
74
|
);
|
|
64
75
|
|
|
65
|
-
const
|
|
76
|
+
const onMenuItemClick = useCallback(
|
|
77
|
+
(e: ClickEvent, item: MenuOption<RowType>) => {
|
|
78
|
+
if (item.subComponent) {
|
|
79
|
+
subComponentIsValid.current = false;
|
|
80
|
+
setSubSelectedValue(null);
|
|
81
|
+
setSubComponentSelected(subComponentSelected === item ? null : item);
|
|
82
|
+
e.keepOpen = true;
|
|
83
|
+
} else {
|
|
84
|
+
setSubComponentSelected(null);
|
|
85
|
+
actionClick(item).then();
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
[actionClick, subComponentSelected],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const selectedRowCount = selectedRows.length;
|
|
66
92
|
|
|
67
93
|
const filteredOptions = options?.filter((menuOption) => {
|
|
68
94
|
return menuOption.label === PopoutMenuSeparator || selectedRowCount === 1 || menuOption.supportsMultiEdit;
|
|
69
95
|
});
|
|
70
96
|
|
|
71
|
-
const
|
|
97
|
+
const save = useCallback(async () => {
|
|
98
|
+
// if a subcomponent is open we assume that it's meant to be saved.
|
|
99
|
+
if (!actionProcessing.current && subComponentSelected) {
|
|
100
|
+
if (!subComponentIsValid.current) return false;
|
|
101
|
+
await actionClick(subComponentSelected);
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}, [actionClick, subComponentSelected]);
|
|
105
|
+
|
|
106
|
+
const { popoverWrapper, triggerSave } = useGridPopoverHook({ className: props.className, save });
|
|
107
|
+
|
|
108
|
+
const localTriggerSave = async (reason?: string) => {
|
|
109
|
+
if (!subComponentIsValid.current) return;
|
|
110
|
+
return triggerSave(reason);
|
|
111
|
+
};
|
|
112
|
+
|
|
72
113
|
return popoverWrapper(
|
|
73
114
|
<ComponentLoadingWrapper loading={!filteredOptions} className={"GridFormPopupMenu"}>
|
|
74
115
|
<>
|
|
@@ -77,14 +118,38 @@ export const GridFormPopoverMenu = <RowType extends GridBaseRow>(_props: GridFor
|
|
|
77
118
|
<MenuDivider key={`$$divider_${index}`} />
|
|
78
119
|
) : (
|
|
79
120
|
!item.hidden && (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
121
|
+
<>
|
|
122
|
+
<MenuItem
|
|
123
|
+
key={`${item.label}`}
|
|
124
|
+
onClick={(e: ClickEvent) => onMenuItemClick(e, item)}
|
|
125
|
+
disabled={!!item.disabled || !filteredOptions?.includes(item)}
|
|
126
|
+
title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
|
|
127
|
+
>
|
|
128
|
+
{item.label as JSX.Element | string}
|
|
129
|
+
</MenuItem>
|
|
130
|
+
{item.subComponent && subComponentSelected === item && (
|
|
131
|
+
<FocusableItem className={"LuiDeprecatedForms"} key={`${item.label}_subcomponent`}>
|
|
132
|
+
{(_: any) =>
|
|
133
|
+
item.subComponent && (
|
|
134
|
+
<GridSubComponentContext.Provider
|
|
135
|
+
value={{
|
|
136
|
+
value: subSelectedValue,
|
|
137
|
+
setValue: (value: any) => {
|
|
138
|
+
setSubSelectedValue(value);
|
|
139
|
+
},
|
|
140
|
+
setValid: (valid: boolean) => {
|
|
141
|
+
subComponentIsValid.current = valid;
|
|
142
|
+
},
|
|
143
|
+
triggerSave: localTriggerSave,
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
<item.subComponent />
|
|
147
|
+
</GridSubComponentContext.Provider>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
</FocusableItem>
|
|
151
|
+
)}
|
|
152
|
+
</>
|
|
88
153
|
)
|
|
89
154
|
),
|
|
90
155
|
)}
|
|
@@ -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;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { useCallback, useState } from "react";
|
|
1
|
+
import { useCallback, useContext, 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 { GridPopoverContext } 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 } = useContext(GridPopoverContext);
|
|
19
|
+
const [value, setValue] = useState(initialVale ?? "");
|
|
19
20
|
|
|
20
21
|
const invalid = useCallback(() => {
|
|
21
22
|
if (props.required && value.length == 0) {
|
|
@@ -34,13 +35,12 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(_props: GridFormTe
|
|
|
34
35
|
async (selectedRows: any[]): 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;
|
|
@@ -48,7 +48,7 @@ export const GridFormTextArea = <RowType extends GridBaseRow>(_props: GridFormTe
|
|
|
48
48
|
selectedRows.forEach((row) => (row[field] = value));
|
|
49
49
|
return true;
|
|
50
50
|
},
|
|
51
|
-
[
|
|
51
|
+
[invalid, initialVale, value, props, field],
|
|
52
52
|
);
|
|
53
53
|
const { popoverWrapper } = useGridPopoverHook({ className: props.className, save });
|
|
54
54
|
return popoverWrapper(
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { useCallback, useState } from "react";
|
|
1
|
+
import { useCallback, useContext, 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 { GridPopoverContext } 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 } = useContext(GridPopoverContext);
|
|
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,15 @@ 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
|
}
|
|
53
|
+
// @ts-ignore row[field] row is of type any
|
|
52
54
|
selectedRows.forEach((row) => (row[field] = trimmedValue));
|
|
53
55
|
return true;
|
|
54
56
|
},
|
|
55
|
-
[invalid, value, initValue, props],
|
|
57
|
+
[invalid, value, initValue, props, field],
|
|
56
58
|
);
|
|
57
59
|
const { popoverWrapper, triggerSave } = useGridPopoverHook({ className: props.className, save });
|
|
58
60
|
|
|
@@ -63,12 +65,6 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(_props: GridFormT
|
|
|
63
65
|
onChange={(e) => setValue(e.target.value)}
|
|
64
66
|
error={invalid()}
|
|
65
67
|
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
68
|
inputProps={{
|
|
73
69
|
style: { width: "100%" },
|
|
74
70
|
placeholder: props.placeholder,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createContext, RefObject } from "react";
|
|
2
|
-
import { ICellEditorParams } from "ag-grid-community";
|
|
3
2
|
|
|
4
3
|
export interface PropsType {
|
|
5
4
|
value: any;
|
|
@@ -13,14 +12,20 @@ export type GridPopoverContextType = {
|
|
|
13
12
|
anchorRef: RefObject<Element>;
|
|
14
13
|
saving: boolean;
|
|
15
14
|
setSaving: (saving: boolean) => void;
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
field: string;
|
|
16
|
+
value: any;
|
|
17
|
+
data: any;
|
|
18
|
+
selectedRows: any[];
|
|
19
|
+
updateValue: (saveFn: (selectedRows: any[]) => Promise<boolean>) => Promise<boolean>;
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
export const GridPopoverContext = createContext<GridPopoverContextType>({
|
|
21
23
|
anchorRef: { current: null },
|
|
22
24
|
saving: false,
|
|
23
25
|
setSaving: () => {},
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
field: "",
|
|
27
|
+
value: null,
|
|
28
|
+
data: null,
|
|
29
|
+
selectedRows: [],
|
|
30
|
+
updateValue: async () => false,
|
|
26
31
|
});
|
|
@@ -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
|
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import "./TextInputFormatted.scss";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ChangeEventHandler,
|
|
5
|
+
DetailedHTMLProps,
|
|
6
|
+
FocusEventHandler,
|
|
7
|
+
InputHTMLAttributes,
|
|
8
|
+
MouseEventHandler,
|
|
9
|
+
} from "react";
|
|
4
10
|
|
|
5
11
|
import clsx from "clsx";
|
|
6
12
|
import { LuiIcon } from "@linzjs/lui";
|
|
7
13
|
|
|
8
14
|
export interface LuiTextInputProps {
|
|
9
15
|
onChange?: ChangeEventHandler<HTMLInputElement>;
|
|
16
|
+
onFocus?: FocusEventHandler<HTMLInputElement>;
|
|
17
|
+
onClick?: MouseEventHandler<HTMLInputElement>;
|
|
10
18
|
inputProps?: DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
|
|
11
19
|
error?: string | boolean | null;
|
|
12
20
|
warning?: string | boolean | null;
|
|
@@ -16,8 +24,6 @@ export interface LuiTextInputProps {
|
|
|
16
24
|
|
|
17
25
|
placeholder?: string;
|
|
18
26
|
formatted?: string;
|
|
19
|
-
|
|
20
|
-
onMouseEnter?: (e: React.MouseEvent<HTMLTextAreaElement>) => void;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
|
|
@@ -38,6 +44,11 @@ export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
|
|
|
38
44
|
spellCheck={true}
|
|
39
45
|
defaultValue={props.value}
|
|
40
46
|
onChange={props.onChange}
|
|
47
|
+
onFocus={props.onFocus}
|
|
48
|
+
onClick={props.onClick}
|
|
49
|
+
onMouseEnter={(e) => {
|
|
50
|
+
e.currentTarget.focus();
|
|
51
|
+
}}
|
|
41
52
|
{...props.inputProps}
|
|
42
53
|
/>
|
|
43
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);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "./FormTest.scss";
|
|
2
2
|
|
|
3
|
-
import { useCallback, useState } from "react";
|
|
3
|
+
import { useCallback, useContext, 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 { GridPopoverContext } 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 } = useContext(GridPopoverContext);
|
|
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
|
<>
|
|
@@ -143,7 +143,6 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
|
|
|
143
143
|
action: async (selectedRows) => {
|
|
144
144
|
await wait(1500);
|
|
145
145
|
setRowData(rowData.filter((data) => !selectedRows.some((row) => row.id == data.id)));
|
|
146
|
-
return true;
|
|
147
146
|
},
|
|
148
147
|
supportsMultiEdit: true,
|
|
149
148
|
},
|