@etsoo/materialui 1.2.53 → 1.2.56
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/HiSelector.js +1 -1
- package/lib/ListChooser.d.ts +7 -3
- package/lib/ListChooser.js +25 -11
- package/lib/SelectEx.d.ts +0 -4
- package/lib/SelectEx.js +4 -4
- package/package.json +7 -7
- package/src/HiSelector.tsx +1 -1
- package/src/ListChooser.tsx +203 -176
- package/src/SelectEx.tsx +7 -9
package/lib/HiSelector.js
CHANGED
|
@@ -44,7 +44,7 @@ export function HiSelector(props) {
|
|
|
44
44
|
React.createElement(FormLabel, { required: required, sx: { fontSize: (theme) => theme.typography.caption } }, label))),
|
|
45
45
|
React.createElement("input", { type: "hidden", name: name, value: `${currentValue !== null && currentValue !== void 0 ? currentValue : ""}` }),
|
|
46
46
|
React.createElement(Grid, { item: true, ...breakPoints },
|
|
47
|
-
React.createElement(SelectEx, { idField: idField, labelField: labelField, name: "tab1", search: search, fullWidth: true, loadData: () => loadData(), value: values[0], onChange: (event) => doChange(event, 0), onItemChange: doItemChange,
|
|
47
|
+
React.createElement(SelectEx, { idField: idField, labelField: labelField, name: "tab1", search: search, fullWidth: true, loadData: () => loadData(), value: values[0], onChange: (event) => doChange(event, 0), onItemChange: doItemChange, required: required, error: error, helperText: helperText })),
|
|
48
48
|
localValues[0] != null && (React.createElement(Grid, { item: true, ...breakPoints },
|
|
49
49
|
React.createElement(SelectEx, { key: `${localValues[0]}`, idField: idField, labelField: labelField, name: "tab2", search: search, fullWidth: true, loadData: () => loadData(localValues[0]), value: values[1], onChange: (event) => doChange(event, 1), onItemChange: doItemChange }))),
|
|
50
50
|
localValues[1] != null && (React.createElement(Grid, { item: true, ...breakPoints },
|
package/lib/ListChooser.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DataTypes, DelayedExecutorType, IdDefaultType } from
|
|
2
|
-
import { ListItemButtonProps, ListProps } from
|
|
3
|
-
import React from
|
|
1
|
+
import { DataTypes, DelayedExecutorType, IdDefaultType } from "@etsoo/shared";
|
|
2
|
+
import { ListItemButtonProps, ListProps } from "@mui/material";
|
|
3
|
+
import React from "react";
|
|
4
4
|
type QueryData = {
|
|
5
5
|
title?: string;
|
|
6
6
|
};
|
|
@@ -46,6 +46,10 @@ export type ListChooserProps<T extends object, D extends DataTypes.Keys<T>, Q ex
|
|
|
46
46
|
* Title
|
|
47
47
|
*/
|
|
48
48
|
title: string;
|
|
49
|
+
/**
|
|
50
|
+
* Double click enabled
|
|
51
|
+
*/
|
|
52
|
+
doubleClickEnabled?: boolean;
|
|
49
53
|
};
|
|
50
54
|
/**
|
|
51
55
|
* List chooser
|
package/lib/ListChooser.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useDelayedExecutor } from
|
|
2
|
-
import { List, ListItem, ListItemButton, ListItemText, TextField } from
|
|
3
|
-
import
|
|
4
|
-
import
|
|
1
|
+
import { useDelayedExecutor } from "@etsoo/react";
|
|
2
|
+
import { List, ListItem, ListItemButton, ListItemText, TextField } from "@mui/material";
|
|
3
|
+
import CheckBoxIcon from "@mui/icons-material/CheckBox";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { VBox } from "./FlexBox";
|
|
5
6
|
/**
|
|
6
7
|
* List chooser
|
|
7
8
|
* @param props Props
|
|
@@ -29,19 +30,20 @@ export function ListChooser(props) {
|
|
|
29
30
|
});
|
|
30
31
|
// Destruct
|
|
31
32
|
const { conditionRenderer = (rq, delayed) => (React.createElement(TextField, { autoFocus: true, margin: "dense", name: "title", label: title, fullWidth: true, variant: "standard", inputProps: { maxLength: 128 }, onChange: (event) => {
|
|
32
|
-
Reflect.set(rq,
|
|
33
|
+
Reflect.set(rq, "title", event.target.value);
|
|
33
34
|
delayed.call();
|
|
34
35
|
} })), itemRenderer = (item, selectProps) => {
|
|
35
36
|
const id = item[idField];
|
|
36
|
-
const
|
|
37
|
+
const sp = selectProps(id);
|
|
38
|
+
const label = typeof labelField === "function"
|
|
37
39
|
? labelField(item)
|
|
38
40
|
: Reflect.get(item, labelField);
|
|
39
|
-
return (React.createElement(ListItem, { disableGutters: true, key: `${id}
|
|
40
|
-
React.createElement(ListItemButton, { ...
|
|
41
|
+
return (React.createElement(ListItem, { disableGutters: true, key: `${id}`, secondaryAction: sp.selected ? React.createElement(CheckBoxIcon, { fontSize: "small" }) : undefined },
|
|
42
|
+
React.createElement(ListItemButton, { ...sp },
|
|
41
43
|
React.createElement(ListItemText, { primary: label }))));
|
|
42
|
-
}, idField =
|
|
44
|
+
}, idField = "id", labelField = "label", loadData, multiple = false, onItemChange, title, doubleClickEnabled = false, onDoubleClick, ...rest } = props;
|
|
43
45
|
// Default minimum height
|
|
44
|
-
(_a = rest.sx) !== null && _a !== void 0 ? _a : (rest.sx = { minHeight:
|
|
46
|
+
(_a = rest.sx) !== null && _a !== void 0 ? _a : (rest.sx = { minHeight: "220px" });
|
|
45
47
|
// State
|
|
46
48
|
const [items, setItems] = React.useState([]);
|
|
47
49
|
// Query request data
|
|
@@ -72,8 +74,20 @@ export function ListChooser(props) {
|
|
|
72
74
|
delayed.clear();
|
|
73
75
|
};
|
|
74
76
|
}, [delayed]);
|
|
77
|
+
const onDoubleClickLocal = (event) => {
|
|
78
|
+
var _a;
|
|
79
|
+
if (onDoubleClick)
|
|
80
|
+
onDoubleClick(event);
|
|
81
|
+
if (doubleClickEnabled) {
|
|
82
|
+
const button = (_a = event.currentTarget
|
|
83
|
+
.closest("form")) === null || _a === void 0 ? void 0 : _a.elements.namedItem("okButton");
|
|
84
|
+
if (button) {
|
|
85
|
+
button.click();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
75
89
|
// Layout
|
|
76
90
|
return (React.createElement(VBox, null,
|
|
77
91
|
conditionRenderer(rq.current, delayed),
|
|
78
|
-
React.createElement(List, { disablePadding: true, dense: true, ...rest }, items.map((item) => itemRenderer(item, selectProps)))));
|
|
92
|
+
React.createElement(List, { onDoubleClick: onDoubleClickLocal, disablePadding: true, dense: true, ...rest }, items.map((item) => itemRenderer(item, selectProps)))));
|
|
79
93
|
}
|
package/lib/SelectEx.d.ts
CHANGED
package/lib/SelectEx.js
CHANGED
|
@@ -12,7 +12,7 @@ import { ReactUtils } from "@etsoo/react";
|
|
|
12
12
|
*/
|
|
13
13
|
export function SelectEx(props) {
|
|
14
14
|
// Destruct
|
|
15
|
-
const { defaultValue, idField = "id", error, helperText,
|
|
15
|
+
const { defaultValue, idField = "id", error, helperText, itemIconRenderer, itemStyle, label, labelField = "label", loadData, onItemChange, onItemClick, onLoadData, multiple = false, name, options, refresh, search = false, autoAddBlankItem = search, value, onChange, fullWidth, required, variant = "outlined", ...rest } = props;
|
|
16
16
|
// Options state
|
|
17
17
|
const [localOptions, setOptions] = React.useState([]);
|
|
18
18
|
const isMounted = React.useRef(false);
|
|
@@ -128,12 +128,12 @@ export function SelectEx(props) {
|
|
|
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 ? MUGlobal.searchFieldShrink : MUGlobal.inputFieldShrink }, label),
|
|
131
|
+
React.createElement(InputLabel, { id: labelId, variant: variant, shrink: search ? MUGlobal.searchFieldShrink : MUGlobal.inputFieldShrink, required: required }, label),
|
|
132
132
|
React.createElement(Select, { ref: divRef, value: multiple
|
|
133
133
|
? valueState
|
|
134
134
|
: localOptions.some((o) => o[idField] === valueState)
|
|
135
135
|
? valueState
|
|
136
|
-
: "", input: React.createElement(OutlinedInput, { notched: true, label: label, required:
|
|
136
|
+
: "", input: React.createElement(OutlinedInput, { notched: true, label: label, required: required }), labelId: labelId, name: name, multiple: multiple, onChange: (event, child) => {
|
|
137
137
|
if (onChange) {
|
|
138
138
|
onChange(event, child);
|
|
139
139
|
// event.preventDefault() will block executing
|
|
@@ -159,7 +159,7 @@ export function SelectEx(props) {
|
|
|
159
159
|
})
|
|
160
160
|
.map((option) => getLabel(option))
|
|
161
161
|
.join(", ");
|
|
162
|
-
}, sx: { minWidth: "150px" }, fullWidth: fullWidth, ...rest }, localOptions.map((option) => {
|
|
162
|
+
}, sx: { minWidth: "150px" }, fullWidth: fullWidth, required: required, variant: variant, ...rest }, localOptions.map((option) => {
|
|
163
163
|
// Option id
|
|
164
164
|
const id = getId(option);
|
|
165
165
|
// Option label
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.56",
|
|
4
4
|
"description": "TypeScript Material-UI Implementation",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -50,18 +50,18 @@
|
|
|
50
50
|
"@emotion/css": "^11.11.0",
|
|
51
51
|
"@emotion/react": "^11.11.1",
|
|
52
52
|
"@emotion/styled": "^11.11.0",
|
|
53
|
-
"@etsoo/appscript": "^1.4.
|
|
53
|
+
"@etsoo/appscript": "^1.4.19",
|
|
54
54
|
"@etsoo/notificationbase": "^1.1.25",
|
|
55
55
|
"@etsoo/react": "^1.6.90",
|
|
56
56
|
"@etsoo/shared": "^1.2.5",
|
|
57
57
|
"@mui/icons-material": "^5.11.16",
|
|
58
|
-
"@mui/material": "^5.13.
|
|
58
|
+
"@mui/material": "^5.13.5",
|
|
59
59
|
"@mui/x-data-grid": "^6.7.0",
|
|
60
60
|
"@types/pica": "^9.0.1",
|
|
61
61
|
"@types/pulltorefreshjs": "^0.1.5",
|
|
62
|
-
"@types/react": "^18.2.
|
|
62
|
+
"@types/react": "^18.2.12",
|
|
63
63
|
"@types/react-avatar-editor": "^13.0.0",
|
|
64
|
-
"@types/react-dom": "^18.2.
|
|
64
|
+
"@types/react-dom": "^18.2.5",
|
|
65
65
|
"@types/react-input-mask": "^3.0.2",
|
|
66
66
|
"chart.js": "^4.3.0",
|
|
67
67
|
"chartjs-plugin-datalabels": "^2.2.0",
|
|
@@ -85,8 +85,8 @@
|
|
|
85
85
|
"@testing-library/jest-dom": "^5.16.5",
|
|
86
86
|
"@testing-library/react": "^14.0.0",
|
|
87
87
|
"@types/jest": "^29.5.2",
|
|
88
|
-
"@typescript-eslint/eslint-plugin": "^5.59.
|
|
89
|
-
"@typescript-eslint/parser": "^5.59.
|
|
88
|
+
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
|
89
|
+
"@typescript-eslint/parser": "^5.59.11",
|
|
90
90
|
"jest": "^29.5.0",
|
|
91
91
|
"jest-environment-jsdom": "^29.5.0",
|
|
92
92
|
"typescript": "^5.1.3"
|
package/src/HiSelector.tsx
CHANGED
package/src/ListChooser.tsx
CHANGED
|
@@ -1,84 +1,90 @@
|
|
|
1
|
-
import { useDelayedExecutor } from
|
|
2
|
-
import { DataTypes, DelayedExecutorType, IdDefaultType } from
|
|
1
|
+
import { useDelayedExecutor } from "@etsoo/react";
|
|
2
|
+
import { DataTypes, DelayedExecutorType, IdDefaultType } from "@etsoo/shared";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from
|
|
12
|
-
import
|
|
13
|
-
import
|
|
4
|
+
List,
|
|
5
|
+
ListItem,
|
|
6
|
+
ListItemButton,
|
|
7
|
+
ListItemButtonProps,
|
|
8
|
+
ListItemText,
|
|
9
|
+
ListProps,
|
|
10
|
+
TextField
|
|
11
|
+
} from "@mui/material";
|
|
12
|
+
import CheckBoxIcon from "@mui/icons-material/CheckBox";
|
|
13
|
+
import React from "react";
|
|
14
|
+
import { VBox } from "./FlexBox";
|
|
14
15
|
|
|
15
16
|
type QueryData = {
|
|
16
|
-
|
|
17
|
+
title?: string;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* List chooser button props
|
|
21
22
|
*/
|
|
22
23
|
export interface ListChooserButtonProps<
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
T extends object,
|
|
25
|
+
D extends DataTypes.Keys<T>
|
|
25
26
|
> {
|
|
26
|
-
|
|
27
|
+
(id: T[D]): ListItemButtonProps;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* List chooser props
|
|
31
32
|
*/
|
|
32
33
|
export type ListChooserProps<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
T extends object,
|
|
35
|
+
D extends DataTypes.Keys<T>,
|
|
36
|
+
Q extends object
|
|
36
37
|
> = ListProps & {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Condition renderer
|
|
40
|
+
*/
|
|
41
|
+
conditionRenderer?: (
|
|
42
|
+
rq: Partial<Q>,
|
|
43
|
+
delayed: DelayedExecutorType
|
|
44
|
+
) => React.ReactNode;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* List item renderer
|
|
48
|
+
*/
|
|
49
|
+
itemRenderer?: (
|
|
50
|
+
data: T,
|
|
51
|
+
props: ListChooserButtonProps<T, D>
|
|
52
|
+
) => React.ReactNode;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Label field
|
|
56
|
+
*/
|
|
57
|
+
labelField?: DataTypes.Keys<T, string> | ((data: T) => string);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Id field
|
|
61
|
+
*/
|
|
62
|
+
idField?: D;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Load data callback
|
|
66
|
+
*/
|
|
67
|
+
loadData: (rq: Partial<Q>) => PromiseLike<T[] | null | undefined>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Multiple selected
|
|
71
|
+
*/
|
|
72
|
+
multiple?: boolean;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Item onchange callback
|
|
76
|
+
*/
|
|
77
|
+
onItemChange: (items: T[], ids: T[D][]) => void;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Title
|
|
81
|
+
*/
|
|
82
|
+
title: string;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Double click enabled
|
|
86
|
+
*/
|
|
87
|
+
doubleClickEnabled?: boolean;
|
|
82
88
|
};
|
|
83
89
|
|
|
84
90
|
/**
|
|
@@ -87,118 +93,139 @@ export type ListChooserProps<
|
|
|
87
93
|
* @returns Component
|
|
88
94
|
*/
|
|
89
95
|
export function ListChooser<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
T extends object,
|
|
97
|
+
D extends DataTypes.Keys<T> = IdDefaultType<T>,
|
|
98
|
+
Q extends object = QueryData
|
|
93
99
|
>(props: ListChooserProps<T, D, Q>) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
};
|
|
193
|
-
}, [delayed]);
|
|
194
|
-
|
|
195
|
-
// Layout
|
|
196
|
-
return (
|
|
197
|
-
<VBox>
|
|
198
|
-
{conditionRenderer(rq.current, delayed)}
|
|
199
|
-
<List disablePadding dense {...rest}>
|
|
200
|
-
{items.map((item) => itemRenderer(item, selectProps))}
|
|
201
|
-
</List>
|
|
202
|
-
</VBox>
|
|
100
|
+
// Selected ids state
|
|
101
|
+
const [selectedIds, setSelectedIds] = React.useState<T[D][]>([]);
|
|
102
|
+
|
|
103
|
+
const selectProps: ListChooserButtonProps<T, D> = (id: T[D]) => ({
|
|
104
|
+
selected: selectedIds.includes(id),
|
|
105
|
+
onClick: () => {
|
|
106
|
+
if (multiple) {
|
|
107
|
+
const index = selectedIds.indexOf(id);
|
|
108
|
+
if (index === -1) selectedIds.push(id);
|
|
109
|
+
else selectedIds.splice(index, 1);
|
|
110
|
+
setSelectedIds([...selectedIds]);
|
|
111
|
+
} else {
|
|
112
|
+
setSelectedIds([id]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Destruct
|
|
118
|
+
const {
|
|
119
|
+
conditionRenderer = (rq: Partial<Q>, delayed: DelayedExecutorType) => (
|
|
120
|
+
<TextField
|
|
121
|
+
autoFocus
|
|
122
|
+
margin="dense"
|
|
123
|
+
name="title"
|
|
124
|
+
label={title}
|
|
125
|
+
fullWidth
|
|
126
|
+
variant="standard"
|
|
127
|
+
inputProps={{ maxLength: 128 }}
|
|
128
|
+
onChange={(event) => {
|
|
129
|
+
Reflect.set(rq, "title", event.target.value);
|
|
130
|
+
delayed.call();
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
),
|
|
134
|
+
itemRenderer = (item, selectProps) => {
|
|
135
|
+
const id = item[idField];
|
|
136
|
+
const sp = selectProps(id);
|
|
137
|
+
const label =
|
|
138
|
+
typeof labelField === "function"
|
|
139
|
+
? labelField(item)
|
|
140
|
+
: (Reflect.get(item, labelField) as React.ReactNode);
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<ListItem
|
|
144
|
+
disableGutters
|
|
145
|
+
key={`${id}`}
|
|
146
|
+
secondaryAction={
|
|
147
|
+
sp.selected ? <CheckBoxIcon fontSize="small" /> : undefined
|
|
148
|
+
}
|
|
149
|
+
>
|
|
150
|
+
<ListItemButton {...sp}>
|
|
151
|
+
<ListItemText primary={label} />
|
|
152
|
+
</ListItemButton>
|
|
153
|
+
</ListItem>
|
|
154
|
+
);
|
|
155
|
+
},
|
|
156
|
+
idField = "id" as D,
|
|
157
|
+
labelField = "label",
|
|
158
|
+
loadData,
|
|
159
|
+
multiple = false,
|
|
160
|
+
onItemChange,
|
|
161
|
+
title,
|
|
162
|
+
doubleClickEnabled = false,
|
|
163
|
+
onDoubleClick,
|
|
164
|
+
...rest
|
|
165
|
+
} = props;
|
|
166
|
+
|
|
167
|
+
// Default minimum height
|
|
168
|
+
rest.sx ??= { minHeight: "220px" };
|
|
169
|
+
|
|
170
|
+
// State
|
|
171
|
+
const [items, setItems] = React.useState<T[]>([]);
|
|
172
|
+
|
|
173
|
+
// Query request data
|
|
174
|
+
const mounted = React.useRef<boolean>(false);
|
|
175
|
+
const rq = React.useRef<Partial<Q>>({});
|
|
176
|
+
|
|
177
|
+
// Delayed execution
|
|
178
|
+
const delayed = useDelayedExecutor(async () => {
|
|
179
|
+
const result = await loadData(rq.current);
|
|
180
|
+
if (result == null || !mounted.current) return;
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
!multiple &&
|
|
184
|
+
selectedIds.length > 0 &&
|
|
185
|
+
!result.some((item) => selectedIds.includes(item[idField]))
|
|
186
|
+
) {
|
|
187
|
+
setSelectedIds([]);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
setItems(result);
|
|
191
|
+
}, 480);
|
|
192
|
+
|
|
193
|
+
React.useEffect(() => {
|
|
194
|
+
if (!mounted.current) return;
|
|
195
|
+
onItemChange(
|
|
196
|
+
items.filter((item) => selectedIds.includes(item[idField])),
|
|
197
|
+
selectedIds
|
|
203
198
|
);
|
|
199
|
+
}, [selectedIds]);
|
|
200
|
+
|
|
201
|
+
React.useEffect(() => {
|
|
202
|
+
mounted.current = true;
|
|
203
|
+
delayed.call(0);
|
|
204
|
+
return () => {
|
|
205
|
+
mounted.current = false;
|
|
206
|
+
delayed.clear();
|
|
207
|
+
};
|
|
208
|
+
}, [delayed]);
|
|
209
|
+
|
|
210
|
+
const onDoubleClickLocal = (event: React.MouseEvent<HTMLUListElement>) => {
|
|
211
|
+
if (onDoubleClick) onDoubleClick(event);
|
|
212
|
+
if (doubleClickEnabled) {
|
|
213
|
+
const button = event.currentTarget
|
|
214
|
+
.closest("form")
|
|
215
|
+
?.elements.namedItem("okButton") as HTMLButtonElement | undefined;
|
|
216
|
+
if (button) {
|
|
217
|
+
button.click();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Layout
|
|
223
|
+
return (
|
|
224
|
+
<VBox>
|
|
225
|
+
{conditionRenderer(rq.current, delayed)}
|
|
226
|
+
<List onDoubleClick={onDoubleClickLocal} disablePadding dense {...rest}>
|
|
227
|
+
{items.map((item) => itemRenderer(item, selectProps))}
|
|
228
|
+
</List>
|
|
229
|
+
</VBox>
|
|
230
|
+
);
|
|
204
231
|
}
|
package/src/SelectEx.tsx
CHANGED
|
@@ -43,11 +43,6 @@ export type SelectExProps<
|
|
|
43
43
|
*/
|
|
44
44
|
helperText?: React.ReactNode;
|
|
45
45
|
|
|
46
|
-
/**
|
|
47
|
-
* Input required
|
|
48
|
-
*/
|
|
49
|
-
inputRequired?: boolean;
|
|
50
|
-
|
|
51
46
|
/**
|
|
52
47
|
* Id field
|
|
53
48
|
*/
|
|
@@ -120,7 +115,6 @@ export function SelectEx<
|
|
|
120
115
|
idField = "id" as D,
|
|
121
116
|
error,
|
|
122
117
|
helperText,
|
|
123
|
-
inputRequired,
|
|
124
118
|
itemIconRenderer,
|
|
125
119
|
itemStyle,
|
|
126
120
|
label,
|
|
@@ -138,6 +132,8 @@ export function SelectEx<
|
|
|
138
132
|
value,
|
|
139
133
|
onChange,
|
|
140
134
|
fullWidth,
|
|
135
|
+
required,
|
|
136
|
+
variant = "outlined",
|
|
141
137
|
...rest
|
|
142
138
|
} = props;
|
|
143
139
|
|
|
@@ -278,9 +274,11 @@ export function SelectEx<
|
|
|
278
274
|
>
|
|
279
275
|
<InputLabel
|
|
280
276
|
id={labelId}
|
|
277
|
+
variant={variant}
|
|
281
278
|
shrink={
|
|
282
279
|
search ? MUGlobal.searchFieldShrink : MUGlobal.inputFieldShrink
|
|
283
280
|
}
|
|
281
|
+
required={required}
|
|
284
282
|
>
|
|
285
283
|
{label}
|
|
286
284
|
</InputLabel>
|
|
@@ -293,9 +291,7 @@ export function SelectEx<
|
|
|
293
291
|
? valueState
|
|
294
292
|
: ""
|
|
295
293
|
}
|
|
296
|
-
input={
|
|
297
|
-
<OutlinedInput notched label={label} required={inputRequired} />
|
|
298
|
-
}
|
|
294
|
+
input={<OutlinedInput notched label={label} required={required} />}
|
|
299
295
|
labelId={labelId}
|
|
300
296
|
name={name}
|
|
301
297
|
multiple={multiple}
|
|
@@ -330,6 +326,8 @@ export function SelectEx<
|
|
|
330
326
|
}}
|
|
331
327
|
sx={{ minWidth: "150px" }}
|
|
332
328
|
fullWidth={fullWidth}
|
|
329
|
+
required={required}
|
|
330
|
+
variant={variant}
|
|
333
331
|
{...rest}
|
|
334
332
|
>
|
|
335
333
|
{localOptions.map((option) => {
|