@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
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
|
};
|
|
@@ -2,7 +2,7 @@ import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
|
2
2
|
import { GridContext } from "../contexts/GridContext";
|
|
3
3
|
import { GridBaseRow } from "./Grid";
|
|
4
4
|
import { ControlledMenu } from "../react-menu3";
|
|
5
|
-
import {
|
|
5
|
+
import { useGridPopoverContext } from "../contexts/GridPopoverContext";
|
|
6
6
|
import { MenuCloseEvent } from "../react-menu3/types";
|
|
7
7
|
|
|
8
8
|
export interface GridPopoverHookProps<RowType> {
|
|
@@ -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 } = useGridPopoverContext<RowType>();
|
|
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 { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
12
13
|
|
|
13
14
|
export interface GridPopoutEditDropDownSelectedItem<RowType, ValueType> {
|
|
14
15
|
// Note the row that was clicked on will be first
|
|
@@ -43,6 +44,7 @@ export interface GridFormPopoutDropDownProps<RowType extends GridBaseRow, ValueT
|
|
|
43
44
|
| "GridPopoverEditDropDown-containerUnlimited"
|
|
44
45
|
| string
|
|
45
46
|
| undefined;
|
|
47
|
+
// local means the filter won't change if it's reloaded, reload means it does change
|
|
46
48
|
filtered?: "local" | "reload";
|
|
47
49
|
filterPlaceholder?: string;
|
|
48
50
|
onSelectedItem?: (props: GridPopoutEditDropDownSelectedItem<RowType, ValueType>) => Promise<void>;
|
|
@@ -53,11 +55,15 @@ export interface GridFormPopoutDropDownProps<RowType extends GridBaseRow, ValueT
|
|
|
53
55
|
optionsRequestCancel?: () => void;
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
const fieldToString = (field: any) => {
|
|
59
|
+
return typeof field == "symbol" ? field.toString() : `${field}`;
|
|
60
|
+
};
|
|
61
|
+
|
|
56
62
|
export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
57
|
-
|
|
63
|
+
props: GridFormPopoutDropDownProps<RowType, ValueType>,
|
|
58
64
|
) => {
|
|
59
|
-
const
|
|
60
|
-
const {
|
|
65
|
+
const { selectedRows, field, updateValue } = useGridPopoverContext<RowType>();
|
|
66
|
+
const { stopEditing } = useContext(GridContext);
|
|
61
67
|
|
|
62
68
|
const [filter, setFilter] = useState("");
|
|
63
69
|
const [filteredValues, setFilteredValues] = useState<any[]>([]);
|
|
@@ -66,9 +72,8 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
66
72
|
const [subComponentValues, setSubComponentValues] = useState<{ optionValue: any; subComponentValue: any }[]>([]);
|
|
67
73
|
|
|
68
74
|
const selectItemHandler = useCallback(
|
|
69
|
-
async (value: ValueType, subComponentValue?: ValueType): Promise<boolean> =>
|
|
70
|
-
|
|
71
|
-
return await updatingCells({ selectedRows: props.selectedRows, field }, async (selectedRows) => {
|
|
75
|
+
async (value: ValueType, subComponentValue?: ValueType): Promise<boolean> =>
|
|
76
|
+
updateValue(async (selectedRows) => {
|
|
72
77
|
const hasChanged = selectedRows.some((row) => row[field as keyof RowType] !== value);
|
|
73
78
|
if (hasChanged) {
|
|
74
79
|
if (props.onSelectedItem) {
|
|
@@ -78,22 +83,17 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
return true;
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
[props, updatingCells],
|
|
86
|
+
}),
|
|
87
|
+
[field, props, updateValue],
|
|
84
88
|
);
|
|
85
89
|
|
|
86
90
|
const selectFilterHandler = useCallback(
|
|
87
|
-
async (value: string)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (props.onSelectFilter) {
|
|
91
|
-
await props.onSelectFilter({ selectedRows, value });
|
|
92
|
-
}
|
|
91
|
+
async (value: string) =>
|
|
92
|
+
updateValue(async (selectedRows) => {
|
|
93
|
+
props.onSelectFilter && (await props.onSelectFilter({ selectedRows, value }));
|
|
93
94
|
return true;
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
[props, updatingCells],
|
|
95
|
+
}),
|
|
96
|
+
[props, updateValue],
|
|
97
97
|
);
|
|
98
98
|
|
|
99
99
|
// Load up options list if it's async function
|
|
@@ -104,7 +104,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
104
104
|
|
|
105
105
|
(async () => {
|
|
106
106
|
if (typeof optionsConf == "function") {
|
|
107
|
-
optionsConf = await optionsConf(
|
|
107
|
+
optionsConf = await optionsConf(selectedRows, filter);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
const optionsList = optionsConf?.map((item) => {
|
|
@@ -127,7 +127,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
127
127
|
}
|
|
128
128
|
optionsInitialising.current = false;
|
|
129
129
|
})();
|
|
130
|
-
}, [filter, options, props]);
|
|
130
|
+
}, [filter, options, props, selectedRows]);
|
|
131
131
|
|
|
132
132
|
// Local filtering.
|
|
133
133
|
useEffect(() => {
|
|
@@ -214,7 +214,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
214
214
|
<ComponentLoadingWrapper loading={!options} className={"GridFormDropDown-options"}>
|
|
215
215
|
<>
|
|
216
216
|
{options && options.length == filteredValues?.length && (
|
|
217
|
-
<MenuItem key={`${
|
|
217
|
+
<MenuItem key={`${fieldToString(field)}-empty`}>[Empty]</MenuItem>
|
|
218
218
|
)}
|
|
219
219
|
{options?.map((item: FinalSelectOption<ValueType | string>, index) =>
|
|
220
220
|
item.value === MenuSeparatorString ? (
|
|
@@ -225,23 +225,22 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
225
225
|
<div key={`menu-wrapper-${index}`}>
|
|
226
226
|
{!item.subComponent ? (
|
|
227
227
|
<MenuItem
|
|
228
|
-
key={`${
|
|
228
|
+
key={`${fieldToString(field)}-${index}`}
|
|
229
229
|
disabled={!!item.disabled}
|
|
230
230
|
title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
|
|
231
231
|
value={item.value}
|
|
232
232
|
onClick={() => {
|
|
233
|
-
selectItemHandler(item.value as ValueType);
|
|
233
|
+
selectItemHandler(item.value as ValueType).then();
|
|
234
234
|
}}
|
|
235
235
|
>
|
|
236
236
|
{item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)}
|
|
237
237
|
</MenuItem>
|
|
238
238
|
) : (
|
|
239
|
-
<FocusableItem className={"LuiDeprecatedForms"} key={`${
|
|
239
|
+
<FocusableItem className={"LuiDeprecatedForms"} key={`${fieldToString(field)}-${index}_subcomponent`}>
|
|
240
240
|
{(ref: any) =>
|
|
241
|
-
item.subComponent &&
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
setValue: (value: any) => {
|
|
241
|
+
item.subComponent && (
|
|
242
|
+
<item.subComponent
|
|
243
|
+
setValue={(value: any) => {
|
|
245
244
|
const localSubComponentValues = [...subComponentValues];
|
|
246
245
|
const subComponentValueIndex = localSubComponentValues.findIndex(
|
|
247
246
|
({ optionValue }) => optionValue === item.value,
|
|
@@ -255,8 +254,8 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
255
254
|
});
|
|
256
255
|
}
|
|
257
256
|
setSubComponentValues(localSubComponentValues);
|
|
258
|
-
}
|
|
259
|
-
keyDown
|
|
257
|
+
}}
|
|
258
|
+
keyDown={(key: string, event: KeyboardEvent<HTMLInputElement>) => {
|
|
260
259
|
const subComponentItem = subComponentValues.find(
|
|
261
260
|
({ optionValue }) => optionValue === item.value,
|
|
262
261
|
);
|
|
@@ -272,10 +271,9 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(
|
|
|
272
271
|
});
|
|
273
272
|
}
|
|
274
273
|
return false;
|
|
275
|
-
}
|
|
276
|
-
key
|
|
277
|
-
|
|
278
|
-
ref,
|
|
274
|
+
}}
|
|
275
|
+
key={`${fieldToString(field)}-${index}_subcomponent_inner`}
|
|
276
|
+
/>
|
|
279
277
|
)
|
|
280
278
|
}
|
|
281
279
|
</FocusableItem>
|
|
@@ -5,7 +5,8 @@ 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 { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
9
10
|
|
|
10
11
|
export interface GridFormEditBearingProps<RowType extends GridBaseRow> extends CellEditorCommon {
|
|
11
12
|
placeHolder?: string;
|
|
@@ -13,31 +14,34 @@ 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 } = useGridPopoverContext<RowType>();
|
|
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 {
|
|
35
|
-
selectedRows.forEach((row) =>
|
|
37
|
+
selectedRows.forEach((row) => {
|
|
38
|
+
row[field] = parsedValue as any;
|
|
39
|
+
});
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
return true;
|
|
39
43
|
},
|
|
40
|
-
[props, value],
|
|
44
|
+
[field, initialValue, props, value],
|
|
41
45
|
);
|
|
42
46
|
const { triggerSave, popoverWrapper } = useGridPopoverHook({ className: props.className, save });
|
|
43
47
|
|
|
@@ -3,22 +3,24 @@ import { 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 { useGridPopoverContext } 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 } = useGridPopoverContext<RowType>();
|
|
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, useEffect, useRef, useState
|
|
3
|
+
import { FocusableItem, MenuDivider, MenuItem } from "../../react-menu3";
|
|
4
|
+
import { KeyboardEvent, useCallback, 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 { useGridPopoverContext } 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 } = useGridPopoverContext<RowType>();
|
|
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;
|