@etsoo/materialui 1.1.70 → 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/Tiplist.d.ts +4 -0
- package/lib/Tiplist.js +4 -2
- 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 +12 -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/Tiplist.d.ts
CHANGED
package/lib/Tiplist.js
CHANGED
|
@@ -15,7 +15,9 @@ export function Tiplist(props) {
|
|
|
15
15
|
// Labels
|
|
16
16
|
const { noOptions, loading, more, open: openDefault } = (_a = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("noOptions", "loading", "more", "open")) !== null && _a !== void 0 ? _a : {};
|
|
17
17
|
// Destruct
|
|
18
|
-
const { search = false, idField = "id", idValue, inputAutoComplete = "new-password", inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, label, loadData, defaultValue, value, maxItems = 16, name, readOnly, onChange, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionLabel, getOptionDisabled, ...rest } = props;
|
|
18
|
+
const { search = false, idField = "id", idValue, inputAutoComplete = "new-password", inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, label, loadData, defaultValue, value, maxItems = 16, width, name, readOnly, onChange, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionLabel, getOptionDisabled, sx = {}, ...rest } = props;
|
|
19
|
+
if (width && sx)
|
|
20
|
+
Object.assign(sx, { width: `${width}px` });
|
|
19
21
|
// Value input ref
|
|
20
22
|
const inputRef = React.createRef();
|
|
21
23
|
// Local value
|
|
@@ -156,7 +158,7 @@ export function Tiplist(props) {
|
|
|
156
158
|
open: false,
|
|
157
159
|
...(!states.value && { options: [] })
|
|
158
160
|
});
|
|
159
|
-
}, loading: states.loading, renderInput: (params) => search ? (React.createElement(SearchField, { onChange: changeHandle, ...addReadOnly(params), readOnly: readOnly, label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })) : (React.createElement(InputField, { onChange: changeHandle, ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })), isOptionEqualToValue: (option, value) => option[idField] === value[idField], noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionDisabled: (item) => {
|
|
161
|
+
}, loading: states.loading, renderInput: (params) => search ? (React.createElement(SearchField, { onChange: changeHandle, ...addReadOnly(params), readOnly: readOnly, label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })) : (React.createElement(InputField, { onChange: changeHandle, ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })), isOptionEqualToValue: (option, value) => option[idField] === value[idField], sx: sx, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionDisabled: (item) => {
|
|
160
162
|
if (item[idField] === "n/a")
|
|
161
163
|
return true;
|
|
162
164
|
return getOptionDisabled ? getOptionDisabled(item) : false;
|
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
|
+
}
|