@etsoo/materialui 1.2.58 → 1.2.59
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/lib/ComboBox.d.ts +8 -0
- package/lib/ComboBox.js +26 -15
- package/package.json +2 -2
- package/src/ComboBox.tsx +89 -53
package/lib/ComboBox.d.ts
CHANGED
|
@@ -29,6 +29,14 @@ export type ComboBoxProps<T extends object = ListType, D extends DataTypes.Keys<
|
|
|
29
29
|
* Array of options.
|
|
30
30
|
*/
|
|
31
31
|
options?: ReadonlyArray<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Add label
|
|
34
|
+
*/
|
|
35
|
+
addLabel?: string;
|
|
36
|
+
/**
|
|
37
|
+
* On add callback
|
|
38
|
+
*/
|
|
39
|
+
onAdd?: (callback: () => void) => void;
|
|
32
40
|
};
|
|
33
41
|
/**
|
|
34
42
|
* ComboBox
|
package/lib/ComboBox.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Keyboard } from "@etsoo/shared";
|
|
2
|
-
import { Autocomplete } from "@mui/material";
|
|
2
|
+
import { Autocomplete, IconButton, Stack } from "@mui/material";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { Utils as SharedUtils } from "@etsoo/shared";
|
|
5
5
|
import { ReactUtils } from "@etsoo/react";
|
|
6
|
+
import AddIcon from "@mui/icons-material/Add";
|
|
6
7
|
import { SearchField } from "./SearchField";
|
|
7
8
|
import { InputField } from "./InputField";
|
|
8
9
|
import { globalApp } from "./app/ReactApp";
|
|
@@ -13,9 +14,9 @@ import { globalApp } from "./app/ReactApp";
|
|
|
13
14
|
*/
|
|
14
15
|
export function ComboBox(props) {
|
|
15
16
|
// Labels
|
|
16
|
-
const labels = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("noOptions", "loading", "open");
|
|
17
|
+
const labels = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("noOptions", "loading", "open", "add");
|
|
17
18
|
// Destruct
|
|
18
|
-
const { search = false, autoAddBlankItem = search, idField = "id", idValue, inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, defaultValue, label, labelField = "label", loadData, onLoadData, name, inputAutoComplete = "off", options, dataReadonly = true, readOnly, onChange, onValueChange, openOnFocus = true, value, disableCloseOnSelect = false, getOptionLabel = (option) => `${option[labelField]}`, sx = { minWidth: "150px" }, noOptionsText = labels === null || labels === void 0 ? void 0 : labels.noOptions, loadingText = labels === null || labels === void 0 ? void 0 : labels.loading, openText = labels === null || labels === void 0 ? void 0 : labels.open, ...rest } = props;
|
|
19
|
+
const { search = false, autoAddBlankItem = search, idField = "id", idValue, inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, defaultValue, label, labelField = "label", loadData, onLoadData, name, inputAutoComplete = "off", options, dataReadonly = true, readOnly, onChange, onValueChange, openOnFocus = true, value, disableCloseOnSelect = false, getOptionLabel = (option) => `${option[labelField]}`, sx = { minWidth: "150px", flexGrow: 2 }, noOptionsText = labels === null || labels === void 0 ? void 0 : labels.noOptions, loadingText = labels === null || labels === void 0 ? void 0 : labels.loading, openText = labels === null || labels === void 0 ? void 0 : labels.open, addLabel = labels === null || labels === void 0 ? void 0 : labels.add, onAdd, ...rest } = props;
|
|
19
20
|
// Value input ref
|
|
20
21
|
const inputRef = React.createRef();
|
|
21
22
|
// Options state
|
|
@@ -88,8 +89,8 @@ export function ComboBox(props) {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
};
|
|
91
|
-
React.
|
|
92
|
-
if (
|
|
92
|
+
const doLoadData = React.useCallback(() => {
|
|
93
|
+
if (loadData == null)
|
|
93
94
|
return;
|
|
94
95
|
loadData().then((result) => {
|
|
95
96
|
if (result == null || !isMounted.current)
|
|
@@ -101,7 +102,12 @@ export function ComboBox(props) {
|
|
|
101
102
|
}
|
|
102
103
|
setOptions(result);
|
|
103
104
|
});
|
|
104
|
-
}, [
|
|
105
|
+
}, [loadData, autoAddBlankItem, idField, labelField]);
|
|
106
|
+
React.useEffect(() => {
|
|
107
|
+
if (propertyWay)
|
|
108
|
+
return;
|
|
109
|
+
doLoadData();
|
|
110
|
+
}, [propertyWay, doLoadData]);
|
|
105
111
|
React.useEffect(() => {
|
|
106
112
|
return () => {
|
|
107
113
|
isMounted.current = false;
|
|
@@ -110,13 +116,18 @@ export function ComboBox(props) {
|
|
|
110
116
|
// Layout
|
|
111
117
|
return (React.createElement("div", null,
|
|
112
118
|
React.createElement("input", { ref: inputRef, "data-reset": "true", type: "text", style: { display: "none" }, name: name, value: getValue(stateValue), readOnly: true, onChange: inputOnChange }),
|
|
113
|
-
React.createElement(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
onChange
|
|
119
|
-
|
|
120
|
-
onValueChange
|
|
121
|
-
|
|
119
|
+
React.createElement(Stack, { gap: 0.5, direction: "row", width: "100%" },
|
|
120
|
+
React.createElement(Autocomplete, { value: stateValue, disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => {
|
|
121
|
+
// Set value
|
|
122
|
+
setInputValue(value);
|
|
123
|
+
// Custom
|
|
124
|
+
if (onChange != null)
|
|
125
|
+
onChange(event, value, reason, details);
|
|
126
|
+
if (onValueChange)
|
|
127
|
+
onValueChange(value);
|
|
128
|
+
}, openOnFocus: openOnFocus, sx: sx, renderInput: (params) => search ? (React.createElement(SearchField, { ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, error: inputError, helperText: inputHelperText })) : (React.createElement(InputField, { ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, error: inputError, helperText: inputHelperText })), options: localOptions, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, ...rest }),
|
|
129
|
+
onAdd && (React.createElement(IconButton, { size: "small", onClick: () => {
|
|
130
|
+
onAdd(doLoadData);
|
|
131
|
+
}, title: addLabel },
|
|
132
|
+
React.createElement(AddIcon, null))))));
|
|
122
133
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.59",
|
|
4
4
|
"description": "TypeScript Material-UI Implementation",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@emotion/styled": "^11.11.0",
|
|
53
53
|
"@etsoo/appscript": "^1.4.19",
|
|
54
54
|
"@etsoo/notificationbase": "^1.1.25",
|
|
55
|
-
"@etsoo/react": "^1.6.
|
|
55
|
+
"@etsoo/react": "^1.6.93",
|
|
56
56
|
"@etsoo/shared": "^1.2.5",
|
|
57
57
|
"@mui/icons-material": "^5.11.16",
|
|
58
58
|
"@mui/material": "^5.13.5",
|
package/src/ComboBox.tsx
CHANGED
|
@@ -5,10 +5,16 @@ import {
|
|
|
5
5
|
LabelDefaultType,
|
|
6
6
|
ListType
|
|
7
7
|
} from "@etsoo/shared";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Autocomplete,
|
|
10
|
+
AutocompleteRenderInputParams,
|
|
11
|
+
IconButton,
|
|
12
|
+
Stack
|
|
13
|
+
} from "@mui/material";
|
|
9
14
|
import React from "react";
|
|
10
15
|
import { Utils as SharedUtils } from "@etsoo/shared";
|
|
11
16
|
import { ReactUtils } from "@etsoo/react";
|
|
17
|
+
import AddIcon from "@mui/icons-material/Add";
|
|
12
18
|
import { AutocompleteExtendedProps } from "./AutocompleteExtendedProps";
|
|
13
19
|
import { SearchField } from "./SearchField";
|
|
14
20
|
import { InputField } from "./InputField";
|
|
@@ -51,6 +57,16 @@ export type ComboBoxProps<
|
|
|
51
57
|
* Array of options.
|
|
52
58
|
*/
|
|
53
59
|
options?: ReadonlyArray<T>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Add label
|
|
63
|
+
*/
|
|
64
|
+
addLabel?: string;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* On add callback
|
|
68
|
+
*/
|
|
69
|
+
onAdd?: (callback: () => void) => void;
|
|
54
70
|
};
|
|
55
71
|
|
|
56
72
|
/**
|
|
@@ -64,7 +80,7 @@ export function ComboBox<
|
|
|
64
80
|
L extends DataTypes.Keys<T, string> = LabelDefaultType<T>
|
|
65
81
|
>(props: ComboBoxProps<T, D, L>) {
|
|
66
82
|
// Labels
|
|
67
|
-
const labels = globalApp?.getLabels("noOptions", "loading", "open");
|
|
83
|
+
const labels = globalApp?.getLabels("noOptions", "loading", "open", "add");
|
|
68
84
|
|
|
69
85
|
// Destruct
|
|
70
86
|
const {
|
|
@@ -94,10 +110,12 @@ export function ComboBox<
|
|
|
94
110
|
value,
|
|
95
111
|
disableCloseOnSelect = false,
|
|
96
112
|
getOptionLabel = (option: T) => `${option[labelField]}`,
|
|
97
|
-
sx = { minWidth: "150px" },
|
|
113
|
+
sx = { minWidth: "150px", flexGrow: 2 },
|
|
98
114
|
noOptionsText = labels?.noOptions,
|
|
99
115
|
loadingText = labels?.loading,
|
|
100
116
|
openText = labels?.open,
|
|
117
|
+
addLabel = labels?.add,
|
|
118
|
+
onAdd,
|
|
101
119
|
...rest
|
|
102
120
|
} = props;
|
|
103
121
|
|
|
@@ -189,8 +207,8 @@ export function ComboBox<
|
|
|
189
207
|
}
|
|
190
208
|
};
|
|
191
209
|
|
|
192
|
-
React.
|
|
193
|
-
if (
|
|
210
|
+
const doLoadData = React.useCallback(() => {
|
|
211
|
+
if (loadData == null) return;
|
|
194
212
|
loadData().then((result) => {
|
|
195
213
|
if (result == null || !isMounted.current) return;
|
|
196
214
|
if (onLoadData) onLoadData(result);
|
|
@@ -199,7 +217,12 @@ export function ComboBox<
|
|
|
199
217
|
}
|
|
200
218
|
setOptions(result);
|
|
201
219
|
});
|
|
202
|
-
}, [
|
|
220
|
+
}, [loadData, autoAddBlankItem, idField, labelField]);
|
|
221
|
+
|
|
222
|
+
React.useEffect(() => {
|
|
223
|
+
if (propertyWay) return;
|
|
224
|
+
doLoadData();
|
|
225
|
+
}, [propertyWay, doLoadData]);
|
|
203
226
|
|
|
204
227
|
React.useEffect(() => {
|
|
205
228
|
return () => {
|
|
@@ -221,55 +244,68 @@ export function ComboBox<
|
|
|
221
244
|
onChange={inputOnChange}
|
|
222
245
|
/>
|
|
223
246
|
{/* Previous input will reset first with "disableClearable = false", next input trigger change works */}
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
option
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
247
|
+
<Stack gap={0.5} direction="row" width="100%">
|
|
248
|
+
<Autocomplete<T, false, false, false>
|
|
249
|
+
value={stateValue}
|
|
250
|
+
disableCloseOnSelect={disableCloseOnSelect}
|
|
251
|
+
getOptionLabel={getOptionLabel}
|
|
252
|
+
isOptionEqualToValue={(option: T, value: T) =>
|
|
253
|
+
option[idField] === value[idField]
|
|
254
|
+
}
|
|
255
|
+
onChange={(event, value, reason, details) => {
|
|
256
|
+
// Set value
|
|
257
|
+
setInputValue(value);
|
|
234
258
|
|
|
235
|
-
|
|
236
|
-
|
|
259
|
+
// Custom
|
|
260
|
+
if (onChange != null) onChange(event, value, reason, details);
|
|
237
261
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
262
|
+
if (onValueChange) onValueChange(value);
|
|
263
|
+
}}
|
|
264
|
+
openOnFocus={openOnFocus}
|
|
265
|
+
sx={sx}
|
|
266
|
+
renderInput={(params) =>
|
|
267
|
+
search ? (
|
|
268
|
+
<SearchField
|
|
269
|
+
{...addReadOnly(params)}
|
|
270
|
+
label={label}
|
|
271
|
+
name={name + "Input"}
|
|
272
|
+
margin={inputMargin}
|
|
273
|
+
variant={inputVariant}
|
|
274
|
+
required={inputRequired}
|
|
275
|
+
error={inputError}
|
|
276
|
+
helperText={inputHelperText}
|
|
277
|
+
/>
|
|
278
|
+
) : (
|
|
279
|
+
<InputField
|
|
280
|
+
{...addReadOnly(params)}
|
|
281
|
+
label={label}
|
|
282
|
+
name={name + "Input"}
|
|
283
|
+
margin={inputMargin}
|
|
284
|
+
variant={inputVariant}
|
|
285
|
+
required={inputRequired}
|
|
286
|
+
error={inputError}
|
|
287
|
+
helperText={inputHelperText}
|
|
288
|
+
/>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
options={localOptions}
|
|
292
|
+
noOptionsText={noOptionsText}
|
|
293
|
+
loadingText={loadingText}
|
|
294
|
+
openText={openText}
|
|
295
|
+
{...rest}
|
|
296
|
+
/>
|
|
297
|
+
{onAdd && (
|
|
298
|
+
<IconButton
|
|
299
|
+
size="small"
|
|
300
|
+
onClick={() => {
|
|
301
|
+
onAdd(doLoadData);
|
|
302
|
+
}}
|
|
303
|
+
title={addLabel}
|
|
304
|
+
>
|
|
305
|
+
<AddIcon />
|
|
306
|
+
</IconButton>
|
|
307
|
+
)}
|
|
308
|
+
</Stack>
|
|
273
309
|
</div>
|
|
274
310
|
);
|
|
275
311
|
}
|