@linzjs/step-ag-grid 1.5.2 → 1.5.4
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.d.ts +8 -8
- package/dist/index.js +219 -200
- package/dist/index.js.map +1 -1
- package/dist/src/components/Grid.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +3 -7
- package/dist/src/components/gridForm/GridFormPopoutMenu.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormTextInput.d.ts +2 -1
- package/dist/src/contexts/GridContext.d.ts +1 -0
- package/dist/src/lui/TextInputFormatted.d.ts +1 -2
- package/dist/src/stories/components/FormTest.d.ts +1 -0
- package/dist/step-ag-grid.esm.js +214 -201
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Grid.tsx +18 -13
- package/src/components/GridCell.tsx +4 -2
- package/src/components/gridForm/GridFormMultiSelect.tsx +8 -10
- package/src/components/gridForm/GridFormPopoutMenu.tsx +3 -3
- package/src/components/gridForm/GridFormTextInput.tsx +15 -11
- package/src/contexts/GridContext.tsx +4 -0
- package/src/contexts/GridContextProvider.tsx +10 -0
- package/src/lui/TextInputFormatted.tsx +1 -3
- package/src/stories/components/FormTest.tsx +1 -0
- package/src/stories/components/GridPopoutEditGenericTextArea.stories.tsx +27 -3
package/package.json
CHANGED
package/src/components/Grid.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import "./GridTheme.scss";
|
|
|
4
4
|
import clsx from "clsx";
|
|
5
5
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
6
6
|
import { AgGridReact } from "ag-grid-react";
|
|
7
|
-
import {
|
|
7
|
+
import { CellClickedEvent, ColDef } from "ag-grid-community";
|
|
8
8
|
import { CellEvent, GridReadyEvent, SelectionChangedEvent } from "ag-grid-community/dist/lib/events";
|
|
9
9
|
import { GridOptions } from "ag-grid-community/dist/lib/entities/gridOptions";
|
|
10
10
|
import { difference, last, xorBy } from "lodash-es";
|
|
@@ -28,7 +28,7 @@ export interface GridProps {
|
|
|
28
28
|
externalSelectedItems?: any[];
|
|
29
29
|
setExternalSelectedItems?: (items: any[]) => void;
|
|
30
30
|
onGridReady?: GridOptions["onGridReady"];
|
|
31
|
-
defaultColDef
|
|
31
|
+
defaultColDef?: GridOptions["defaultColDef"];
|
|
32
32
|
columnDefs: GridOptions["columnDefs"];
|
|
33
33
|
rowData: GridOptions["rowData"];
|
|
34
34
|
noRowsOverlayText?: string;
|
|
@@ -38,8 +38,15 @@ export interface GridProps {
|
|
|
38
38
|
* Wrapper for AgGrid to add commonly used functionality.
|
|
39
39
|
*/
|
|
40
40
|
export const Grid = (params: GridProps): JSX.Element => {
|
|
41
|
-
const {
|
|
42
|
-
|
|
41
|
+
const {
|
|
42
|
+
gridReady,
|
|
43
|
+
setGridApi,
|
|
44
|
+
setQuickFilter,
|
|
45
|
+
ensureRowVisible,
|
|
46
|
+
selectRowsById,
|
|
47
|
+
ensureSelectedRowIsVisible,
|
|
48
|
+
sizeColumnsToFit,
|
|
49
|
+
} = useContext(GridContext);
|
|
43
50
|
const { checkUpdating } = useContext(UpdatingContext);
|
|
44
51
|
|
|
45
52
|
const [internalQuickFilter, setInternalQuickFilter] = useState("");
|
|
@@ -156,15 +163,6 @@ export const Grid = (params: GridProps): JSX.Element => {
|
|
|
156
163
|
[params.noRowsOverlayText],
|
|
157
164
|
);
|
|
158
165
|
|
|
159
|
-
const sizeColumnsToFit = useCallback((event: AgGridEvent) => {
|
|
160
|
-
// @ts-ignore (gridBodyCtrl is private)
|
|
161
|
-
if (event.api.gridBodyCtrl == undefined) {
|
|
162
|
-
return;
|
|
163
|
-
// this is here because ag grid was throwing api undefined error when closing POPPED OUT panel
|
|
164
|
-
}
|
|
165
|
-
event.api.sizeColumnsToFit();
|
|
166
|
-
}, []);
|
|
167
|
-
|
|
168
166
|
const refreshSelectedRows = useCallback((event: CellEvent): void => {
|
|
169
167
|
// Force-refresh all selected rows to re-run class function, to update selection highlighting
|
|
170
168
|
event.api.refreshCells({
|
|
@@ -227,6 +225,13 @@ export const Grid = (params: GridProps): JSX.Element => {
|
|
|
227
225
|
[refreshSelectedRows],
|
|
228
226
|
);
|
|
229
227
|
|
|
228
|
+
// When rows added or removed then resize columns
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
if (columnDefs?.length) {
|
|
231
|
+
sizeColumnsToFit();
|
|
232
|
+
}
|
|
233
|
+
}, [columnDefs?.length, sizeColumnsToFit]);
|
|
234
|
+
|
|
230
235
|
return (
|
|
231
236
|
<div
|
|
232
237
|
data-testid={params.dataTestId}
|
|
@@ -72,8 +72,9 @@ export const GridCell = <RowType extends GridBaseRow, FormProps extends GenericC
|
|
|
72
72
|
}),
|
|
73
73
|
// Default value formatter, otherwise react freaks out on objects
|
|
74
74
|
valueFormatter: (params: ValueFormatterParams) => {
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
if (params.value == null) return "-";
|
|
76
|
+
const types = ["number", "boolean", "string"];
|
|
77
|
+
if (types.includes(typeof params.value)) return `${params.value}`;
|
|
77
78
|
else return JSON.stringify(params.value);
|
|
78
79
|
},
|
|
79
80
|
...props,
|
|
@@ -130,6 +131,7 @@ export const GenericCellEditorComponentFr = <RowType extends GridBaseRow, FormPr
|
|
|
130
131
|
updateValue={updateValue}
|
|
131
132
|
saving={saving}
|
|
132
133
|
formProps={formProps}
|
|
134
|
+
data={data}
|
|
133
135
|
value={value}
|
|
134
136
|
field={field}
|
|
135
137
|
selectedRows={selectedRows}
|
|
@@ -8,17 +8,15 @@ import { delay, fromPairs } from "lodash-es";
|
|
|
8
8
|
import { LuiCheckboxInput } from "@linzjs/lui";
|
|
9
9
|
import { GenericCellEditorParams, GridFormProps } from "../GridCell";
|
|
10
10
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
11
|
+
import { MenuSeparatorString } from "@components/gridForm/GridFormDropDown";
|
|
11
12
|
|
|
12
|
-
interface
|
|
13
|
+
interface MultiFinalSelectOption<ValueType> {
|
|
13
14
|
value: ValueType;
|
|
14
15
|
label?: JSX.Element | string;
|
|
15
16
|
subComponent?: (props: any, ref: any) => any;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export
|
|
19
|
-
export const MenuSeparator = Object.freeze({ value: MenuSeparatorString });
|
|
20
|
-
|
|
21
|
-
export type SelectOption<ValueType> = ValueType | FinalSelectOption<ValueType>;
|
|
19
|
+
export type MultiSelectOption<ValueType> = ValueType | MultiFinalSelectOption<ValueType>;
|
|
22
20
|
|
|
23
21
|
export interface MultiSelectResult<RowType> {
|
|
24
22
|
selectedRows: RowType[];
|
|
@@ -31,8 +29,8 @@ export interface GridFormMultiSelectProps<RowType extends GridBaseRow, ValueType
|
|
|
31
29
|
filterPlaceholder?: string;
|
|
32
30
|
onSave?: (props: MultiSelectResult<RowType>) => Promise<boolean>;
|
|
33
31
|
options:
|
|
34
|
-
|
|
|
35
|
-
| ((selectedRows: RowType[]) => Promise<
|
|
32
|
+
| MultiSelectOption<ValueType>[]
|
|
33
|
+
| ((selectedRows: RowType[]) => Promise<MultiSelectOption<ValueType>[]> | MultiSelectOption<ValueType>[]);
|
|
36
34
|
initialSelectedValues?: (selectedRows: RowType[]) => any[];
|
|
37
35
|
}
|
|
38
36
|
|
|
@@ -42,7 +40,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
|
|
|
42
40
|
const [filter, setFilter] = useState("");
|
|
43
41
|
const [filteredValues, setFilteredValues] = useState<any[]>([]);
|
|
44
42
|
const optionsInitialising = useRef(false);
|
|
45
|
-
const [options, setOptions] = useState<
|
|
43
|
+
const [options, setOptions] = useState<MultiFinalSelectOption<ValueType>[]>();
|
|
46
44
|
const subSelectedValues = useRef<Record<string, any>>({});
|
|
47
45
|
const [selectedValues, setSelectedValues] = useState<any[]>(() =>
|
|
48
46
|
formProps.initialSelectedValues ? formProps.initialSelectedValues(props.selectedRows) : [],
|
|
@@ -75,10 +73,10 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
|
|
|
75
73
|
|
|
76
74
|
const optionsList = optionsConf?.map((item) => {
|
|
77
75
|
if (item == null || typeof item == "string" || typeof item == "number") {
|
|
78
|
-
item = { value: item as ValueType, label: item } as
|
|
76
|
+
item = { value: item as ValueType, label: item } as MultiFinalSelectOption<ValueType>;
|
|
79
77
|
}
|
|
80
78
|
return item;
|
|
81
|
-
}) as any as
|
|
79
|
+
}) as any as MultiFinalSelectOption<ValueType>[];
|
|
82
80
|
|
|
83
81
|
if (formProps.filtered) {
|
|
84
82
|
// This is needed otherwise when filter input is rendered and sets autofocus
|
|
@@ -11,7 +11,7 @@ export interface GridFormPopoutMenuProps<RowType extends GridBaseRow> extends Ge
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/** Menu configuration types **/
|
|
14
|
-
export const
|
|
14
|
+
export const PopoutMenuSeparator = Object.freeze({ __isMenuSeparator__: true });
|
|
15
15
|
|
|
16
16
|
interface MenuSeparatorType {
|
|
17
17
|
__isMenuSeparator__: boolean;
|
|
@@ -66,7 +66,7 @@ export const GridFormPopoutMenu = <RowType extends GridBaseRow>(props: GridFormP
|
|
|
66
66
|
const selectedRowCount = props.selectedRows.length;
|
|
67
67
|
|
|
68
68
|
const filteredOptions = options?.filter((menuOption) => {
|
|
69
|
-
return menuOption.label ===
|
|
69
|
+
return menuOption.label === PopoutMenuSeparator || selectedRowCount === 1 || menuOption.supportsMultiEdit;
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
const { popoverWrapper } = useGridPopoverHook(props);
|
|
@@ -74,7 +74,7 @@ export const GridFormPopoutMenu = <RowType extends GridBaseRow>(props: GridFormP
|
|
|
74
74
|
<ComponentLoadingWrapper loading={!filteredOptions}>
|
|
75
75
|
<div className={"Grid-popoverContainerList"}>
|
|
76
76
|
{options?.map((item, index) =>
|
|
77
|
-
item.label ===
|
|
77
|
+
item.label === PopoutMenuSeparator ? (
|
|
78
78
|
<MenuDivider key={`$$divider_${index}`} />
|
|
79
79
|
) : (
|
|
80
80
|
!item.hidden && (
|
|
@@ -6,26 +6,30 @@ import { GridBaseRow } from "../Grid";
|
|
|
6
6
|
|
|
7
7
|
export interface GridFormTextInputProps<RowType extends GridBaseRow> extends GenericCellEditorParams<RowType> {
|
|
8
8
|
placeholder?: string;
|
|
9
|
+
units?: string;
|
|
9
10
|
required?: boolean;
|
|
10
11
|
maxlength?: number;
|
|
11
12
|
width?: string | number;
|
|
12
|
-
|
|
13
|
+
// Return null for ok, otherwise an error string
|
|
14
|
+
validate?: (value: string, data: RowType) => string | null;
|
|
13
15
|
onSave?: (selectedRows: RowType[], value: string) => Promise<boolean>;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormProps<RowType>) => {
|
|
17
19
|
const formProps = props.formProps as GridFormTextInputProps<RowType>;
|
|
18
|
-
const
|
|
20
|
+
const initValue = props.value == null ? "" : `${props.value}`;
|
|
21
|
+
const [value, setValue] = useState(initValue);
|
|
19
22
|
|
|
20
23
|
const invalid = useCallback(() => {
|
|
21
|
-
|
|
24
|
+
const trimmedValue = value.trim();
|
|
25
|
+
if (formProps.required && trimmedValue.length == 0) {
|
|
22
26
|
return `Some text is required`;
|
|
23
27
|
}
|
|
24
|
-
if (formProps.maxlength &&
|
|
28
|
+
if (formProps.maxlength && trimmedValue.length > formProps.maxlength) {
|
|
25
29
|
return `Text must be no longer than ${formProps.maxlength} characters`;
|
|
26
30
|
}
|
|
27
31
|
if (formProps.validate) {
|
|
28
|
-
return formProps.validate(
|
|
32
|
+
return formProps.validate(trimmedValue, props.data);
|
|
29
33
|
}
|
|
30
34
|
return null;
|
|
31
35
|
}, [formProps, value]);
|
|
@@ -33,11 +37,11 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormPr
|
|
|
33
37
|
const save = useCallback(
|
|
34
38
|
async (selectedRows: any[]): Promise<boolean> => {
|
|
35
39
|
if (invalid()) return false;
|
|
36
|
-
|
|
37
|
-
if (
|
|
40
|
+
const trimmedValue = value.trim();
|
|
41
|
+
if (initValue === trimmedValue) return true;
|
|
38
42
|
|
|
39
43
|
if (formProps.onSave) {
|
|
40
|
-
return await formProps.onSave(selectedRows,
|
|
44
|
+
return await formProps.onSave(selectedRows, trimmedValue);
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
const field = props.field;
|
|
@@ -45,10 +49,10 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormPr
|
|
|
45
49
|
console.error("ColDef has no field set");
|
|
46
50
|
return false;
|
|
47
51
|
}
|
|
48
|
-
selectedRows.forEach((row) => (row[field] =
|
|
52
|
+
selectedRows.forEach((row) => (row[field] = trimmedValue));
|
|
49
53
|
return true;
|
|
50
54
|
},
|
|
51
|
-
[invalid,
|
|
55
|
+
[invalid, value, initValue, formProps, props.field],
|
|
52
56
|
);
|
|
53
57
|
const { popoverWrapper, triggerSave } = useGridPopoverHook(props, save);
|
|
54
58
|
|
|
@@ -58,7 +62,7 @@ export const GridFormTextInput = <RowType extends GridBaseRow>(props: GridFormPr
|
|
|
58
62
|
value={value}
|
|
59
63
|
onChange={(e) => setValue(e.target.value)}
|
|
60
64
|
error={invalid()}
|
|
61
|
-
formatted={
|
|
65
|
+
formatted={formProps.units}
|
|
62
66
|
inputProps={{
|
|
63
67
|
style: { width: "100%" },
|
|
64
68
|
placeholder: formProps.placeholder,
|
|
@@ -17,6 +17,7 @@ export interface GridContextType {
|
|
|
17
17
|
flashRowsDiff: (updateFn: () => Promise<any>) => Promise<void>;
|
|
18
18
|
ensureRowVisible: (id: number) => void;
|
|
19
19
|
ensureSelectedRowIsVisible: () => void;
|
|
20
|
+
sizeColumnsToFit: () => void;
|
|
20
21
|
stopEditing: () => void;
|
|
21
22
|
updatingCells: (
|
|
22
23
|
props: { selectedRows: GridBaseRow[]; field?: string },
|
|
@@ -68,6 +69,9 @@ export const GridContext = createContext<GridContextType>({
|
|
|
68
69
|
ensureSelectedRowIsVisible: () => {
|
|
69
70
|
console.error("no context provider for ensureSelectedRowIsVisible");
|
|
70
71
|
},
|
|
72
|
+
sizeColumnsToFit: () => {
|
|
73
|
+
console.error("no context provider for sizeColumnsToFit");
|
|
74
|
+
},
|
|
71
75
|
editingCells: () => {
|
|
72
76
|
console.error("no context provider for editingCells");
|
|
73
77
|
return false;
|
|
@@ -220,6 +220,15 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
|
|
|
220
220
|
});
|
|
221
221
|
};
|
|
222
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Resize columns to fit container
|
|
225
|
+
*/
|
|
226
|
+
const sizeColumnsToFit = (): void => {
|
|
227
|
+
gridApiOp((gridApi) => {
|
|
228
|
+
gridApi.sizeColumnsToFit();
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
|
|
223
232
|
const stopEditing = (): void => gridApiOp((gridApi) => gridApi.stopEditing());
|
|
224
233
|
|
|
225
234
|
const updatingCells = async (
|
|
@@ -283,6 +292,7 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
|
|
|
283
292
|
editingCells,
|
|
284
293
|
ensureRowVisible,
|
|
285
294
|
ensureSelectedRowIsVisible,
|
|
295
|
+
sizeColumnsToFit,
|
|
286
296
|
stopEditing,
|
|
287
297
|
updatingCells,
|
|
288
298
|
}}
|
|
@@ -14,9 +14,8 @@ export interface LuiTextInputProps {
|
|
|
14
14
|
className?: string;
|
|
15
15
|
value: string;
|
|
16
16
|
|
|
17
|
-
icon?: JSX.Element;
|
|
18
17
|
placeholder?: string;
|
|
19
|
-
formatted
|
|
18
|
+
formatted?: string;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
|
|
@@ -39,7 +38,6 @@ export const TextInputFormatted = (props: LuiTextInputProps): JSX.Element => {
|
|
|
39
38
|
{...props.inputProps}
|
|
40
39
|
/>
|
|
41
40
|
<span className={"LuiTextInput-formatted"}>{props.formatted}</span>
|
|
42
|
-
{props.icon}
|
|
43
41
|
</span>
|
|
44
42
|
|
|
45
43
|
{props.error && (
|
|
@@ -9,7 +9,7 @@ import { Grid, GridProps } from "@components/Grid";
|
|
|
9
9
|
import { useMemo, useState } from "react";
|
|
10
10
|
import { GridCell } from "@components/GridCell";
|
|
11
11
|
import { IFormTestRow } from "./FormTest";
|
|
12
|
-
import { wait } from "@utils/util";
|
|
12
|
+
import { isFloat, wait } from "@utils/util";
|
|
13
13
|
import { GridPopoverTextArea } from "@components/gridPopoverEdit/GridPopoverTextArea";
|
|
14
14
|
import { GridPopoverTextInput } from "@components/gridPopoverEdit/GridPopoverTextInput";
|
|
15
15
|
|
|
@@ -63,6 +63,30 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
|
|
|
63
63
|
},
|
|
64
64
|
},
|
|
65
65
|
}),
|
|
66
|
+
GridPopoverTextInput<IFormTestRow>({
|
|
67
|
+
field: "distance",
|
|
68
|
+
headerName: "Number input",
|
|
69
|
+
maxWidth: 140,
|
|
70
|
+
valueFormatter: (params) => {
|
|
71
|
+
const v = params.data.distance;
|
|
72
|
+
return v != null ? `${v}${params.colDef.cellEditorParams.units}` : v;
|
|
73
|
+
},
|
|
74
|
+
cellEditorParams: {
|
|
75
|
+
maxlength: 12,
|
|
76
|
+
placeholder: "Enter distance...",
|
|
77
|
+
units: "m",
|
|
78
|
+
validate: (value: string) => {
|
|
79
|
+
if (value.length && !isFloat(value)) return "Value must be a number";
|
|
80
|
+
return null;
|
|
81
|
+
},
|
|
82
|
+
multiEdit: false,
|
|
83
|
+
onSave: async (selectedRows, value) => {
|
|
84
|
+
await wait(1000);
|
|
85
|
+
selectedRows.forEach((selectedRow) => (selectedRow["distance"] = value.length ? parseFloat(value) : null));
|
|
86
|
+
return true;
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
66
90
|
GridPopoverTextArea<IFormTestRow>({
|
|
67
91
|
field: "plan",
|
|
68
92
|
headerName: "Text area",
|
|
@@ -90,8 +114,8 @@ const GridPopoutEditGenericTemplate: ComponentStory<typeof Grid> = (props: GridP
|
|
|
90
114
|
const rowData = useMemo(
|
|
91
115
|
() =>
|
|
92
116
|
[
|
|
93
|
-
{ id: 1000, name: "IS IS DP12345", nameType: "IS", numba: "IX", plan: "DP 12345" },
|
|
94
|
-
{ id: 1001, name: "PEG V SD523", nameType: "PEG", numba: "V", plan: "SD 523" },
|
|
117
|
+
{ id: 1000, name: "IS IS DP12345", nameType: "IS", numba: "IX", plan: "DP 12345", distance: 10 },
|
|
118
|
+
{ id: 1001, name: "PEG V SD523", nameType: "PEG", numba: "V", plan: "SD 523", distance: null },
|
|
95
119
|
] as IFormTestRow[],
|
|
96
120
|
[],
|
|
97
121
|
);
|