@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
package/package.json
CHANGED
|
@@ -9,15 +9,14 @@ import {
|
|
|
9
9
|
} from "./gridRender/GridRenderGenericCell";
|
|
10
10
|
import { ColDef, ICellEditorParams, ICellRendererParams } from "ag-grid-community";
|
|
11
11
|
import { GridLoadableCell } from "./GridLoadableCell";
|
|
12
|
-
import { GridIcon } from "
|
|
12
|
+
import { GridIcon } from "./GridIcon";
|
|
13
13
|
import { ValueFormatterParams } from "ag-grid-community/dist/lib/entities/colDef";
|
|
14
|
-
import { GridPopoverContext } from "../contexts/GridPopoverContext";
|
|
15
14
|
import { GridPopoverContextProvider } from "../contexts/GridPopoverContextProvider";
|
|
16
15
|
|
|
17
16
|
export interface GenericCellEditorProps<E> {
|
|
18
17
|
multiEdit?: boolean;
|
|
19
18
|
editor?: (editorProps: E) => JSX.Element;
|
|
20
|
-
editorParams?: E;
|
|
19
|
+
editorParams?: E;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
export const GridCellRenderer = (props: ICellRendererParams) => {
|
|
@@ -46,6 +45,7 @@ export const GridCellRenderer = (props: ICellRendererParams) => {
|
|
|
46
45
|
// This is so that typescript retains the row type to pass to the GridCells
|
|
47
46
|
export interface ColDefT<RowType extends GridBaseRow> extends ColDef {
|
|
48
47
|
_?: RowType;
|
|
48
|
+
editor?: (editorProps: any) => JSX.Element;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/*
|
|
@@ -66,7 +66,7 @@ export const GridCell = <RowType extends GridBaseRow, Props extends CellEditorCo
|
|
|
66
66
|
...(custom?.editor && {
|
|
67
67
|
cellClass: custom?.multiEdit ? GenericMultiEditCellClass : undefined,
|
|
68
68
|
editable: props.editable ?? true,
|
|
69
|
-
cellEditor:
|
|
69
|
+
cellEditor: GenericCellEditorComponentWrapper(custom),
|
|
70
70
|
}),
|
|
71
71
|
...(custom?.editorParams && {
|
|
72
72
|
cellEditorParams: { ...custom.editorParams, multiEdit: custom.multiEdit },
|
|
@@ -91,37 +91,13 @@ export interface CellEditorCommon {
|
|
|
91
91
|
className?: string | undefined;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
export
|
|
95
|
-
|
|
96
|
-
data: RowType;
|
|
97
|
-
field: string | undefined;
|
|
98
|
-
selectedRows: RowType[];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// TODO memo?
|
|
102
|
-
export const GenericCellEditorComponent = (editor: (props: any) => JSX.Element) =>
|
|
103
|
-
forwardRef(function GenericCellEditorComponent2(props: ICellEditorParams, _) {
|
|
94
|
+
export const GenericCellEditorComponentWrapper = (custom?: { editor?: (props: any) => JSX.Element }) => {
|
|
95
|
+
return forwardRef(function GenericCellEditorComponentFr(cellEditorParams: ICellEditorParams, _) {
|
|
104
96
|
return (
|
|
105
|
-
<GridPopoverContextProvider>
|
|
106
|
-
<
|
|
97
|
+
<GridPopoverContextProvider props={cellEditorParams}>
|
|
98
|
+
{<cellEditorParams.colDef.cellRenderer {...cellEditorParams} {...cellEditorParams.colDef.cellRendererParams} />}
|
|
99
|
+
{custom?.editor && <custom.editor {...cellEditorParams} />}
|
|
107
100
|
</GridPopoverContextProvider>
|
|
108
101
|
);
|
|
109
102
|
});
|
|
110
|
-
|
|
111
|
-
const GenericCellEditorComponent3 = (props: ICellEditorParams & { editor: (props: any) => JSX.Element }) => {
|
|
112
|
-
const { setProps, propsRef } = useContext(GridPopoverContext);
|
|
113
|
-
|
|
114
|
-
const { colDef } = props;
|
|
115
|
-
const { cellEditorParams } = colDef;
|
|
116
|
-
const multiEdit = cellEditorParams?.multiEdit ?? false;
|
|
117
|
-
|
|
118
|
-
// TODO don't need all these props in context
|
|
119
|
-
setProps(props, multiEdit);
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
<>
|
|
123
|
-
{<colDef.cellRenderer {...props} />}
|
|
124
|
-
{props?.editor && <props.editor {...cellEditorParams} {...propsRef.current} />}
|
|
125
|
-
</>
|
|
126
|
-
);
|
|
127
103
|
};
|
|
@@ -12,7 +12,7 @@ export interface GridPopoverHookProps<RowType> {
|
|
|
12
12
|
|
|
13
13
|
export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopoverHookProps<RowType>) => {
|
|
14
14
|
const { stopEditing } = useContext(GridContext);
|
|
15
|
-
const { anchorRef, saving,
|
|
15
|
+
const { anchorRef, saving, updateValue } = useContext(GridPopoverContext);
|
|
16
16
|
const saveButtonRef = useRef<HTMLButtonElement>(null);
|
|
17
17
|
const [isOpen, setOpen] = useState(false);
|
|
18
18
|
|
|
@@ -22,16 +22,12 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(props: GridPopov
|
|
|
22
22
|
|
|
23
23
|
const triggerSave = useCallback(
|
|
24
24
|
async (reason?: string) => {
|
|
25
|
-
if (
|
|
26
|
-
reason == "cancel" ||
|
|
27
|
-
!props.save ||
|
|
28
|
-
(propsRef.current?.updateValue && (await propsRef.current?.updateValue(props.save)))
|
|
29
|
-
) {
|
|
25
|
+
if (reason == "cancel" || !props.save || (updateValue && (await updateValue(props.save)))) {
|
|
30
26
|
setOpen(false);
|
|
31
27
|
stopEditing();
|
|
32
28
|
}
|
|
33
29
|
},
|
|
34
|
-
[props, stopEditing,
|
|
30
|
+
[props.save, stopEditing, updateValue],
|
|
35
31
|
);
|
|
36
32
|
|
|
37
33
|
const popoverWrapper = useCallback(
|
|
@@ -49,12 +49,6 @@ export const GridSubComponentTextArea = (props: GridSubComponentTextAreaProps):
|
|
|
49
49
|
<TextInputFormatted
|
|
50
50
|
className={"free-text-input"}
|
|
51
51
|
value={value}
|
|
52
|
-
onMouseEnter={(e) => {
|
|
53
|
-
if (document.activeElement != e.currentTarget) {
|
|
54
|
-
e.currentTarget.focus();
|
|
55
|
-
e.currentTarget.selectionStart = e.currentTarget.value.length;
|
|
56
|
-
}
|
|
57
|
-
}}
|
|
58
52
|
onChange={(e) => setValue(e.target.value)}
|
|
59
53
|
error={validate(value)}
|
|
60
54
|
inputProps={{
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import "../../styles/GridFormDropDown.scss";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { useCallback, useContext, useEffect, useRef, useState
|
|
3
|
+
import { FocusableItem, MenuDivider, MenuHeader, MenuItem } from "../../react-menu3";
|
|
4
|
+
import { KeyboardEvent, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
5
5
|
import { GridBaseRow } from "../Grid";
|
|
6
6
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
7
7
|
import { GridContext } from "../../contexts/GridContext";
|
|
8
8
|
import { delay } from "lodash-es";
|
|
9
9
|
import debounce from "debounce-promise";
|
|
10
|
-
import { CellEditorCommon
|
|
10
|
+
import { CellEditorCommon } from "../GridCell";
|
|
11
11
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
12
|
+
import { GridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
12
13
|
|
|
13
14
|
export interface GridPopoutEditDropDownSelectedItem<RowType, ValueType> {
|
|
14
15
|
// Note the row that was clicked on will be first
|
|
@@ -54,10 +55,10 @@ export interface GridFormPopoutDropDownProps<RowType extends GridBaseRow, ValueT
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
57
|
-
|
|
58
|
+
props: GridFormPopoutDropDownProps<RowType, ValueType>,
|
|
58
59
|
) => {
|
|
59
|
-
const
|
|
60
|
-
const {
|
|
60
|
+
const { selectedRows, field, updateValue } = useContext(GridPopoverContext);
|
|
61
|
+
const { stopEditing } = useContext(GridContext);
|
|
61
62
|
|
|
62
63
|
const [filter, setFilter] = useState("");
|
|
63
64
|
const [filteredValues, setFilteredValues] = useState<any[]>([]);
|
|
@@ -66,9 +67,8 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
66
67
|
const [subComponentValues, setSubComponentValues] = useState<{ optionValue: any; subComponentValue: any }[]>([]);
|
|
67
68
|
|
|
68
69
|
const selectItemHandler = useCallback(
|
|
69
|
-
async (value: ValueType, subComponentValue?: ValueType): Promise<boolean> =>
|
|
70
|
-
|
|
71
|
-
return await updatingCells({ selectedRows: props.selectedRows, field }, async (selectedRows) => {
|
|
70
|
+
async (value: ValueType, subComponentValue?: ValueType): Promise<boolean> =>
|
|
71
|
+
updateValue(async (selectedRows) => {
|
|
72
72
|
const hasChanged = selectedRows.some((row) => row[field as keyof RowType] !== value);
|
|
73
73
|
if (hasChanged) {
|
|
74
74
|
if (props.onSelectedItem) {
|
|
@@ -78,22 +78,17 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
return true;
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
[props, updatingCells],
|
|
81
|
+
}),
|
|
82
|
+
[field, props, updateValue],
|
|
84
83
|
);
|
|
85
84
|
|
|
86
85
|
const selectFilterHandler = useCallback(
|
|
87
|
-
async (value: string)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (props.onSelectFilter) {
|
|
91
|
-
await props.onSelectFilter({ selectedRows, value });
|
|
92
|
-
}
|
|
86
|
+
async (value: string) =>
|
|
87
|
+
updateValue(async (selectedRows) => {
|
|
88
|
+
props.onSelectFilter && (await props.onSelectFilter({ selectedRows, value }));
|
|
93
89
|
return true;
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
[props, updatingCells],
|
|
90
|
+
}),
|
|
91
|
+
[props, updateValue],
|
|
97
92
|
);
|
|
98
93
|
|
|
99
94
|
// Load up options list if it's async function
|
|
@@ -104,7 +99,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
104
99
|
|
|
105
100
|
(async () => {
|
|
106
101
|
if (typeof optionsConf == "function") {
|
|
107
|
-
optionsConf = await optionsConf(
|
|
102
|
+
optionsConf = await optionsConf(selectedRows, filter);
|
|
108
103
|
}
|
|
109
104
|
|
|
110
105
|
const optionsList = optionsConf?.map((item) => {
|
|
@@ -127,7 +122,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
127
122
|
}
|
|
128
123
|
optionsInitialising.current = false;
|
|
129
124
|
})();
|
|
130
|
-
}, [filter, options, props]);
|
|
125
|
+
}, [filter, options, props, selectedRows]);
|
|
131
126
|
|
|
132
127
|
// Local filtering.
|
|
133
128
|
useEffect(() => {
|
|
@@ -213,9 +208,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
213
208
|
)}
|
|
214
209
|
<ComponentLoadingWrapper loading={!options} className={"GridFormDropDown-options"}>
|
|
215
210
|
<>
|
|
216
|
-
{options && options.length == filteredValues?.length &&
|
|
217
|
-
<MenuItem key={`${props.field}-empty`}>[Empty]</MenuItem>
|
|
218
|
-
)}
|
|
211
|
+
{options && options.length == filteredValues?.length && <MenuItem key={`${field}-empty`}>[Empty]</MenuItem>}
|
|
219
212
|
{options?.map((item: FinalSelectOption<ValueType | string>, index) =>
|
|
220
213
|
item.value === MenuSeparatorString ? (
|
|
221
214
|
<MenuDivider key={`$$divider_${index}`} />
|
|
@@ -225,18 +218,18 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
225
218
|
<div key={`menu-wrapper-${index}`}>
|
|
226
219
|
{!item.subComponent ? (
|
|
227
220
|
<MenuItem
|
|
228
|
-
key={`${
|
|
221
|
+
key={`${field}-${index}`}
|
|
229
222
|
disabled={!!item.disabled}
|
|
230
223
|
title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
|
|
231
224
|
value={item.value}
|
|
232
225
|
onClick={() => {
|
|
233
|
-
selectItemHandler(item.value as ValueType);
|
|
226
|
+
selectItemHandler(item.value as ValueType).then();
|
|
234
227
|
}}
|
|
235
228
|
>
|
|
236
229
|
{item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)}
|
|
237
230
|
</MenuItem>
|
|
238
231
|
) : (
|
|
239
|
-
<FocusableItem className={"LuiDeprecatedForms"} key={`${
|
|
232
|
+
<FocusableItem className={"LuiDeprecatedForms"} key={`${field}-${index}_subcomponent`}>
|
|
240
233
|
{(ref: any) =>
|
|
241
234
|
item.subComponent &&
|
|
242
235
|
item.subComponent(
|
|
@@ -273,7 +266,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
273
266
|
}
|
|
274
267
|
return false;
|
|
275
268
|
},
|
|
276
|
-
key: `${
|
|
269
|
+
key: `${field}-${index}_subcomponent_inner`,
|
|
277
270
|
},
|
|
278
271
|
ref,
|
|
279
272
|
)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import "../../styles/GridFormEditBearing.scss";
|
|
2
2
|
|
|
3
|
-
import { useCallback, useState } from "react";
|
|
3
|
+
import { useCallback, useContext, useState } from "react";
|
|
4
4
|
import { GridBaseRow } from "../Grid";
|
|
5
5
|
import { TextInputFormatted } from "../../lui/TextInputFormatted";
|
|
6
6
|
import { bearingNumberParser, bearingStringValidator, convertDDToDMS } from "../../utils/bearing";
|
|
7
7
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
8
|
-
import { CellEditorCommon
|
|
8
|
+
import { CellEditorCommon } from "../GridCell";
|
|
9
|
+
import { GridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
9
10
|
|
|
10
11
|
export interface GridFormEditBearingProps<RowType extends GridBaseRow> extends CellEditorCommon {
|
|
11
12
|
placeHolder?: string;
|
|
@@ -13,22 +14,23 @@ export interface GridFormEditBearingProps<RowType extends GridBaseRow> extends C
|
|
|
13
14
|
onSave?: (selectedRows: RowType[], value: number | null) => Promise<boolean>;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export const GridFormEditBearing = <RowType extends GridBaseRow>(
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
export const GridFormEditBearing = <RowType extends GridBaseRow>(props: GridFormEditBearingProps<RowType>) => {
|
|
18
|
+
const { field, value: initialValue } = useContext(GridPopoverContext);
|
|
19
|
+
|
|
20
|
+
const [value, setValue] = useState<string>(`${initialValue ?? ""}`);
|
|
21
|
+
|
|
19
22
|
const save = useCallback(
|
|
20
23
|
async (selectedRows: RowType[]): Promise<boolean> => {
|
|
21
24
|
if (bearingStringValidator(value)) return false;
|
|
22
25
|
const parsedValue = bearingNumberParser(value);
|
|
23
26
|
// Value didn't change so don't save just cancel
|
|
24
|
-
if (parsedValue ===
|
|
27
|
+
if (parsedValue === initialValue) {
|
|
25
28
|
return true;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
if (props.onSave) {
|
|
29
32
|
return await props.onSave(selectedRows, parsedValue);
|
|
30
33
|
} else {
|
|
31
|
-
const field = props.field;
|
|
32
34
|
if (field == null) {
|
|
33
35
|
console.error("field is not defined in ColDef");
|
|
34
36
|
} else {
|
|
@@ -37,7 +39,7 @@ export const GridFormEditBearing = <RowType extends GridBaseRow>(_props: GridFor
|
|
|
37
39
|
}
|
|
38
40
|
return true;
|
|
39
41
|
},
|
|
40
|
-
[props, value],
|
|
42
|
+
[field, initialValue, props, value],
|
|
41
43
|
);
|
|
42
44
|
const { triggerSave, popoverWrapper } = useGridPopoverHook({ className: props.className, save });
|
|
43
45
|
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
import clsx from "clsx";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
2
|
+
import { useContext, useEffect, useState } from "react";
|
|
3
3
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
4
4
|
import { GridBaseRow } from "../Grid";
|
|
5
5
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
6
|
-
import { CellEditorCommon
|
|
6
|
+
import { CellEditorCommon } from "../GridCell";
|
|
7
|
+
import { GridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
7
8
|
|
|
8
9
|
export interface GridFormMessageProps<RowType extends GridBaseRow> extends CellEditorCommon {
|
|
9
|
-
message: (
|
|
10
|
+
message: (selectedRows: RowType[]) => Promise<string | JSX.Element> | string | JSX.Element;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export const GridFormMessage = <RowType extends GridBaseRow>(
|
|
13
|
-
const
|
|
13
|
+
export const GridFormMessage = <RowType extends GridBaseRow>(props: GridFormMessageProps<RowType>) => {
|
|
14
|
+
const { selectedRows } = useContext(GridPopoverContext);
|
|
15
|
+
|
|
14
16
|
const [message, setMessage] = useState<string | JSX.Element | null>(null);
|
|
15
17
|
const { popoverWrapper } = useGridPopoverHook({ className: props.className });
|
|
16
18
|
|
|
17
19
|
useEffect(() => {
|
|
18
20
|
(async () => {
|
|
19
|
-
setMessage(await props.message(
|
|
21
|
+
setMessage(await props.message(selectedRows));
|
|
20
22
|
})().then();
|
|
21
|
-
}, [props]);
|
|
23
|
+
}, [props, selectedRows]);
|
|
22
24
|
|
|
23
25
|
return popoverWrapper(
|
|
24
26
|
<ComponentLoadingWrapper loading={message === null} className={clsx("GridFormMessage-container", props.className)}>
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import "../../styles/GridFormMultiSelect.scss";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { useCallback,
|
|
3
|
+
import { FocusableItem, MenuDivider, MenuItem } from "../../react-menu3";
|
|
4
|
+
import { KeyboardEvent, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { GridBaseRow } from "../Grid";
|
|
6
6
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
7
|
-
import { delay, pick,
|
|
7
|
+
import { delay, fromPairs, isEqual, omit, pick, toPairs } from "lodash-es";
|
|
8
8
|
import { LuiCheckboxInput } from "@linzjs/lui";
|
|
9
9
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
10
10
|
import { MenuSeparatorString } from "./GridFormDropDown";
|
|
11
|
-
import { CellEditorCommon
|
|
11
|
+
import { CellEditorCommon } from "../GridCell";
|
|
12
12
|
import { ClickEvent } from "../../react-menu3/types";
|
|
13
13
|
import { GridSubComponentContext } from "contexts/GridSubComponentContext";
|
|
14
|
+
import { GridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
14
15
|
|
|
15
16
|
interface MultiFinalSelectOption<ValueType> {
|
|
16
17
|
value: ValueType;
|
|
@@ -21,11 +22,10 @@ interface MultiFinalSelectOption<ValueType> {
|
|
|
21
22
|
export type MultiSelectOption<ValueType> = ValueType | MultiFinalSelectOption<ValueType>;
|
|
22
23
|
|
|
23
24
|
export interface SelectedOptionResult<ValueType> extends MultiFinalSelectOption<ValueType> {
|
|
24
|
-
subValue
|
|
25
|
+
subValue?: any;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export interface GridFormMultiSelectProps<RowType extends GridBaseRow, ValueType> extends CellEditorCommon {
|
|
28
|
-
// This overrides CellEditorCommon to provide some common class options
|
|
29
29
|
className?:
|
|
30
30
|
| "GridMultiSelect-containerSmall"
|
|
31
31
|
| "GridMultiSelect-containerMedium"
|
|
@@ -39,28 +39,22 @@ export interface GridFormMultiSelectProps<RowType extends GridBaseRow, ValueType
|
|
|
39
39
|
options:
|
|
40
40
|
| MultiSelectOption<ValueType>[]
|
|
41
41
|
| ((selectedRows: RowType[]) => Promise<MultiSelectOption<ValueType>[]> | MultiSelectOption<ValueType>[]);
|
|
42
|
-
initialSelectedValues?: (selectedRows: RowType[]) => any[] | Record<string, any>;
|
|
42
|
+
initialSelectedValues?: (selectedRows: RowType[]) => any[] | Record<string, null | any>;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
|
|
46
46
|
props: GridFormMultiSelectProps<RowType, ValueType>,
|
|
47
47
|
) => {
|
|
48
|
-
const { selectedRows } =
|
|
48
|
+
const { selectedRows } = useContext(GridPopoverContext);
|
|
49
49
|
|
|
50
|
-
const initialiseValues = useMemo(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const initialiseValues = useMemo(() => {
|
|
51
|
+
const r = props.initialSelectedValues && props.initialSelectedValues(selectedRows);
|
|
52
|
+
// convert array of strings to object<value,null>
|
|
53
|
+
return Array.isArray(r) ? fromPairs(r.map((v) => [v, null])) : r;
|
|
54
|
+
}, [props, selectedRows]);
|
|
54
55
|
|
|
55
56
|
const subComponentIsValid = useRef<Record<string, boolean>>({});
|
|
56
|
-
const [
|
|
57
|
-
initialiseValues && !Array.isArray(initialiseValues)
|
|
58
|
-
? pickBy(initialiseValues, (value) => typeof value !== "boolean")
|
|
59
|
-
: {},
|
|
60
|
-
);
|
|
61
|
-
const [selectedValues, setSelectedValues] = useState<any[]>(() =>
|
|
62
|
-
initialiseValues ? (Array.isArray(initialiseValues) ? initialiseValues : Object.keys(initialiseValues)) : [],
|
|
63
|
-
);
|
|
57
|
+
const [selectedValues, setSelectedValues] = useState<Record<string, null | any>>(() => initialiseValues ?? {});
|
|
64
58
|
|
|
65
59
|
const [filter, setFilter] = useState("");
|
|
66
60
|
const [filteredValues, setFilteredValues] = useState<any[]>([]);
|
|
@@ -70,23 +64,33 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
|
|
|
70
64
|
const save = useCallback(
|
|
71
65
|
async (selectedRows: RowType[]): Promise<boolean> => {
|
|
72
66
|
if (props.onSave) {
|
|
73
|
-
|
|
67
|
+
if (!options) {
|
|
68
|
+
console.error("options not initialised");
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const validations = pick(subComponentIsValid.current, Object.keys(selectedValues));
|
|
74
73
|
const notValid = Object.values(validations).some((v) => !v);
|
|
75
74
|
if (notValid) return false;
|
|
76
75
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
const menuOptionSubValueResult = toPairs(selectedValues).map(([value, subValue]) => {
|
|
77
|
+
const o = {
|
|
78
|
+
...options.find((o) => o.value == value),
|
|
79
|
+
} as SelectedOptionResult<ValueType>;
|
|
80
|
+
o.subValue = subValue;
|
|
81
|
+
return o;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (isEqual(initialiseValues, selectedValues)) {
|
|
85
|
+
// No changes to save
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return await props.onSave(selectedRows, menuOptionSubValueResult);
|
|
86
90
|
}
|
|
87
91
|
return true;
|
|
88
92
|
},
|
|
89
|
-
[options, props, selectedValues
|
|
93
|
+
[initialiseValues, options, props, selectedValues],
|
|
90
94
|
);
|
|
91
95
|
|
|
92
96
|
// Load up options list if it's async function
|
|
@@ -101,7 +105,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
|
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
const optionsList = optionsConf?.map((item) => {
|
|
104
|
-
if (item == null || typeof item
|
|
108
|
+
if (item == null || typeof item === "string" || typeof item === "number") {
|
|
105
109
|
item = { value: item as ValueType, label: item } as MultiFinalSelectOption<ValueType>;
|
|
106
110
|
}
|
|
107
111
|
return item;
|
|
@@ -134,6 +138,17 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
|
|
|
134
138
|
);
|
|
135
139
|
}, [props.filtered, filter, options]);
|
|
136
140
|
|
|
141
|
+
const toggleValue = useCallback(
|
|
142
|
+
(item: MultiFinalSelectOption<ValueType>) => {
|
|
143
|
+
if (`${item.value}` in selectedValues) {
|
|
144
|
+
setSelectedValues(omit(selectedValues, [`${item.value}`]));
|
|
145
|
+
} else {
|
|
146
|
+
setSelectedValues({ ...selectedValues, [`${item.value}`]: null });
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
[selectedValues],
|
|
150
|
+
);
|
|
151
|
+
|
|
137
152
|
const { popoverWrapper, triggerSave } = useGridPopoverHook({ className: props.className, save });
|
|
138
153
|
return popoverWrapper(
|
|
139
154
|
<ComponentLoadingWrapper loading={!options} className={"GridFormMultiSelect-container"}>
|
|
@@ -167,30 +182,21 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
|
|
|
167
182
|
) : filteredValues.includes(item.value) ? null : (
|
|
168
183
|
<div key={`${index}`}>
|
|
169
184
|
<MenuItem
|
|
170
|
-
key={`${index}`}
|
|
171
185
|
onClick={(e: ClickEvent) => {
|
|
172
186
|
e.keepOpen = true;
|
|
173
|
-
|
|
174
|
-
setSelectedValues(selectedValues.filter((value) => value != item.value));
|
|
175
|
-
} else {
|
|
176
|
-
setSelectedValues([...selectedValues, item.value]);
|
|
177
|
-
}
|
|
187
|
+
toggleValue(item);
|
|
178
188
|
}}
|
|
179
189
|
onKeyDown={async (e: KeyboardEvent) => {
|
|
180
190
|
if (e.key === "Enter") triggerSave().then();
|
|
181
191
|
else if (e.key === " ") {
|
|
182
|
-
|
|
183
|
-
setSelectedValues(selectedValues.filter((value) => value != item.value));
|
|
184
|
-
} else {
|
|
185
|
-
setSelectedValues([...selectedValues, item.value]);
|
|
186
|
-
}
|
|
192
|
+
toggleValue(item);
|
|
187
193
|
e.preventDefault();
|
|
188
194
|
e.stopPropagation();
|
|
189
195
|
}
|
|
190
196
|
}}
|
|
191
197
|
>
|
|
192
198
|
<LuiCheckboxInput
|
|
193
|
-
isChecked={
|
|
199
|
+
isChecked={`${item.value}` in selectedValues}
|
|
194
200
|
value={`${item.value}`}
|
|
195
201
|
label={item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)}
|
|
196
202
|
inputProps={{
|
|
@@ -206,16 +212,18 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(
|
|
|
206
212
|
/>
|
|
207
213
|
</MenuItem>
|
|
208
214
|
|
|
209
|
-
{
|
|
215
|
+
{`${item.value}` in selectedValues && item.subComponent && (
|
|
210
216
|
<FocusableItem className={"LuiDeprecatedForms"} key={`${item.value}_subcomponent`}>
|
|
211
217
|
{(_: any) =>
|
|
212
218
|
item.subComponent && (
|
|
213
219
|
<GridSubComponentContext.Provider
|
|
214
220
|
value={{
|
|
215
|
-
value:
|
|
221
|
+
value: selectedValues[`${item.value}`],
|
|
216
222
|
setValue: (value: any) => {
|
|
217
|
-
|
|
218
|
-
|
|
223
|
+
setSelectedValues({
|
|
224
|
+
...selectedValues,
|
|
225
|
+
[`${item.value}`]: value,
|
|
226
|
+
});
|
|
219
227
|
},
|
|
220
228
|
setValid: (valid: boolean) => {
|
|
221
229
|
subComponentIsValid.current[`${item.value}`] = valid;
|