@etsoo/materialui 1.5.48 → 1.5.50
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/cjs/ButtonPopupCheckbox.js +48 -32
- package/lib/mjs/ButtonPopupCheckbox.js +48 -32
- package/package.json +10 -10
- package/src/ButtonPopupCheckbox.tsx +79 -51
|
@@ -8,7 +8,6 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
8
8
|
const Button_1 = __importDefault(require("@mui/material/Button"));
|
|
9
9
|
const Chip_1 = __importDefault(require("@mui/material/Chip"));
|
|
10
10
|
const react_1 = __importDefault(require("react"));
|
|
11
|
-
const shared_1 = require("@etsoo/shared");
|
|
12
11
|
const FormGroup_1 = __importDefault(require("@mui/material/FormGroup"));
|
|
13
12
|
const Grid_1 = __importDefault(require("@mui/material/Grid"));
|
|
14
13
|
const Typography_1 = __importDefault(require("@mui/material/Typography"));
|
|
@@ -23,25 +22,27 @@ const FlexBox_1 = require("./FlexBox");
|
|
|
23
22
|
const ReactApp_1 = require("./app/ReactApp");
|
|
24
23
|
function ButtonPopupList(props) {
|
|
25
24
|
// Destruct
|
|
26
|
-
const { addSplitter = /\s*[,;]\s*/, ids, labelField, labelFormatter
|
|
27
|
-
console.log("data", data);
|
|
28
|
-
if (labelField in data) {
|
|
29
|
-
return data[labelField];
|
|
30
|
-
}
|
|
31
|
-
return data.id.toString();
|
|
32
|
-
}, labels, loadData, onAdd } = props;
|
|
25
|
+
const { addSplitter = /\s*[,;]\s*/, ids = [], items, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
33
26
|
// Methods
|
|
34
27
|
const dndRef = react_1.default.createRef();
|
|
35
28
|
// Ref
|
|
36
29
|
const inputRef = react_1.default.useRef(null);
|
|
30
|
+
// State
|
|
31
|
+
const [selectedIds, setSelectedIds] = react_1.default.useState([]);
|
|
37
32
|
react_1.default.useEffect(() => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
33
|
+
// Sort items by ids for first load
|
|
34
|
+
items.sortByProperty("id", ids);
|
|
35
|
+
// Set selected ids
|
|
36
|
+
setSelectedIds([...ids]);
|
|
43
37
|
}, [ids]);
|
|
44
|
-
return ((0, jsx_runtime_1.jsxs)(FlexBox_1.VBox, { gap: 2, children: [(0, jsx_runtime_1.jsx)(FormGroup_1.default, { children: (0, jsx_runtime_1.jsx)(Grid_1.default, { container: true, spacing: 0, children: (0, jsx_runtime_1.jsx)(DnDList_1.DnDList, { items:
|
|
38
|
+
return ((0, jsx_runtime_1.jsxs)(FlexBox_1.VBox, { gap: 2, children: [(0, jsx_runtime_1.jsx)(FormGroup_1.default, { children: (0, jsx_runtime_1.jsx)(Grid_1.default, { container: true, spacing: 0, children: (0, jsx_runtime_1.jsx)(DnDList_1.DnDList, { items: items, labelField: labelField, itemRenderer: (item, index, nodeRef, actionNodeRef) => ((0, jsx_runtime_1.jsxs)(Grid_1.default, { size: { xs: 12, md: 6, lg: 4 }, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ...nodeRef, children: [(0, jsx_runtime_1.jsx)(IconButton_1.default, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ...actionNodeRef, children: (0, jsx_runtime_1.jsx)(DragIndicator_1.default, {}) }), (0, jsx_runtime_1.jsx)(FormControlLabel_1.default, { control: (0, jsx_runtime_1.jsx)(Checkbox_1.default, { name: "item", value: item.id, checked: selectedIds.includes(item.id), onChange: (e) => {
|
|
39
|
+
const checked = e.target.checked;
|
|
40
|
+
const newIds = [
|
|
41
|
+
...selectedIds.toggleItem(item.id, checked)
|
|
42
|
+
];
|
|
43
|
+
setSelectedIds(newIds);
|
|
44
|
+
onValueChange(newIds);
|
|
45
|
+
} }), label: `${index + 1}. ${labelFormatter(item)}` })] })), height: 200, mRef: dndRef }) }) }), onAdd && ((0, jsx_runtime_1.jsxs)(FlexBox_1.HBox, { gap: 1, children: [(0, jsx_runtime_1.jsx)(TextField_1.default, { variant: "outlined", label: labels?.more, fullWidth: true, inputRef: inputRef }), (0, jsx_runtime_1.jsx)(Button_1.default, { sx: { width: "120px" }, variant: "contained", startIcon: (0, jsx_runtime_1.jsx)(Add_1.default, {}), size: "small", onClick: async () => {
|
|
45
46
|
if (inputRef.current == null)
|
|
46
47
|
return;
|
|
47
48
|
const input = inputRef.current.value.trim();
|
|
@@ -71,7 +72,12 @@ function ButtonPopupCheckbox(props) {
|
|
|
71
72
|
// App
|
|
72
73
|
const app = (0, ReactApp_1.useRequiredAppContext)();
|
|
73
74
|
// Destruct
|
|
74
|
-
const { addSplitter, ids, inputName, label, labelEnd, labelFormatter
|
|
75
|
+
const { addSplitter, ids, inputName, label, labelEnd, labelFormatter = (data) => {
|
|
76
|
+
if (labelField in data) {
|
|
77
|
+
return data[labelField];
|
|
78
|
+
}
|
|
79
|
+
return data.id.toString();
|
|
80
|
+
}, labelField, labels = {}, loadData, onAdd, onValueChange, popupTitle = label, popupMessage, required = false, sx = { gap: 1, justifyContent: "flex-start" }, variant = "outlined", ...rest } = props;
|
|
75
81
|
// Default labels
|
|
76
82
|
if (!labels.add)
|
|
77
83
|
labels.add = app.get("add");
|
|
@@ -80,34 +86,44 @@ function ButtonPopupCheckbox(props) {
|
|
|
80
86
|
if (!labels.more)
|
|
81
87
|
labels.more = app.get("more");
|
|
82
88
|
// State
|
|
83
|
-
const [
|
|
89
|
+
const [items, setItems] = react_1.default.useState([]);
|
|
90
|
+
const [selectedIds, setSelectedIds] = react_1.default.useState();
|
|
91
|
+
react_1.default.useEffect(() => {
|
|
92
|
+
// Load data
|
|
93
|
+
loadData().then((data) => {
|
|
94
|
+
if (data != null) {
|
|
95
|
+
setItems(data);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}, []);
|
|
84
99
|
react_1.default.useEffect(() => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
setValues(ids);
|
|
100
|
+
// Set selected ids
|
|
101
|
+
setSelectedIds(ids);
|
|
88
102
|
}, [ids]);
|
|
103
|
+
// Selected ids
|
|
104
|
+
const tempSelectedIds = react_1.default.useRef();
|
|
89
105
|
// Click handler
|
|
90
106
|
const clickHandler = () => {
|
|
91
107
|
app.showInputDialog({
|
|
92
108
|
title: popupTitle,
|
|
93
109
|
message: popupMessage,
|
|
94
110
|
callback: (form) => {
|
|
95
|
-
if (form == null)
|
|
111
|
+
if (form == null || tempSelectedIds.current == null)
|
|
96
112
|
return;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
});
|
|
101
|
-
if (required && item.length === 0) {
|
|
102
|
-
shared_1.DomUtils.setFocus("item", form);
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
setValues(item);
|
|
106
|
-
onValueChange?.(item);
|
|
113
|
+
const ids = tempSelectedIds.current;
|
|
114
|
+
setSelectedIds(ids);
|
|
115
|
+
onValueChange?.(ids);
|
|
107
116
|
},
|
|
108
|
-
inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, ids:
|
|
117
|
+
inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, ids: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
|
|
118
|
+
tempSelectedIds.current = ids;
|
|
119
|
+
} })),
|
|
109
120
|
fullScreen: app.smDown
|
|
110
121
|
});
|
|
111
122
|
};
|
|
112
|
-
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsx)("input", { type: "text", style: { position: "absolute", opacity: 0, width: 0 }, name: inputName, required: required, defaultValue:
|
|
123
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsx)("input", { type: "text", style: { position: "absolute", opacity: 0, width: 0 }, name: inputName, required: required, defaultValue: selectedIds?.join(",") }), (0, jsx_runtime_1.jsxs)(Button_1.default, { variant: variant, sx: sx, onClick: () => clickHandler(), ...rest, disabled: !items || items.length === 0, children: [label && (0, jsx_runtime_1.jsxs)(Typography_1.default, { variant: "body2", children: [label, ":"] }), selectedIds?.map((id) => {
|
|
124
|
+
const item = items.find((item) => item.id === id);
|
|
125
|
+
if (item == null)
|
|
126
|
+
return null;
|
|
127
|
+
return (0, jsx_runtime_1.jsx)(Chip_1.default, { size: "small", label: labelFormatter(item) }, id);
|
|
128
|
+
}), labelEnd && (0, jsx_runtime_1.jsx)(Typography_1.default, { variant: "caption", children: labelEnd })] })] }));
|
|
113
129
|
}
|
|
@@ -2,7 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import Button from "@mui/material/Button";
|
|
3
3
|
import Chip from "@mui/material/Chip";
|
|
4
4
|
import React from "react";
|
|
5
|
-
import { DomUtils } from "@etsoo/shared";
|
|
6
5
|
import FormGroup from "@mui/material/FormGroup";
|
|
7
6
|
import Grid from "@mui/material/Grid";
|
|
8
7
|
import Typography from "@mui/material/Typography";
|
|
@@ -17,25 +16,27 @@ import { HBox, VBox } from "./FlexBox";
|
|
|
17
16
|
import { useRequiredAppContext } from "./app/ReactApp";
|
|
18
17
|
function ButtonPopupList(props) {
|
|
19
18
|
// Destruct
|
|
20
|
-
const { addSplitter = /\s*[,;]\s*/, ids, labelField, labelFormatter
|
|
21
|
-
console.log("data", data);
|
|
22
|
-
if (labelField in data) {
|
|
23
|
-
return data[labelField];
|
|
24
|
-
}
|
|
25
|
-
return data.id.toString();
|
|
26
|
-
}, labels, loadData, onAdd } = props;
|
|
19
|
+
const { addSplitter = /\s*[,;]\s*/, ids = [], items, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
27
20
|
// Methods
|
|
28
21
|
const dndRef = React.createRef();
|
|
29
22
|
// Ref
|
|
30
23
|
const inputRef = React.useRef(null);
|
|
24
|
+
// State
|
|
25
|
+
const [selectedIds, setSelectedIds] = React.useState([]);
|
|
31
26
|
React.useEffect(() => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
27
|
+
// Sort items by ids for first load
|
|
28
|
+
items.sortByProperty("id", ids);
|
|
29
|
+
// Set selected ids
|
|
30
|
+
setSelectedIds([...ids]);
|
|
37
31
|
}, [ids]);
|
|
38
|
-
return (_jsxs(VBox, { gap: 2, children: [_jsx(FormGroup, { children: _jsx(Grid, { container: true, spacing: 0, children: _jsx(DnDList, { items:
|
|
32
|
+
return (_jsxs(VBox, { gap: 2, children: [_jsx(FormGroup, { children: _jsx(Grid, { container: true, spacing: 0, children: _jsx(DnDList, { items: items, labelField: labelField, itemRenderer: (item, index, nodeRef, actionNodeRef) => (_jsxs(Grid, { size: { xs: 12, md: 6, lg: 4 }, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ...nodeRef, children: [_jsx(IconButton, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ...actionNodeRef, children: _jsx(DragIndicatorIcon, {}) }), _jsx(FormControlLabel, { control: _jsx(Checkbox, { name: "item", value: item.id, checked: selectedIds.includes(item.id), onChange: (e) => {
|
|
33
|
+
const checked = e.target.checked;
|
|
34
|
+
const newIds = [
|
|
35
|
+
...selectedIds.toggleItem(item.id, checked)
|
|
36
|
+
];
|
|
37
|
+
setSelectedIds(newIds);
|
|
38
|
+
onValueChange(newIds);
|
|
39
|
+
} }), label: `${index + 1}. ${labelFormatter(item)}` })] })), height: 200, mRef: dndRef }) }) }), onAdd && (_jsxs(HBox, { gap: 1, children: [_jsx(TextField, { variant: "outlined", label: labels?.more, fullWidth: true, inputRef: inputRef }), _jsx(Button, { sx: { width: "120px" }, variant: "contained", startIcon: _jsx(AddIcon, {}), size: "small", onClick: async () => {
|
|
39
40
|
if (inputRef.current == null)
|
|
40
41
|
return;
|
|
41
42
|
const input = inputRef.current.value.trim();
|
|
@@ -65,7 +66,12 @@ export function ButtonPopupCheckbox(props) {
|
|
|
65
66
|
// App
|
|
66
67
|
const app = useRequiredAppContext();
|
|
67
68
|
// Destruct
|
|
68
|
-
const { addSplitter, ids, inputName, label, labelEnd, labelFormatter
|
|
69
|
+
const { addSplitter, ids, inputName, label, labelEnd, labelFormatter = (data) => {
|
|
70
|
+
if (labelField in data) {
|
|
71
|
+
return data[labelField];
|
|
72
|
+
}
|
|
73
|
+
return data.id.toString();
|
|
74
|
+
}, labelField, labels = {}, loadData, onAdd, onValueChange, popupTitle = label, popupMessage, required = false, sx = { gap: 1, justifyContent: "flex-start" }, variant = "outlined", ...rest } = props;
|
|
69
75
|
// Default labels
|
|
70
76
|
if (!labels.add)
|
|
71
77
|
labels.add = app.get("add");
|
|
@@ -74,34 +80,44 @@ export function ButtonPopupCheckbox(props) {
|
|
|
74
80
|
if (!labels.more)
|
|
75
81
|
labels.more = app.get("more");
|
|
76
82
|
// State
|
|
77
|
-
const [
|
|
83
|
+
const [items, setItems] = React.useState([]);
|
|
84
|
+
const [selectedIds, setSelectedIds] = React.useState();
|
|
85
|
+
React.useEffect(() => {
|
|
86
|
+
// Load data
|
|
87
|
+
loadData().then((data) => {
|
|
88
|
+
if (data != null) {
|
|
89
|
+
setItems(data);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}, []);
|
|
78
93
|
React.useEffect(() => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
setValues(ids);
|
|
94
|
+
// Set selected ids
|
|
95
|
+
setSelectedIds(ids);
|
|
82
96
|
}, [ids]);
|
|
97
|
+
// Selected ids
|
|
98
|
+
const tempSelectedIds = React.useRef();
|
|
83
99
|
// Click handler
|
|
84
100
|
const clickHandler = () => {
|
|
85
101
|
app.showInputDialog({
|
|
86
102
|
title: popupTitle,
|
|
87
103
|
message: popupMessage,
|
|
88
104
|
callback: (form) => {
|
|
89
|
-
if (form == null)
|
|
105
|
+
if (form == null || tempSelectedIds.current == null)
|
|
90
106
|
return;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
if (required && item.length === 0) {
|
|
96
|
-
DomUtils.setFocus("item", form);
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
setValues(item);
|
|
100
|
-
onValueChange?.(item);
|
|
107
|
+
const ids = tempSelectedIds.current;
|
|
108
|
+
setSelectedIds(ids);
|
|
109
|
+
onValueChange?.(ids);
|
|
101
110
|
},
|
|
102
|
-
inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, ids:
|
|
111
|
+
inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, ids: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
|
|
112
|
+
tempSelectedIds.current = ids;
|
|
113
|
+
} })),
|
|
103
114
|
fullScreen: app.smDown
|
|
104
115
|
});
|
|
105
116
|
};
|
|
106
|
-
return (_jsxs(React.Fragment, { children: [_jsx("input", { type: "text", style: { position: "absolute", opacity: 0, width: 0 }, name: inputName, required: required, defaultValue:
|
|
117
|
+
return (_jsxs(React.Fragment, { children: [_jsx("input", { type: "text", style: { position: "absolute", opacity: 0, width: 0 }, name: inputName, required: required, defaultValue: selectedIds?.join(",") }), _jsxs(Button, { variant: variant, sx: sx, onClick: () => clickHandler(), ...rest, disabled: !items || items.length === 0, children: [label && _jsxs(Typography, { variant: "body2", children: [label, ":"] }), selectedIds?.map((id) => {
|
|
118
|
+
const item = items.find((item) => item.id === id);
|
|
119
|
+
if (item == null)
|
|
120
|
+
return null;
|
|
121
|
+
return _jsx(Chip, { size: "small", label: labelFormatter(item) }, id);
|
|
122
|
+
}), labelEnd && _jsx(Typography, { variant: "caption", children: labelEnd })] })] }));
|
|
107
123
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.50",
|
|
4
4
|
"description": "TypeScript Material-UI Implementation",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"module": "lib/mjs/index.js",
|
|
@@ -40,16 +40,16 @@
|
|
|
40
40
|
"@dnd-kit/sortable": "^10.0.0",
|
|
41
41
|
"@emotion/react": "^11.14.0",
|
|
42
42
|
"@emotion/styled": "^11.14.0",
|
|
43
|
-
"@etsoo/appscript": "^1.6.
|
|
44
|
-
"@etsoo/notificationbase": "^1.1.
|
|
45
|
-
"@etsoo/react": "^1.8.
|
|
46
|
-
"@etsoo/shared": "^1.2.
|
|
43
|
+
"@etsoo/appscript": "^1.6.36",
|
|
44
|
+
"@etsoo/notificationbase": "^1.1.62",
|
|
45
|
+
"@etsoo/react": "^1.8.45",
|
|
46
|
+
"@etsoo/shared": "^1.2.74",
|
|
47
47
|
"@mui/icons-material": "^7.1.0",
|
|
48
48
|
"@mui/material": "^7.1.0",
|
|
49
|
-
"@mui/x-data-grid": "^8.
|
|
49
|
+
"@mui/x-data-grid": "^8.4.0",
|
|
50
50
|
"chart.js": "^4.4.9",
|
|
51
51
|
"chartjs-plugin-datalabels": "^2.2.0",
|
|
52
|
-
"dompurify": "^3.2.
|
|
52
|
+
"dompurify": "^3.2.6",
|
|
53
53
|
"eventemitter3": "^5.0.1",
|
|
54
54
|
"pica": "^9.0.1",
|
|
55
55
|
"pulltorefreshjs": "^0.1.22",
|
|
@@ -76,14 +76,14 @@
|
|
|
76
76
|
"@testing-library/react": "^16.3.0",
|
|
77
77
|
"@types/pica": "^9.0.5",
|
|
78
78
|
"@types/pulltorefreshjs": "^0.1.7",
|
|
79
|
-
"@types/react": "^18.3.
|
|
79
|
+
"@types/react": "^18.3.22",
|
|
80
80
|
"@types/react-avatar-editor": "^13.0.4",
|
|
81
81
|
"@types/react-dom": "^18.3.7",
|
|
82
82
|
"@types/react-input-mask": "^3.0.6",
|
|
83
83
|
"@types/react-window": "^1.8.8",
|
|
84
|
-
"@vitejs/plugin-react": "^4.
|
|
84
|
+
"@vitejs/plugin-react": "^4.5.0",
|
|
85
85
|
"jsdom": "^26.1.0",
|
|
86
86
|
"typescript": "^5.8.3",
|
|
87
|
-
"vitest": "^3.1.
|
|
87
|
+
"vitest": "^3.1.4"
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Button, { ButtonProps } from "@mui/material/Button";
|
|
2
2
|
import Chip from "@mui/material/Chip";
|
|
3
3
|
import React from "react";
|
|
4
|
-
import { DataTypes,
|
|
4
|
+
import { DataTypes, IdType } from "@etsoo/shared";
|
|
5
5
|
import FormGroup from "@mui/material/FormGroup";
|
|
6
6
|
import Grid from "@mui/material/Grid";
|
|
7
7
|
import Typography from "@mui/material/Typography";
|
|
@@ -103,14 +103,20 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<
|
|
|
103
103
|
|
|
104
104
|
type ButtonPopupListProps<D extends DnDItemType> = Pick<
|
|
105
105
|
ButtonPopupCheckboxProps<D>,
|
|
106
|
-
| "
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
"addSplitter" | "labelField" | "labels" | "onAdd" | "ids"
|
|
107
|
+
> &
|
|
108
|
+
Required<Pick<ButtonPopupCheckboxProps<D>, "labelFormatter">> & {
|
|
109
|
+
/**
|
|
110
|
+
* Items to be displayed
|
|
111
|
+
*/
|
|
112
|
+
items: D[];
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* On value change handler
|
|
116
|
+
* @param ids Ids
|
|
117
|
+
*/
|
|
118
|
+
onValueChange: (ids: D["id"][]) => void;
|
|
119
|
+
};
|
|
114
120
|
|
|
115
121
|
function ButtonPopupList<D extends DnDItemType>(
|
|
116
122
|
props: ButtonPopupListProps<D>
|
|
@@ -118,19 +124,13 @@ function ButtonPopupList<D extends DnDItemType>(
|
|
|
118
124
|
// Destruct
|
|
119
125
|
const {
|
|
120
126
|
addSplitter = /\s*[,;]\s*/,
|
|
121
|
-
ids,
|
|
127
|
+
ids = [],
|
|
128
|
+
items,
|
|
122
129
|
labelField,
|
|
123
|
-
labelFormatter
|
|
124
|
-
console.log("data", data);
|
|
125
|
-
if (labelField in data) {
|
|
126
|
-
return data[labelField] as string;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return data.id.toString();
|
|
130
|
-
},
|
|
130
|
+
labelFormatter,
|
|
131
131
|
labels,
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
onAdd,
|
|
133
|
+
onValueChange
|
|
134
134
|
} = props;
|
|
135
135
|
|
|
136
136
|
// Methods
|
|
@@ -139,11 +139,15 @@ function ButtonPopupList<D extends DnDItemType>(
|
|
|
139
139
|
// Ref
|
|
140
140
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
141
141
|
|
|
142
|
+
// State
|
|
143
|
+
const [selectedIds, setSelectedIds] = React.useState<D["id"][]>([]);
|
|
144
|
+
|
|
142
145
|
React.useEffect(() => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
// Sort items by ids for first load
|
|
147
|
+
items.sortByProperty("id", ids);
|
|
148
|
+
|
|
149
|
+
// Set selected ids
|
|
150
|
+
setSelectedIds([...ids]);
|
|
147
151
|
}, [ids]);
|
|
148
152
|
|
|
149
153
|
return (
|
|
@@ -151,7 +155,7 @@ function ButtonPopupList<D extends DnDItemType>(
|
|
|
151
155
|
<FormGroup>
|
|
152
156
|
<Grid container spacing={0}>
|
|
153
157
|
<DnDList<D>
|
|
154
|
-
items={
|
|
158
|
+
items={items}
|
|
155
159
|
labelField={labelField}
|
|
156
160
|
itemRenderer={(item, index, nodeRef, actionNodeRef) => (
|
|
157
161
|
<Grid
|
|
@@ -175,7 +179,15 @@ function ButtonPopupList<D extends DnDItemType>(
|
|
|
175
179
|
<Checkbox
|
|
176
180
|
name="item"
|
|
177
181
|
value={item.id}
|
|
178
|
-
|
|
182
|
+
checked={selectedIds.includes(item.id)}
|
|
183
|
+
onChange={(e) => {
|
|
184
|
+
const checked = e.target.checked;
|
|
185
|
+
const newIds = [
|
|
186
|
+
...selectedIds.toggleItem(item.id, checked)
|
|
187
|
+
];
|
|
188
|
+
setSelectedIds(newIds);
|
|
189
|
+
onValueChange(newIds);
|
|
190
|
+
}}
|
|
179
191
|
/>
|
|
180
192
|
}
|
|
181
193
|
label={`${index + 1}. ${labelFormatter(item)}`}
|
|
@@ -253,7 +265,13 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
|
|
|
253
265
|
inputName,
|
|
254
266
|
label,
|
|
255
267
|
labelEnd,
|
|
256
|
-
labelFormatter
|
|
268
|
+
labelFormatter = (data) => {
|
|
269
|
+
if (labelField in data) {
|
|
270
|
+
return data[labelField] as string;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return data.id.toString();
|
|
274
|
+
},
|
|
257
275
|
labelField,
|
|
258
276
|
labels = {},
|
|
259
277
|
loadData,
|
|
@@ -273,43 +291,49 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
|
|
|
273
291
|
if (!labels.more) labels.more = app.get("more");
|
|
274
292
|
|
|
275
293
|
// State
|
|
276
|
-
const [
|
|
294
|
+
const [items, setItems] = React.useState<D[]>([]);
|
|
295
|
+
const [selectedIds, setSelectedIds] = React.useState<D["id"][]>();
|
|
296
|
+
|
|
277
297
|
React.useEffect(() => {
|
|
278
|
-
|
|
279
|
-
|
|
298
|
+
// Load data
|
|
299
|
+
loadData().then((data) => {
|
|
300
|
+
if (data != null) {
|
|
301
|
+
setItems(data);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}, []);
|
|
305
|
+
|
|
306
|
+
React.useEffect(() => {
|
|
307
|
+
// Set selected ids
|
|
308
|
+
setSelectedIds(ids);
|
|
280
309
|
}, [ids]);
|
|
281
310
|
|
|
311
|
+
// Selected ids
|
|
312
|
+
const tempSelectedIds = React.useRef<D["id"][]>();
|
|
313
|
+
|
|
282
314
|
// Click handler
|
|
283
315
|
const clickHandler = () => {
|
|
284
316
|
app.showInputDialog({
|
|
285
317
|
title: popupTitle,
|
|
286
318
|
message: popupMessage,
|
|
287
319
|
callback: (form) => {
|
|
288
|
-
if (form == null) return;
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
item: "string[]"
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
if (required && item.length === 0) {
|
|
296
|
-
DomUtils.setFocus("item", form);
|
|
297
|
-
return false;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
setValues(item);
|
|
301
|
-
|
|
302
|
-
onValueChange?.(item);
|
|
320
|
+
if (form == null || tempSelectedIds.current == null) return;
|
|
321
|
+
const ids = tempSelectedIds.current;
|
|
322
|
+
setSelectedIds(ids);
|
|
323
|
+
onValueChange?.(ids);
|
|
303
324
|
},
|
|
304
325
|
inputs: (
|
|
305
326
|
<ButtonPopupList
|
|
306
327
|
addSplitter={addSplitter}
|
|
307
|
-
ids={
|
|
328
|
+
ids={selectedIds}
|
|
329
|
+
items={items}
|
|
308
330
|
labelFormatter={labelFormatter}
|
|
309
331
|
labelField={labelField}
|
|
310
332
|
labels={labels}
|
|
311
|
-
loadData={loadData}
|
|
312
333
|
onAdd={onAdd}
|
|
334
|
+
onValueChange={(ids) => {
|
|
335
|
+
tempSelectedIds.current = ids;
|
|
336
|
+
}}
|
|
313
337
|
/>
|
|
314
338
|
),
|
|
315
339
|
fullScreen: app.smDown
|
|
@@ -323,18 +347,22 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
|
|
|
323
347
|
style={{ position: "absolute", opacity: 0, width: 0 }}
|
|
324
348
|
name={inputName}
|
|
325
349
|
required={required}
|
|
326
|
-
defaultValue={
|
|
350
|
+
defaultValue={selectedIds?.join(",")}
|
|
327
351
|
/>
|
|
328
352
|
<Button
|
|
329
353
|
variant={variant}
|
|
330
354
|
sx={sx}
|
|
331
355
|
onClick={() => clickHandler()}
|
|
332
356
|
{...rest}
|
|
357
|
+
disabled={!items || items.length === 0}
|
|
333
358
|
>
|
|
334
359
|
{label && <Typography variant="body2">{label}:</Typography>}
|
|
335
|
-
{
|
|
336
|
-
|
|
337
|
-
|
|
360
|
+
{selectedIds?.map((id) => {
|
|
361
|
+
const item = items.find((item) => item.id === id);
|
|
362
|
+
if (item == null) return null;
|
|
363
|
+
|
|
364
|
+
return <Chip key={id} size="small" label={labelFormatter(item)} />;
|
|
365
|
+
})}
|
|
338
366
|
{labelEnd && <Typography variant="caption">{labelEnd}</Typography>}
|
|
339
367
|
</Button>
|
|
340
368
|
</React.Fragment>
|