@etsoo/materialui 1.1.71 → 1.1.72
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/ComboBoxPro.d.ts +34 -0
- package/lib/ComboBoxPro.js +50 -0
- package/lib/SelectEx.d.ts +4 -4
- package/lib/SelectEx.js +24 -28
- package/lib/TagList.js +14 -3
- package/lib/TagListPro.d.ts +30 -0
- package/lib/TagListPro.js +59 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/package.json +4 -4
- package/src/ComboBoxPro.tsx +129 -0
- package/src/SelectEx.tsx +359 -382
- package/src/TagList.tsx +19 -2
- package/src/TagListPro.tsx +171 -0
- package/src/Tiplist.tsx +1 -1
- package/src/index.ts +2 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { AutocompleteProps } from "@mui/material";
|
|
3
|
+
import { InputFieldProps } from "./InputField";
|
|
4
|
+
type DataType = {
|
|
5
|
+
id: number | string;
|
|
6
|
+
} & ({
|
|
7
|
+
label: string;
|
|
8
|
+
} | {
|
|
9
|
+
name: string;
|
|
10
|
+
});
|
|
11
|
+
export type ComboBoxProProps<D extends DataType = DataType> = Omit<AutocompleteProps<D, false, false, true>, "open" | "multiple" | "options" | "renderInput"> & {
|
|
12
|
+
/**
|
|
13
|
+
* Label
|
|
14
|
+
*/
|
|
15
|
+
label?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Field name
|
|
18
|
+
*/
|
|
19
|
+
name?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Id value
|
|
22
|
+
*/
|
|
23
|
+
idValue?: D["id"] | null;
|
|
24
|
+
/**
|
|
25
|
+
* Options
|
|
26
|
+
*/
|
|
27
|
+
options: (() => PromiseLike<D[] | null | undefined>) | D[];
|
|
28
|
+
/**
|
|
29
|
+
* Input props
|
|
30
|
+
*/
|
|
31
|
+
inputProps?: Omit<InputFieldProps, "onChange">;
|
|
32
|
+
};
|
|
33
|
+
export declare function ComboBoxPro<D extends DataType = DataType>(props: ComboBoxProProps<D>): JSX.Element;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Autocomplete } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { globalApp } from "./app/ReactApp";
|
|
4
|
+
import { InputField } from "./InputField";
|
|
5
|
+
export function ComboBoxPro(props) {
|
|
6
|
+
var _a;
|
|
7
|
+
// Labels
|
|
8
|
+
const { noOptions, loading: loadingLabel, open: openDefault } = (_a = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("noOptions", "loading", "open")) !== null && _a !== void 0 ? _a : {};
|
|
9
|
+
const getLabel = (item) => "label" in item ? item.label : "name" in item ? item.name : "";
|
|
10
|
+
// Destruct
|
|
11
|
+
const { noOptionsText = noOptions, loadingText = loadingLabel, openText = openDefault, options, openOnFocus = true, label, inputProps, name, value, idValue, onChange, ...rest } = props;
|
|
12
|
+
const [open, setOpen] = React.useState(false);
|
|
13
|
+
const [localOptions, setOptions] = React.useState([]);
|
|
14
|
+
const [localValue, setValue] = React.useState(null);
|
|
15
|
+
const [loading, setLoading] = React.useState(false);
|
|
16
|
+
React.useEffect(() => {
|
|
17
|
+
if (value == null)
|
|
18
|
+
return;
|
|
19
|
+
setValue(value);
|
|
20
|
+
}, [value]);
|
|
21
|
+
React.useEffect(() => {
|
|
22
|
+
if (idValue == null)
|
|
23
|
+
return;
|
|
24
|
+
const option = localOptions.find((option) => option.id === idValue);
|
|
25
|
+
if (option)
|
|
26
|
+
setValue(option);
|
|
27
|
+
}, [localOptions]);
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
if (typeof options === "function") {
|
|
30
|
+
setLoading(true);
|
|
31
|
+
options().then((result) => {
|
|
32
|
+
setLoading(false);
|
|
33
|
+
if (result != null)
|
|
34
|
+
setOptions(result);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
setOptions(options);
|
|
39
|
+
}
|
|
40
|
+
}, [options]);
|
|
41
|
+
return (React.createElement(Autocomplete, { id: name, value: localValue, open: open, freeSolo: true, onOpen: () => {
|
|
42
|
+
setOpen(true);
|
|
43
|
+
}, onClose: () => {
|
|
44
|
+
setOpen(false);
|
|
45
|
+
}, options: localOptions, loading: loading, openOnFocus: openOnFocus, renderInput: (params) => (React.createElement(InputField, { ...inputProps, ...params, label: label, name: name })), getOptionLabel: (item) => typeof item === "object" ? getLabel(item) : item, isOptionEqualToValue: (option, value) => option.id === value.id, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, onChange: (event, value, reason, details) => {
|
|
46
|
+
setValue(value);
|
|
47
|
+
if (onChange)
|
|
48
|
+
onChange(event, value, reason, details);
|
|
49
|
+
}, ...rest }));
|
|
50
|
+
}
|
package/lib/SelectEx.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { SelectProps } from
|
|
2
|
-
import React from
|
|
3
|
-
import { DataTypes, IdDefaultType, LabelDefaultType, ListType } from
|
|
1
|
+
import { SelectProps } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { DataTypes, IdDefaultType, LabelDefaultType, ListType } from "@etsoo/shared";
|
|
4
4
|
/**
|
|
5
5
|
* Extended select component props
|
|
6
6
|
*/
|
|
7
|
-
export type SelectExProps<T extends object, D extends DataTypes.Keys<T> = IdDefaultType<T>, L extends DataTypes.Keys<T, string> = LabelDefaultType<T>> = Omit<SelectProps,
|
|
7
|
+
export type SelectExProps<T extends object, D extends DataTypes.Keys<T> = IdDefaultType<T>, L extends DataTypes.Keys<T, string> = LabelDefaultType<T>> = Omit<SelectProps, "labelId" | "input" | "native"> & {
|
|
8
8
|
/**
|
|
9
9
|
* Auto add blank item
|
|
10
10
|
*/
|
package/lib/SelectEx.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Checkbox, FormControl, FormHelperText, IconButton, InputLabel, ListItemText, MenuItem, OutlinedInput, Select, Stack } from
|
|
2
|
-
import React from
|
|
3
|
-
import { MUGlobal } from
|
|
4
|
-
import { ListItemRightIcon } from
|
|
5
|
-
import RefreshIcon from
|
|
6
|
-
import { Utils } from
|
|
7
|
-
import { ReactUtils } from
|
|
1
|
+
import { Checkbox, FormControl, FormHelperText, IconButton, InputLabel, ListItemText, MenuItem, OutlinedInput, Select, Stack } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { MUGlobal } from "./MUGlobal";
|
|
4
|
+
import { ListItemRightIcon } from "./ListItemRightIcon";
|
|
5
|
+
import RefreshIcon from "@mui/icons-material/Refresh";
|
|
6
|
+
import { ArrayUtils, Utils } from "@etsoo/shared";
|
|
7
|
+
import { ReactUtils } from "@etsoo/react";
|
|
8
8
|
/**
|
|
9
9
|
* Extended select component
|
|
10
10
|
* @param props Props
|
|
@@ -12,7 +12,7 @@ import { ReactUtils } from '@etsoo/react';
|
|
|
12
12
|
*/
|
|
13
13
|
export function SelectEx(props) {
|
|
14
14
|
// Destruct
|
|
15
|
-
const { defaultValue, idField =
|
|
15
|
+
const { defaultValue, idField = "id", error, helperText, inputRequired, itemIconRenderer, itemStyle, label, labelField = "label", loadData, onItemChange, onItemClick, onLoadData, multiple = false, name, options, refresh, search = false, autoAddBlankItem = search, value, onChange, fullWidth, ...rest } = props;
|
|
16
16
|
// Options state
|
|
17
17
|
const [localOptions, setOptions] = React.useState([]);
|
|
18
18
|
const isMounted = React.useRef(false);
|
|
@@ -23,7 +23,7 @@ export function SelectEx(props) {
|
|
|
23
23
|
if (multiple && Array.isArray(value)) {
|
|
24
24
|
option = options.find((option) => value.includes(option[idField]));
|
|
25
25
|
}
|
|
26
|
-
else if (value == null || value ===
|
|
26
|
+
else if (value == null || value === "") {
|
|
27
27
|
option = undefined;
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
@@ -46,7 +46,7 @@ export function SelectEx(props) {
|
|
|
46
46
|
}, [options, propertyWay]);
|
|
47
47
|
// Local value
|
|
48
48
|
const v = defaultValue !== null && defaultValue !== void 0 ? defaultValue : value;
|
|
49
|
-
const valueSource = React.useMemo(() => (multiple ? (v ? (Array.isArray(v) ? v : [v]) : []) : v !== null && v !== void 0 ? v :
|
|
49
|
+
const valueSource = React.useMemo(() => (multiple ? (v ? (Array.isArray(v) ? v : [v]) : []) : v !== null && v !== void 0 ? v : ""), [multiple, v]);
|
|
50
50
|
// Value state
|
|
51
51
|
const [valueState, setValueStateBase] = React.useState(valueSource);
|
|
52
52
|
const valueRef = React.useRef();
|
|
@@ -66,10 +66,10 @@ export function SelectEx(props) {
|
|
|
66
66
|
if (id !== valueRef.current) {
|
|
67
67
|
// Difference
|
|
68
68
|
const diff = multiple
|
|
69
|
-
?
|
|
69
|
+
? ArrayUtils.differences(id, valueRef.current)
|
|
70
70
|
: id;
|
|
71
71
|
setValueState(id);
|
|
72
|
-
const input = (_a = divRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(
|
|
72
|
+
const input = (_a = divRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("input");
|
|
73
73
|
if (input) {
|
|
74
74
|
// Different value, trigger change event
|
|
75
75
|
ReactUtils.triggerChange(input, id, false);
|
|
@@ -84,7 +84,7 @@ export function SelectEx(props) {
|
|
|
84
84
|
};
|
|
85
85
|
// Get option label
|
|
86
86
|
const getLabel = (option) => {
|
|
87
|
-
return typeof labelField ===
|
|
87
|
+
return typeof labelField === "function"
|
|
88
88
|
? labelField(option)
|
|
89
89
|
: option[labelField];
|
|
90
90
|
};
|
|
@@ -112,30 +112,28 @@ export function SelectEx(props) {
|
|
|
112
112
|
// When layout ready
|
|
113
113
|
React.useEffect(() => {
|
|
114
114
|
var _a;
|
|
115
|
-
const input = (_a = divRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(
|
|
115
|
+
const input = (_a = divRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("input");
|
|
116
116
|
const inputChange = (event) => {
|
|
117
117
|
// Reset case
|
|
118
118
|
if (event.cancelable)
|
|
119
|
-
setValueState(multiple ? [] :
|
|
119
|
+
setValueState(multiple ? [] : "");
|
|
120
120
|
};
|
|
121
|
-
input === null || input === void 0 ? void 0 : input.addEventListener(
|
|
121
|
+
input === null || input === void 0 ? void 0 : input.addEventListener("change", inputChange);
|
|
122
122
|
isMounted.current = true;
|
|
123
123
|
return () => {
|
|
124
124
|
isMounted.current = false;
|
|
125
|
-
input === null || input === void 0 ? void 0 : input.removeEventListener(
|
|
125
|
+
input === null || input === void 0 ? void 0 : input.removeEventListener("change", inputChange);
|
|
126
126
|
};
|
|
127
127
|
}, [multiple]);
|
|
128
128
|
// Layout
|
|
129
129
|
return (React.createElement(Stack, { direction: "row" },
|
|
130
130
|
React.createElement(FormControl, { size: search ? MUGlobal.searchFieldSize : MUGlobal.inputFieldSize, fullWidth: fullWidth, error: error },
|
|
131
|
-
React.createElement(InputLabel, { id: labelId, shrink: search
|
|
132
|
-
? MUGlobal.searchFieldShrink
|
|
133
|
-
: MUGlobal.inputFieldShrink }, label),
|
|
131
|
+
React.createElement(InputLabel, { id: labelId, shrink: search ? MUGlobal.searchFieldShrink : MUGlobal.inputFieldShrink }, label),
|
|
134
132
|
React.createElement(Select, { ref: divRef, value: multiple
|
|
135
133
|
? valueState
|
|
136
134
|
: localOptions.some((o) => o[idField] === valueState)
|
|
137
135
|
? valueState
|
|
138
|
-
:
|
|
136
|
+
: "", input: React.createElement(OutlinedInput, { notched: true, label: label, required: inputRequired }), labelId: labelId, name: name, multiple: multiple, onChange: (event, child) => {
|
|
139
137
|
if (onChange) {
|
|
140
138
|
onChange(event, child);
|
|
141
139
|
// event.preventDefault() will block executing
|
|
@@ -160,8 +158,8 @@ export function SelectEx(props) {
|
|
|
160
158
|
: selected === id;
|
|
161
159
|
})
|
|
162
160
|
.map((option) => getLabel(option))
|
|
163
|
-
.join(
|
|
164
|
-
}, sx: { minWidth:
|
|
161
|
+
.join(", ");
|
|
162
|
+
}, sx: { minWidth: "150px" }, fullWidth: fullWidth, ...rest }, localOptions.map((option) => {
|
|
165
163
|
// Option id
|
|
166
164
|
const id = getId(option);
|
|
167
165
|
// Option label
|
|
@@ -171,18 +169,16 @@ export function SelectEx(props) {
|
|
|
171
169
|
if (onItemClick) {
|
|
172
170
|
onItemClick(event, option);
|
|
173
171
|
}
|
|
174
|
-
}, style: itemStyle == null
|
|
175
|
-
? undefined
|
|
176
|
-
: itemStyle(option) },
|
|
172
|
+
}, style: itemStyle == null ? undefined : itemStyle(option) },
|
|
177
173
|
multiple && (React.createElement(Checkbox, { checked: Array.isArray(valueState)
|
|
178
174
|
? valueState.includes(id)
|
|
179
175
|
: valueState === id })),
|
|
180
176
|
React.createElement(ListItemText, { primary: label }),
|
|
181
177
|
itemIconRenderer && (React.createElement(ListItemRightIcon, null, itemIconRenderer(option[idField])))));
|
|
182
178
|
})),
|
|
183
|
-
helperText != null &&
|
|
179
|
+
helperText != null && React.createElement(FormHelperText, null, helperText)),
|
|
184
180
|
refresh != null &&
|
|
185
181
|
loadData != null &&
|
|
186
|
-
(typeof refresh ===
|
|
182
|
+
(typeof refresh === "string" ? (React.createElement(IconButton, { size: "small", title: refresh, onClick: refreshData },
|
|
187
183
|
React.createElement(RefreshIcon, null))) : (refresh))));
|
|
188
184
|
}
|
package/lib/TagList.js
CHANGED
|
@@ -12,15 +12,22 @@ export function TagList(props) {
|
|
|
12
12
|
// Destruct
|
|
13
13
|
const { renderOption = (props, option, { selected }) => (React.createElement("li", { ...props },
|
|
14
14
|
React.createElement(Checkbox, { icon: React.createElement(CheckBoxOutlineBlankIcon, { fontSize: "small" }), checkedIcon: React.createElement(CheckBoxIcon, { fontSize: "small" }), style: { marginRight: 8 }, checked: selected }),
|
|
15
|
-
option)), renderTags = (value, getTagProps) => value.map((option, index) => (React.createElement(Chip, { variant: "outlined", label: option, ...getTagProps({ index }) }))), noOptionsText = noOptions, loadingText = loadingLabel, openText = openDefault, loadData, maxItems = 16, disableCloseOnSelect = true, openOnFocus = true, label, inputProps, ...rest } = props;
|
|
15
|
+
option)), renderTags = (value, getTagProps) => value.map((option, index) => (React.createElement(Chip, { variant: "outlined", label: option, ...getTagProps({ index }) }))), noOptionsText = noOptions, loadingText = loadingLabel, openText = openDefault, loadData, maxItems = 16, disableCloseOnSelect = true, openOnFocus = true, label, inputProps, onChange, value, ...rest } = props;
|
|
16
16
|
const [open, setOpen] = React.useState(false);
|
|
17
17
|
const [options, setOptions] = React.useState([]);
|
|
18
18
|
const [loading, setLoading] = React.useState(false);
|
|
19
|
+
const currentValue = React.useRef([]);
|
|
20
|
+
currentValue.current = value !== null && value !== void 0 ? value : [];
|
|
19
21
|
const loadDataLocal = async (keyword) => {
|
|
20
22
|
var _a;
|
|
21
23
|
setLoading(true);
|
|
22
24
|
const result = (_a = (await loadData(keyword, maxItems))) !== null && _a !== void 0 ? _a : [];
|
|
23
|
-
|
|
25
|
+
const len = result.length;
|
|
26
|
+
currentValue.current.forEach((item) => {
|
|
27
|
+
if (!result.includes(item))
|
|
28
|
+
result.push(item);
|
|
29
|
+
});
|
|
30
|
+
if (len >= maxItems) {
|
|
24
31
|
result.push(moreLabel);
|
|
25
32
|
}
|
|
26
33
|
setOptions(result);
|
|
@@ -40,5 +47,9 @@ export function TagList(props) {
|
|
|
40
47
|
await loadDataLocal(event.target.value);
|
|
41
48
|
}, ...inputProps, ...params })), getOptionDisabled: (item) => {
|
|
42
49
|
return item === moreLabel;
|
|
43
|
-
}, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText,
|
|
50
|
+
}, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, value: value, onChange: (event, value, reason, details) => {
|
|
51
|
+
currentValue.current = value;
|
|
52
|
+
if (onChange)
|
|
53
|
+
onChange(event, value, reason, details);
|
|
54
|
+
}, ...rest }));
|
|
44
55
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { AutocompleteProps } from "@mui/material";
|
|
3
|
+
import { InputFieldProps } from "./InputField";
|
|
4
|
+
type DataType = {
|
|
5
|
+
id: number | string;
|
|
6
|
+
} & ({
|
|
7
|
+
label: string;
|
|
8
|
+
} | {
|
|
9
|
+
name: string;
|
|
10
|
+
});
|
|
11
|
+
export type TagListProProps<D extends DataType = DataType> = Omit<AutocompleteProps<D, true, false, false>, "open" | "multiple" | "options" | "renderInput"> & {
|
|
12
|
+
/**
|
|
13
|
+
* Label
|
|
14
|
+
*/
|
|
15
|
+
label?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Load data callback
|
|
18
|
+
*/
|
|
19
|
+
loadData: (keyword: string | undefined, items: number) => PromiseLike<D[] | null | undefined>;
|
|
20
|
+
/**
|
|
21
|
+
* Input props
|
|
22
|
+
*/
|
|
23
|
+
inputProps?: Omit<InputFieldProps, "onChange">;
|
|
24
|
+
/**
|
|
25
|
+
* Max items
|
|
26
|
+
*/
|
|
27
|
+
maxItems?: number;
|
|
28
|
+
};
|
|
29
|
+
export declare function TagListPro<D extends DataType = DataType>(props: TagListProProps<D>): JSX.Element;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Autocomplete, Checkbox, Chip } from "@mui/material";
|
|
2
|
+
import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank";
|
|
3
|
+
import CheckBoxIcon from "@mui/icons-material/CheckBox";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { InputField } from "./InputField";
|
|
6
|
+
import { globalApp } from "./app/ReactApp";
|
|
7
|
+
export function TagListPro(props) {
|
|
8
|
+
var _a;
|
|
9
|
+
// Labels
|
|
10
|
+
const { noOptions, loading: loadingLabel, more = "More", open: openDefault } = (_a = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("noOptions", "loading", "more", "open")) !== null && _a !== void 0 ? _a : {};
|
|
11
|
+
const moreLabel = more + "...";
|
|
12
|
+
const getLabel = (item) => "label" in item ? item.label : "name" in item ? item.name : "";
|
|
13
|
+
// Destruct
|
|
14
|
+
const { renderOption = (props, option, { selected }) => (React.createElement("li", { ...props },
|
|
15
|
+
React.createElement(React.Fragment, null,
|
|
16
|
+
React.createElement(Checkbox, { icon: React.createElement(CheckBoxOutlineBlankIcon, { fontSize: "small" }), checkedIcon: React.createElement(CheckBoxIcon, { fontSize: "small" }), style: { marginRight: 8 }, checked: selected }),
|
|
17
|
+
getLabel(option)))), renderTags = (value, getTagProps) => value.map((option, index) => (React.createElement(Chip, { variant: "outlined", label: getLabel(option), ...getTagProps({ index }) }))), noOptionsText = noOptions, loadingText = loadingLabel, openText = openDefault, loadData, maxItems = 16, disableCloseOnSelect = true, openOnFocus = true, label, inputProps, onChange, value, ...rest } = props;
|
|
18
|
+
const [open, setOpen] = React.useState(false);
|
|
19
|
+
const [options, setOptions] = React.useState([]);
|
|
20
|
+
const [loading, setLoading] = React.useState(false);
|
|
21
|
+
const currentValue = React.useRef([]);
|
|
22
|
+
currentValue.current = value !== null && value !== void 0 ? value : [];
|
|
23
|
+
const loadDataLocal = async (keyword) => {
|
|
24
|
+
var _a;
|
|
25
|
+
setLoading(true);
|
|
26
|
+
const result = (_a = (await loadData(keyword, maxItems))) !== null && _a !== void 0 ? _a : [];
|
|
27
|
+
const len = result.length;
|
|
28
|
+
currentValue.current.forEach((item) => {
|
|
29
|
+
if (!result.some((r) => r.id === item.id))
|
|
30
|
+
result.push(item);
|
|
31
|
+
});
|
|
32
|
+
if (len >= maxItems) {
|
|
33
|
+
result.push({ id: -1, name: moreLabel });
|
|
34
|
+
}
|
|
35
|
+
setOptions(result);
|
|
36
|
+
setLoading(false);
|
|
37
|
+
};
|
|
38
|
+
return (React.createElement(Autocomplete, { multiple: true, filterOptions: (options, _state) => options, open: open, onOpen: () => {
|
|
39
|
+
setOpen(true);
|
|
40
|
+
if (options.length === 0) {
|
|
41
|
+
loadDataLocal();
|
|
42
|
+
}
|
|
43
|
+
}, onClose: () => {
|
|
44
|
+
setOpen(false);
|
|
45
|
+
}, options: options, loading: loading, disableCloseOnSelect: disableCloseOnSelect, openOnFocus: openOnFocus, renderOption: renderOption, renderTags: renderTags, renderInput: (params) => (React.createElement(InputField, { label: label, changeDelay: 480, onChange: async (event) => {
|
|
46
|
+
// Stop bubble
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
event.stopPropagation();
|
|
49
|
+
await loadDataLocal(event.target.value);
|
|
50
|
+
}, ...inputProps, ...params })), getOptionDisabled: (item) => {
|
|
51
|
+
return (typeof item.id === "number" &&
|
|
52
|
+
item.id < 0 &&
|
|
53
|
+
getLabel(item) === moreLabel);
|
|
54
|
+
}, getOptionLabel: (item) => getLabel(item), isOptionEqualToValue: (option, value) => option.id === value.id, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, value: value, onChange: (event, value, reason, details) => {
|
|
55
|
+
currentValue.current = value;
|
|
56
|
+
if (onChange)
|
|
57
|
+
onChange(event, value, reason, details);
|
|
58
|
+
}, ...rest }));
|
|
59
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export * from "./BridgeCloseButton";
|
|
|
34
34
|
export * from "./ButtonLink";
|
|
35
35
|
export * from "./ComboBox";
|
|
36
36
|
export * from "./ComboBoxMultiple";
|
|
37
|
+
export * from "./ComboBoxPro";
|
|
37
38
|
export * from "./CountdownButton";
|
|
38
39
|
export * from "./CountryList";
|
|
39
40
|
export * from "./CustomFabProps";
|
|
@@ -87,6 +88,7 @@ export * from "./TableEx";
|
|
|
87
88
|
export * from "./TextFieldEx";
|
|
88
89
|
export * from "./Tiplist";
|
|
89
90
|
export * from "./TagList";
|
|
91
|
+
export * from "./TagListPro";
|
|
90
92
|
export * from "./TwoFieldInput";
|
|
91
93
|
export * from "./TooltipClick";
|
|
92
94
|
export * from "./UserAvatar";
|
package/lib/index.js
CHANGED
|
@@ -34,6 +34,7 @@ export * from "./BridgeCloseButton";
|
|
|
34
34
|
export * from "./ButtonLink";
|
|
35
35
|
export * from "./ComboBox";
|
|
36
36
|
export * from "./ComboBoxMultiple";
|
|
37
|
+
export * from "./ComboBoxPro";
|
|
37
38
|
export * from "./CountdownButton";
|
|
38
39
|
export * from "./CountryList";
|
|
39
40
|
export * from "./CustomFabProps";
|
|
@@ -87,6 +88,7 @@ export * from "./TableEx";
|
|
|
87
88
|
export * from "./TextFieldEx";
|
|
88
89
|
export * from "./Tiplist";
|
|
89
90
|
export * from "./TagList";
|
|
91
|
+
export * from "./TagListPro";
|
|
90
92
|
export * from "./TwoFieldInput";
|
|
91
93
|
export * from "./TooltipClick";
|
|
92
94
|
export * from "./UserAvatar";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.72",
|
|
4
4
|
"description": "TypeScript Material-UI Implementation",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"@emotion/css": "^11.10.6",
|
|
51
51
|
"@emotion/react": "^11.10.6",
|
|
52
52
|
"@emotion/styled": "^11.10.6",
|
|
53
|
-
"@etsoo/appscript": "^1.3.
|
|
53
|
+
"@etsoo/appscript": "^1.3.83",
|
|
54
54
|
"@etsoo/notificationbase": "^1.1.24",
|
|
55
|
-
"@etsoo/react": "^1.6.
|
|
56
|
-
"@etsoo/shared": "^1.1.
|
|
55
|
+
"@etsoo/react": "^1.6.56",
|
|
56
|
+
"@etsoo/shared": "^1.1.93",
|
|
57
57
|
"@mui/icons-material": "^5.11.11",
|
|
58
58
|
"@mui/material": "^5.11.14",
|
|
59
59
|
"@mui/x-data-grid": "^6.0.3",
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Autocomplete, AutocompleteProps } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { globalApp } from "./app/ReactApp";
|
|
4
|
+
import { InputField, InputFieldProps } from "./InputField";
|
|
5
|
+
|
|
6
|
+
type DataType = {
|
|
7
|
+
id: number | string;
|
|
8
|
+
} & ({ label: string } | { name: string });
|
|
9
|
+
|
|
10
|
+
export type ComboBoxProProps<D extends DataType = DataType> = Omit<
|
|
11
|
+
AutocompleteProps<D, false, false, true>,
|
|
12
|
+
"open" | "multiple" | "options" | "renderInput"
|
|
13
|
+
> & {
|
|
14
|
+
/**
|
|
15
|
+
* Label
|
|
16
|
+
*/
|
|
17
|
+
label?: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Field name
|
|
21
|
+
*/
|
|
22
|
+
name?: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Id value
|
|
26
|
+
*/
|
|
27
|
+
idValue?: D["id"] | null;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Options
|
|
31
|
+
*/
|
|
32
|
+
options: (() => PromiseLike<D[] | null | undefined>) | D[];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Input props
|
|
36
|
+
*/
|
|
37
|
+
inputProps?: Omit<InputFieldProps, "onChange">;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function ComboBoxPro<D extends DataType = DataType>(
|
|
41
|
+
props: ComboBoxProProps<D>
|
|
42
|
+
) {
|
|
43
|
+
// Labels
|
|
44
|
+
const {
|
|
45
|
+
noOptions,
|
|
46
|
+
loading: loadingLabel,
|
|
47
|
+
open: openDefault
|
|
48
|
+
} = globalApp?.getLabels("noOptions", "loading", "open") ?? {};
|
|
49
|
+
|
|
50
|
+
const getLabel = (item: D) =>
|
|
51
|
+
"label" in item ? item.label : "name" in item ? item.name : "";
|
|
52
|
+
|
|
53
|
+
// Destruct
|
|
54
|
+
const {
|
|
55
|
+
noOptionsText = noOptions,
|
|
56
|
+
loadingText = loadingLabel,
|
|
57
|
+
openText = openDefault,
|
|
58
|
+
options,
|
|
59
|
+
openOnFocus = true,
|
|
60
|
+
label,
|
|
61
|
+
inputProps,
|
|
62
|
+
name,
|
|
63
|
+
value,
|
|
64
|
+
idValue,
|
|
65
|
+
onChange,
|
|
66
|
+
...rest
|
|
67
|
+
} = props;
|
|
68
|
+
|
|
69
|
+
const [open, setOpen] = React.useState(false);
|
|
70
|
+
const [localOptions, setOptions] = React.useState<readonly D[]>([]);
|
|
71
|
+
const [localValue, setValue] = React.useState<string | D | null>(null);
|
|
72
|
+
const [loading, setLoading] = React.useState(false);
|
|
73
|
+
|
|
74
|
+
React.useEffect(() => {
|
|
75
|
+
if (value == null) return;
|
|
76
|
+
setValue(value);
|
|
77
|
+
}, [value]);
|
|
78
|
+
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
if (idValue == null) return;
|
|
81
|
+
const option = localOptions.find((option) => option.id === idValue);
|
|
82
|
+
if (option) setValue(option);
|
|
83
|
+
}, [localOptions]);
|
|
84
|
+
|
|
85
|
+
React.useEffect(() => {
|
|
86
|
+
if (typeof options === "function") {
|
|
87
|
+
setLoading(true);
|
|
88
|
+
options().then((result) => {
|
|
89
|
+
setLoading(false);
|
|
90
|
+
if (result != null) setOptions(result);
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
setOptions(options);
|
|
94
|
+
}
|
|
95
|
+
}, [options]);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<Autocomplete<D, false, false, true>
|
|
99
|
+
id={name}
|
|
100
|
+
value={localValue}
|
|
101
|
+
open={open}
|
|
102
|
+
freeSolo
|
|
103
|
+
onOpen={() => {
|
|
104
|
+
setOpen(true);
|
|
105
|
+
}}
|
|
106
|
+
onClose={() => {
|
|
107
|
+
setOpen(false);
|
|
108
|
+
}}
|
|
109
|
+
options={localOptions}
|
|
110
|
+
loading={loading}
|
|
111
|
+
openOnFocus={openOnFocus}
|
|
112
|
+
renderInput={(params) => (
|
|
113
|
+
<InputField {...inputProps} {...params} label={label} name={name} />
|
|
114
|
+
)}
|
|
115
|
+
getOptionLabel={(item) =>
|
|
116
|
+
typeof item === "object" ? getLabel(item) : item
|
|
117
|
+
}
|
|
118
|
+
isOptionEqualToValue={(option, value) => option.id === value.id}
|
|
119
|
+
noOptionsText={noOptionsText}
|
|
120
|
+
loadingText={loadingText}
|
|
121
|
+
openText={openText}
|
|
122
|
+
onChange={(event, value, reason, details) => {
|
|
123
|
+
setValue(value);
|
|
124
|
+
if (onChange) onChange(event, value, reason, details);
|
|
125
|
+
}}
|
|
126
|
+
{...rest}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
}
|