@linzjs/step-ag-grid 7.11.7 → 7.11.9
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 +107 -159
- package/dist/index.js.map +1 -1
- package/dist/step-ag-grid.esm.js +108 -160
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Grid.tsx +2 -2
- package/src/components/gridForm/GridFormDropDown.tsx +100 -118
- package/src/components/gridHeader/GridHeaderSelect.tsx +3 -3
- package/src/contexts/GridContextProvider.tsx +240 -195
- package/src/react-menu3/components/ControlledMenu.tsx +2 -2
package/package.json
CHANGED
package/src/components/Grid.tsx
CHANGED
|
@@ -186,9 +186,9 @@ export const Grid = (params: GridProps): JSX.Element => {
|
|
|
186
186
|
} else {
|
|
187
187
|
e.api.deselectAll();
|
|
188
188
|
}
|
|
189
|
-
return
|
|
189
|
+
return true;
|
|
190
190
|
}
|
|
191
|
-
return
|
|
191
|
+
return false;
|
|
192
192
|
},
|
|
193
193
|
onCellClicked: clickSelectorCheckboxWhenContainingCellClicked,
|
|
194
194
|
},
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { FocusableItem, MenuDivider, MenuHeader, MenuItem } from "../../react-menu3";
|
|
2
|
-
import {
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { GridBaseRow } from "../Grid";
|
|
4
4
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
5
|
-
import { delay } from "lodash-es";
|
|
5
|
+
import { delay, isEmpty } from "lodash-es";
|
|
6
6
|
import debounce from "debounce-promise";
|
|
7
7
|
import { CellEditorCommon } from "../GridCell";
|
|
8
8
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
9
9
|
import { useGridPopoverContext } from "../../contexts/GridPopoverContext";
|
|
10
10
|
import { GridSubComponentContext } from "contexts/GridSubComponentContext";
|
|
11
11
|
import { ClickEvent, MenuInstance } from "../../react-menu3/types";
|
|
12
|
-
import { CloseReason } from "../../react-menu3/utils";
|
|
13
|
-
import { GridContext } from "../../contexts/GridContext";
|
|
14
12
|
import { FormError } from "../../lui/FormError";
|
|
15
13
|
import { isNotEmpty } from "../../utils/util";
|
|
16
14
|
|
|
@@ -62,8 +60,7 @@ const fieldToString = (field: any) => {
|
|
|
62
60
|
};
|
|
63
61
|
|
|
64
62
|
export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormPopoutDropDownProps<RowType>) => {
|
|
65
|
-
const {
|
|
66
|
-
const { selectedRows, field, updateValue, data } = useGridPopoverContext<RowType>();
|
|
63
|
+
const { selectedRows, field, data } = useGridPopoverContext<RowType>();
|
|
67
64
|
|
|
68
65
|
// Save triggers during async action processing which triggers another selectItem(), this ref blocks that
|
|
69
66
|
const [filter, setFilter] = useState("");
|
|
@@ -73,7 +70,8 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormPop
|
|
|
73
70
|
const subComponentIsValid = useRef(false);
|
|
74
71
|
const subComponentInitialValue = useRef<string | null>(null);
|
|
75
72
|
const [subSelectedValue, setSubSelectedValue] = useState<any>(null);
|
|
76
|
-
|
|
73
|
+
// Note: null is assumed to be the filter
|
|
74
|
+
const [selectedItem, setSelectedItem] = useState<FinalSelectOption | null>(null);
|
|
77
75
|
|
|
78
76
|
const selectItemHandler = useCallback(
|
|
79
77
|
async (value: any, subComponentValue?: any): Promise<boolean> => {
|
|
@@ -92,29 +90,6 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormPop
|
|
|
92
90
|
[field, props, selectedRows],
|
|
93
91
|
);
|
|
94
92
|
|
|
95
|
-
const clickItemHandler = useCallback(
|
|
96
|
-
async (value: any, subComponentValue?: any, reason?: string): Promise<boolean> => {
|
|
97
|
-
if (subComponentValue !== undefined && !subComponentIsValid.current) return false;
|
|
98
|
-
return updateValue(
|
|
99
|
-
async () => {
|
|
100
|
-
return await selectItemHandler(value, subComponentValue);
|
|
101
|
-
},
|
|
102
|
-
reason === CloseReason.TAB_FORWARD ? 1 : reason === CloseReason.TAB_BACKWARD ? -1 : 0,
|
|
103
|
-
);
|
|
104
|
-
},
|
|
105
|
-
[selectItemHandler, updateValue],
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
const selectFilterHandler = useCallback(
|
|
109
|
-
async (value: string) => {
|
|
110
|
-
await updateValue(async (selectedRows) => {
|
|
111
|
-
props.onSelectFilter && (await props.onSelectFilter({ selectedRows, value }));
|
|
112
|
-
return true;
|
|
113
|
-
}, 0);
|
|
114
|
-
},
|
|
115
|
-
[props, updateValue],
|
|
116
|
-
);
|
|
117
|
-
|
|
118
93
|
// Load up options list if it's async function
|
|
119
94
|
useEffect(() => {
|
|
120
95
|
if (options || optionsInitialising.current) return;
|
|
@@ -159,7 +134,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormPop
|
|
|
159
134
|
return undefined;
|
|
160
135
|
}
|
|
161
136
|
const str = (option.label as string) || "";
|
|
162
|
-
return str.toLowerCase().indexOf(filter.toLowerCase())
|
|
137
|
+
return str.toLowerCase().indexOf(filter.toLowerCase()) !== -1 ? option : undefined;
|
|
163
138
|
})
|
|
164
139
|
.filter((r) => r !== undefined),
|
|
165
140
|
);
|
|
@@ -190,65 +165,58 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormPop
|
|
|
190
165
|
const save = useCallback(async () => {
|
|
191
166
|
if (!options) return true;
|
|
192
167
|
|
|
193
|
-
//
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
168
|
+
// Filter saved
|
|
169
|
+
if (selectedItem === null) {
|
|
170
|
+
if (props.onSelectFilter) {
|
|
171
|
+
const { onSelectFilter } = props;
|
|
172
|
+
await onSelectFilter({ selectedRows, value: filter });
|
|
173
|
+
return true;
|
|
174
|
+
} else {
|
|
175
|
+
if (filteredValues.length === 1) {
|
|
176
|
+
if (filteredValues[0].subComponent) return false;
|
|
177
|
+
return await selectItemHandler(filteredValues[0].value, null);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
if (selectedItem.subComponent && !subComponentIsValid.current) return false;
|
|
183
|
+
await selectItemHandler(selectedItem.value, subSelectedValue);
|
|
197
184
|
|
|
198
185
|
return true;
|
|
199
|
-
}, [options, selectItemHandler,
|
|
186
|
+
}, [filter, filteredValues, options, props, selectItemHandler, selectedItem, selectedRows, subSelectedValue]);
|
|
200
187
|
|
|
201
188
|
const { popoverWrapper } = useGridPopoverHook({
|
|
202
189
|
className: props.className,
|
|
203
|
-
invalid: () => !!(
|
|
190
|
+
invalid: () => !!(selectedItem && !subComponentIsValid.current),
|
|
204
191
|
save,
|
|
205
192
|
});
|
|
206
193
|
|
|
207
|
-
const enterKeyPressedRef = useRef(false);
|
|
208
|
-
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
|
209
|
-
if (e.key === "Enter") {
|
|
210
|
-
e.stopPropagation();
|
|
211
|
-
e.preventDefault();
|
|
212
|
-
enterKeyPressedRef.current = true;
|
|
213
|
-
}
|
|
214
|
-
}, []);
|
|
215
|
-
|
|
216
|
-
const handleKeyUp = useCallback(
|
|
217
|
-
async (e: KeyboardEvent) => {
|
|
218
|
-
if (!options) return;
|
|
219
|
-
|
|
220
|
-
if (e.key === "Enter") {
|
|
221
|
-
e.stopPropagation();
|
|
222
|
-
e.preventDefault();
|
|
223
|
-
if (!enterKeyPressedRef.current) return;
|
|
224
|
-
|
|
225
|
-
props.onSelectFilter && (await selectFilterHandler(filter));
|
|
226
|
-
stopEditing();
|
|
227
|
-
}
|
|
228
|
-
},
|
|
229
|
-
[filter, options, props.onSelectFilter, selectFilterHandler, stopEditing],
|
|
230
|
-
);
|
|
231
|
-
|
|
232
194
|
return popoverWrapper(
|
|
233
195
|
<>
|
|
234
196
|
{props.filtered && (
|
|
235
197
|
<div className={"GridFormDropDown-filter"}>
|
|
236
|
-
<FocusableItem
|
|
198
|
+
<FocusableItem
|
|
199
|
+
className={"filter-item"}
|
|
200
|
+
onFocus={() => {
|
|
201
|
+
setSelectedItem(null);
|
|
202
|
+
setSubSelectedValue(null);
|
|
203
|
+
subComponentIsValid.current = true;
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
237
206
|
{({ ref }: any) => (
|
|
238
207
|
<div style={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
|
239
208
|
<input
|
|
240
|
-
autoFocus
|
|
241
209
|
className={"LuiTextInput-input"}
|
|
242
210
|
ref={ref}
|
|
243
211
|
type="text"
|
|
244
212
|
placeholder={props.filterPlaceholder ?? "Placeholder"}
|
|
245
213
|
data-testid={"filteredMenu-free-text-input"}
|
|
246
214
|
defaultValue={filter}
|
|
247
|
-
data-disableenterautosave={true}
|
|
248
215
|
data-allowtabtosave={true}
|
|
216
|
+
data-disableenterautosave={
|
|
217
|
+
!props.onSelectFilter && !(filteredValues.length === 1 && !filteredValues[0].subComponent)
|
|
218
|
+
}
|
|
249
219
|
onChange={(e) => setFilter(e.target.value)}
|
|
250
|
-
onKeyDown={handleKeyDown}
|
|
251
|
-
onKeyUp={handleKeyUp}
|
|
252
220
|
/>
|
|
253
221
|
{props.filterHelpText && isNotEmpty(filter) && (
|
|
254
222
|
<FormError error={null} helpText={props.filterHelpText} />
|
|
@@ -261,7 +229,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormPop
|
|
|
261
229
|
)}
|
|
262
230
|
<ComponentLoadingWrapper loading={!options} className={"GridFormDropDown-options"}>
|
|
263
231
|
<>
|
|
264
|
-
{options &&
|
|
232
|
+
{options && isEmpty(filteredValues) && (
|
|
265
233
|
<MenuItem key={`${fieldToString(field)}-empty`} className={"GridPopoverEditDropDown-noOptions"}>
|
|
266
234
|
No Options
|
|
267
235
|
</MenuItem>
|
|
@@ -271,19 +239,33 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormPop
|
|
|
271
239
|
<MenuDivider key={`$$divider_${index}`} />
|
|
272
240
|
) : item.value === MenuHeaderString ? (
|
|
273
241
|
<MenuHeader key={`$$header_${index}`}>{item.label}</MenuHeader>
|
|
274
|
-
) :
|
|
275
|
-
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
242
|
+
) : (
|
|
243
|
+
filteredValues.includes(item) && (
|
|
244
|
+
<div key={`menu-wrapper-${index}`}>
|
|
245
|
+
<MenuItem
|
|
246
|
+
key={`${fieldToString(field)}-${index}`}
|
|
247
|
+
disabled={!!item.disabled}
|
|
248
|
+
title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
|
|
249
|
+
value={item.value}
|
|
250
|
+
onFocus={() => {
|
|
251
|
+
setSelectedItem(item);
|
|
252
|
+
if (item.subComponent) {
|
|
253
|
+
setSelectedItem(item);
|
|
254
|
+
subComponentIsValid.current = true;
|
|
255
|
+
subComponentInitialValue.current = null;
|
|
256
|
+
} else {
|
|
257
|
+
setSubSelectedValue(null);
|
|
258
|
+
subComponentIsValid.current = true;
|
|
259
|
+
}
|
|
260
|
+
}}
|
|
261
|
+
onClick={(e: ClickEvent) => {
|
|
262
|
+
if (item.subComponent) {
|
|
263
|
+
e.keepOpen = true;
|
|
264
|
+
}
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
267
|
+
{/*onClick={(e: ClickEvent) => {
|
|
282
268
|
if (item.subComponent) {
|
|
283
|
-
// toggle selection
|
|
284
|
-
setSelectedSubComponent(selectedSubComponent === item ? null : item);
|
|
285
|
-
subComponentIsValid.current = true;
|
|
286
|
-
subComponentInitialValue.current = null;
|
|
287
269
|
e.keepOpen = true;
|
|
288
270
|
} else {
|
|
289
271
|
clickItemHandler(
|
|
@@ -296,45 +278,45 @@ export const GridFormDropDown = <RowType extends GridBaseRow>(props: GridFormPop
|
|
|
296
278
|
: CloseReason.CLICK,
|
|
297
279
|
).then();
|
|
298
280
|
}
|
|
299
|
-
}}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
</MenuItem>
|
|
281
|
+
}*/}
|
|
282
|
+
{item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)}
|
|
283
|
+
{item.subComponent ? "..." : ""}
|
|
284
|
+
</MenuItem>
|
|
304
285
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
286
|
+
{item.subComponent && selectedItem === item && (
|
|
287
|
+
<FocusableItem className={"LuiDeprecatedForms"} key={`${item.label}_subcomponent`}>
|
|
288
|
+
{(ref: MenuInstance) => (
|
|
289
|
+
<GridSubComponentContext.Provider
|
|
290
|
+
value={{
|
|
291
|
+
context: { options },
|
|
292
|
+
data,
|
|
293
|
+
value: subSelectedValue,
|
|
294
|
+
setValue: (value: any) => {
|
|
295
|
+
setSubSelectedValue(value);
|
|
296
|
+
if (subComponentInitialValue.current === null) {
|
|
297
|
+
// copy the default value of the sub-component so we can change detect on save
|
|
298
|
+
subComponentInitialValue.current = JSON.stringify(value);
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
setValid: (valid: boolean) => {
|
|
302
|
+
subComponentIsValid.current = valid;
|
|
303
|
+
},
|
|
304
|
+
triggerSave: async () => {
|
|
305
|
+
ref.closeMenu();
|
|
306
|
+
},
|
|
307
|
+
}}
|
|
308
|
+
>
|
|
309
|
+
{item.subComponent && (
|
|
310
|
+
<div className={"subComponent"}>
|
|
311
|
+
<item.subComponent key={`${fieldToString(field)}-${index}_subcomponent_inner`} />
|
|
312
|
+
</div>
|
|
313
|
+
)}
|
|
314
|
+
</GridSubComponentContext.Provider>
|
|
315
|
+
)}
|
|
316
|
+
</FocusableItem>
|
|
317
|
+
)}
|
|
318
|
+
</div>
|
|
319
|
+
)
|
|
338
320
|
),
|
|
339
321
|
)}
|
|
340
322
|
</>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import clsx from "clsx";
|
|
2
2
|
import { IHeaderParams } from "ag-grid-community";
|
|
3
|
-
import { useEffect, useState } from "react";
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* AgGrid's existing select header doesn't work the way we want.
|
|
@@ -12,9 +12,9 @@ export const GridHeaderSelect = ({ api }: IHeaderParams) => {
|
|
|
12
12
|
const [updateCounter, setUpdateCounter] = useState(0);
|
|
13
13
|
const selectedNodeCount = api.getSelectedRows().length;
|
|
14
14
|
|
|
15
|
-
const clickHandler = () => {
|
|
15
|
+
const clickHandler = useCallback(() => {
|
|
16
16
|
setUpdateCounter(updateCounter + 1);
|
|
17
|
-
};
|
|
17
|
+
}, [updateCounter]);
|
|
18
18
|
|
|
19
19
|
useEffect(() => {
|
|
20
20
|
api.addEventListener("selectionChanged", clickHandler);
|