@etsoo/materialui 1.5.47 → 1.5.49
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 +51 -32
- package/lib/cjs/pages/ViewPage.d.ts +9 -0
- package/lib/cjs/pages/ViewPage.js +11 -1
- package/lib/mjs/ButtonPopupCheckbox.js +51 -32
- package/lib/mjs/pages/ViewPage.d.ts +9 -0
- package/lib/mjs/pages/ViewPage.js +10 -1
- package/package.json +10 -10
- package/src/ButtonPopupCheckbox.tsx +82 -51
- package/src/pages/ViewPage.tsx +31 -13
|
@@ -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,30 @@ 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, setSelectedIdsBase] = react_1.default.useState([]);
|
|
32
|
+
// Sort items
|
|
33
|
+
const setSelectedIds = (ids) => {
|
|
34
|
+
items.sortByProperty("id", ids);
|
|
35
|
+
setSelectedIdsBase(ids);
|
|
36
|
+
};
|
|
37
37
|
react_1.default.useEffect(() => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return;
|
|
41
|
-
dndRef.current.addItems(data);
|
|
42
|
-
});
|
|
38
|
+
// Set selected ids
|
|
39
|
+
setSelectedIds([...ids]);
|
|
43
40
|
}, [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:
|
|
41
|
+
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) => {
|
|
42
|
+
const checked = e.target.checked;
|
|
43
|
+
const newIds = [
|
|
44
|
+
...selectedIds.toggleItem(item.id, checked)
|
|
45
|
+
];
|
|
46
|
+
setSelectedIds(newIds);
|
|
47
|
+
onValueChange(newIds);
|
|
48
|
+
} }), 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
49
|
if (inputRef.current == null)
|
|
46
50
|
return;
|
|
47
51
|
const input = inputRef.current.value.trim();
|
|
@@ -71,7 +75,12 @@ function ButtonPopupCheckbox(props) {
|
|
|
71
75
|
// App
|
|
72
76
|
const app = (0, ReactApp_1.useRequiredAppContext)();
|
|
73
77
|
// Destruct
|
|
74
|
-
const { addSplitter, ids, inputName, label, labelEnd, labelFormatter
|
|
78
|
+
const { addSplitter, ids, inputName, label, labelEnd, labelFormatter = (data) => {
|
|
79
|
+
if (labelField in data) {
|
|
80
|
+
return data[labelField];
|
|
81
|
+
}
|
|
82
|
+
return data.id.toString();
|
|
83
|
+
}, labelField, labels = {}, loadData, onAdd, onValueChange, popupTitle = label, popupMessage, required = false, sx = { gap: 1, justifyContent: "flex-start" }, variant = "outlined", ...rest } = props;
|
|
75
84
|
// Default labels
|
|
76
85
|
if (!labels.add)
|
|
77
86
|
labels.add = app.get("add");
|
|
@@ -80,34 +89,44 @@ function ButtonPopupCheckbox(props) {
|
|
|
80
89
|
if (!labels.more)
|
|
81
90
|
labels.more = app.get("more");
|
|
82
91
|
// State
|
|
83
|
-
const [
|
|
92
|
+
const [items, setItems] = react_1.default.useState([]);
|
|
93
|
+
const [selectedIds, setSelectedIds] = react_1.default.useState();
|
|
94
|
+
react_1.default.useEffect(() => {
|
|
95
|
+
// Load data
|
|
96
|
+
loadData().then((data) => {
|
|
97
|
+
if (data != null) {
|
|
98
|
+
setItems(data);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}, []);
|
|
84
102
|
react_1.default.useEffect(() => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
setValues(ids);
|
|
103
|
+
// Set selected ids
|
|
104
|
+
setSelectedIds(ids);
|
|
88
105
|
}, [ids]);
|
|
106
|
+
// Selected ids
|
|
107
|
+
const tempSelectedIds = react_1.default.useRef();
|
|
89
108
|
// Click handler
|
|
90
109
|
const clickHandler = () => {
|
|
91
110
|
app.showInputDialog({
|
|
92
111
|
title: popupTitle,
|
|
93
112
|
message: popupMessage,
|
|
94
113
|
callback: (form) => {
|
|
95
|
-
if (form == null)
|
|
114
|
+
if (form == null || tempSelectedIds.current == null)
|
|
96
115
|
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);
|
|
116
|
+
const ids = tempSelectedIds.current;
|
|
117
|
+
setSelectedIds(ids);
|
|
118
|
+
onValueChange?.(ids);
|
|
107
119
|
},
|
|
108
|
-
inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, ids:
|
|
120
|
+
inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, ids: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
|
|
121
|
+
tempSelectedIds.current = ids;
|
|
122
|
+
} })),
|
|
109
123
|
fullScreen: app.smDown
|
|
110
124
|
});
|
|
111
125
|
};
|
|
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:
|
|
126
|
+
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) => {
|
|
127
|
+
const item = items.find((item) => item.id === id);
|
|
128
|
+
if (item == null)
|
|
129
|
+
return null;
|
|
130
|
+
return (0, jsx_runtime_1.jsx)(Chip_1.default, { size: "small", label: labelFormatter(item) }, id);
|
|
131
|
+
}), labelEnd && (0, jsx_runtime_1.jsx)(Typography_1.default, { variant: "caption", children: labelEnd })] })] }));
|
|
113
132
|
}
|
|
@@ -3,6 +3,15 @@ import React from "react";
|
|
|
3
3
|
import { CommonPageProps } from "./CommonPage";
|
|
4
4
|
import type { OperationMessageHandlerAll } from "../messages/OperationMessageHandler";
|
|
5
5
|
import { ViewContainerProps } from "../ViewContainer";
|
|
6
|
+
import { StackProps } from "@mui/material/Stack";
|
|
7
|
+
/**
|
|
8
|
+
* View page action bar
|
|
9
|
+
* @param props Props
|
|
10
|
+
* @returns Component
|
|
11
|
+
*/
|
|
12
|
+
export declare function ViewPageActionBar(props: StackProps & {
|
|
13
|
+
actionPaddings?: number | Record<string, string | number>;
|
|
14
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
6
15
|
/**
|
|
7
16
|
* View page props
|
|
8
17
|
*/
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ViewPageActionBar = ViewPageActionBar;
|
|
6
7
|
exports.ViewPage = ViewPage;
|
|
7
8
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
9
|
const react_1 = require("@etsoo/react");
|
|
@@ -17,6 +18,15 @@ const OperationMessageContainer_1 = require("../messages/OperationMessageContain
|
|
|
17
18
|
const ViewContainer_1 = require("../ViewContainer");
|
|
18
19
|
const LinearProgress_1 = __importDefault(require("@mui/material/LinearProgress"));
|
|
19
20
|
const Stack_1 = __importDefault(require("@mui/material/Stack"));
|
|
21
|
+
/**
|
|
22
|
+
* View page action bar
|
|
23
|
+
* @param props Props
|
|
24
|
+
* @returns Component
|
|
25
|
+
*/
|
|
26
|
+
function ViewPageActionBar(props) {
|
|
27
|
+
const { actionPaddings = MUGlobal_1.MUGlobal.pagePaddings, ...rest } = props;
|
|
28
|
+
return ((0, jsx_runtime_1.jsx)(Stack_1.default, { className: "ET-ViewPage-Actions", direction: "row", width: "100%", flexWrap: "wrap", justifyContent: "center", paddingTop: actionPaddings, paddingBottom: actionPaddings, gap: actionPaddings, ...rest }));
|
|
29
|
+
}
|
|
20
30
|
/**
|
|
21
31
|
* View page
|
|
22
32
|
* @param props Props
|
|
@@ -51,7 +61,7 @@ function ViewPage(props) {
|
|
|
51
61
|
refresh,
|
|
52
62
|
operationMessageHandler.id
|
|
53
63
|
]
|
|
54
|
-
: operationMessageHandler })), titleBar && titleBar(data), (0, jsx_runtime_1.jsx)(ViewContainer_1.ViewContainer, { data: data, fields: fields, gridRef: gridRef, leftContainer: leftContainer, leftContainerLines: leftContainerLines, leftContainerProps: leftContainerProps, refresh: refresh, spacing: spacing }), actions !== null && ((0, jsx_runtime_1.jsx)(
|
|
64
|
+
: operationMessageHandler })), titleBar && titleBar(data), (0, jsx_runtime_1.jsx)(ViewContainer_1.ViewContainer, { data: data, fields: fields, gridRef: gridRef, leftContainer: leftContainer, leftContainerLines: leftContainerLines, leftContainerProps: leftContainerProps, refresh: refresh, spacing: spacing }), actions !== null && ((0, jsx_runtime_1.jsx)(ViewPageActionBar, { actionPaddings: actionPaddings, children: shared_1.Utils.getResult(actions, data, refresh) })), shared_1.Utils.getResult(children, data, refresh), pullToRefresh && ((0, jsx_runtime_1.jsx)(PullToRefreshUI_1.PullToRefreshUI, { mainElement: pullContainer, triggerElement: pullContainer, instructionsPullToRefresh: labels.pullToRefresh, instructionsReleaseToRefresh: labels.releaseToRefresh, instructionsRefreshing: labels.refreshing, onRefresh: refresh, shouldPullToRefresh: () => {
|
|
55
65
|
const container = document.querySelector(pullContainer);
|
|
56
66
|
return !container?.scrollTop;
|
|
57
67
|
} })), (0, jsx_runtime_1.jsx)(react_1.ScrollRestoration, {})] })) }));
|
|
@@ -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,30 @@ 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, setSelectedIdsBase] = React.useState([]);
|
|
26
|
+
// Sort items
|
|
27
|
+
const setSelectedIds = (ids) => {
|
|
28
|
+
items.sortByProperty("id", ids);
|
|
29
|
+
setSelectedIdsBase(ids);
|
|
30
|
+
};
|
|
31
31
|
React.useEffect(() => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return;
|
|
35
|
-
dndRef.current.addItems(data);
|
|
36
|
-
});
|
|
32
|
+
// Set selected ids
|
|
33
|
+
setSelectedIds([...ids]);
|
|
37
34
|
}, [ids]);
|
|
38
|
-
return (_jsxs(VBox, { gap: 2, children: [_jsx(FormGroup, { children: _jsx(Grid, { container: true, spacing: 0, children: _jsx(DnDList, { items:
|
|
35
|
+
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) => {
|
|
36
|
+
const checked = e.target.checked;
|
|
37
|
+
const newIds = [
|
|
38
|
+
...selectedIds.toggleItem(item.id, checked)
|
|
39
|
+
];
|
|
40
|
+
setSelectedIds(newIds);
|
|
41
|
+
onValueChange(newIds);
|
|
42
|
+
} }), 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
43
|
if (inputRef.current == null)
|
|
40
44
|
return;
|
|
41
45
|
const input = inputRef.current.value.trim();
|
|
@@ -65,7 +69,12 @@ export function ButtonPopupCheckbox(props) {
|
|
|
65
69
|
// App
|
|
66
70
|
const app = useRequiredAppContext();
|
|
67
71
|
// Destruct
|
|
68
|
-
const { addSplitter, ids, inputName, label, labelEnd, labelFormatter
|
|
72
|
+
const { addSplitter, ids, inputName, label, labelEnd, labelFormatter = (data) => {
|
|
73
|
+
if (labelField in data) {
|
|
74
|
+
return data[labelField];
|
|
75
|
+
}
|
|
76
|
+
return data.id.toString();
|
|
77
|
+
}, labelField, labels = {}, loadData, onAdd, onValueChange, popupTitle = label, popupMessage, required = false, sx = { gap: 1, justifyContent: "flex-start" }, variant = "outlined", ...rest } = props;
|
|
69
78
|
// Default labels
|
|
70
79
|
if (!labels.add)
|
|
71
80
|
labels.add = app.get("add");
|
|
@@ -74,34 +83,44 @@ export function ButtonPopupCheckbox(props) {
|
|
|
74
83
|
if (!labels.more)
|
|
75
84
|
labels.more = app.get("more");
|
|
76
85
|
// State
|
|
77
|
-
const [
|
|
86
|
+
const [items, setItems] = React.useState([]);
|
|
87
|
+
const [selectedIds, setSelectedIds] = React.useState();
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
// Load data
|
|
90
|
+
loadData().then((data) => {
|
|
91
|
+
if (data != null) {
|
|
92
|
+
setItems(data);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}, []);
|
|
78
96
|
React.useEffect(() => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
setValues(ids);
|
|
97
|
+
// Set selected ids
|
|
98
|
+
setSelectedIds(ids);
|
|
82
99
|
}, [ids]);
|
|
100
|
+
// Selected ids
|
|
101
|
+
const tempSelectedIds = React.useRef();
|
|
83
102
|
// Click handler
|
|
84
103
|
const clickHandler = () => {
|
|
85
104
|
app.showInputDialog({
|
|
86
105
|
title: popupTitle,
|
|
87
106
|
message: popupMessage,
|
|
88
107
|
callback: (form) => {
|
|
89
|
-
if (form == null)
|
|
108
|
+
if (form == null || tempSelectedIds.current == null)
|
|
90
109
|
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);
|
|
110
|
+
const ids = tempSelectedIds.current;
|
|
111
|
+
setSelectedIds(ids);
|
|
112
|
+
onValueChange?.(ids);
|
|
101
113
|
},
|
|
102
|
-
inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, ids:
|
|
114
|
+
inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, ids: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
|
|
115
|
+
tempSelectedIds.current = ids;
|
|
116
|
+
} })),
|
|
103
117
|
fullScreen: app.smDown
|
|
104
118
|
});
|
|
105
119
|
};
|
|
106
|
-
return (_jsxs(React.Fragment, { children: [_jsx("input", { type: "text", style: { position: "absolute", opacity: 0, width: 0 }, name: inputName, required: required, defaultValue:
|
|
120
|
+
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) => {
|
|
121
|
+
const item = items.find((item) => item.id === id);
|
|
122
|
+
if (item == null)
|
|
123
|
+
return null;
|
|
124
|
+
return _jsx(Chip, { size: "small", label: labelFormatter(item) }, id);
|
|
125
|
+
}), labelEnd && _jsx(Typography, { variant: "caption", children: labelEnd })] })] }));
|
|
107
126
|
}
|
|
@@ -3,6 +3,15 @@ import React from "react";
|
|
|
3
3
|
import { CommonPageProps } from "./CommonPage";
|
|
4
4
|
import type { OperationMessageHandlerAll } from "../messages/OperationMessageHandler";
|
|
5
5
|
import { ViewContainerProps } from "../ViewContainer";
|
|
6
|
+
import { StackProps } from "@mui/material/Stack";
|
|
7
|
+
/**
|
|
8
|
+
* View page action bar
|
|
9
|
+
* @param props Props
|
|
10
|
+
* @returns Component
|
|
11
|
+
*/
|
|
12
|
+
export declare function ViewPageActionBar(props: StackProps & {
|
|
13
|
+
actionPaddings?: number | Record<string, string | number>;
|
|
14
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
6
15
|
/**
|
|
7
16
|
* View page props
|
|
8
17
|
*/
|
|
@@ -11,6 +11,15 @@ import { OperationMessageContainer } from "../messages/OperationMessageContainer
|
|
|
11
11
|
import { ViewContainer } from "../ViewContainer";
|
|
12
12
|
import LinearProgress from "@mui/material/LinearProgress";
|
|
13
13
|
import Stack from "@mui/material/Stack";
|
|
14
|
+
/**
|
|
15
|
+
* View page action bar
|
|
16
|
+
* @param props Props
|
|
17
|
+
* @returns Component
|
|
18
|
+
*/
|
|
19
|
+
export function ViewPageActionBar(props) {
|
|
20
|
+
const { actionPaddings = MUGlobal.pagePaddings, ...rest } = props;
|
|
21
|
+
return (_jsx(Stack, { className: "ET-ViewPage-Actions", direction: "row", width: "100%", flexWrap: "wrap", justifyContent: "center", paddingTop: actionPaddings, paddingBottom: actionPaddings, gap: actionPaddings, ...rest }));
|
|
22
|
+
}
|
|
14
23
|
/**
|
|
15
24
|
* View page
|
|
16
25
|
* @param props Props
|
|
@@ -45,7 +54,7 @@ export function ViewPage(props) {
|
|
|
45
54
|
refresh,
|
|
46
55
|
operationMessageHandler.id
|
|
47
56
|
]
|
|
48
|
-
: operationMessageHandler })), titleBar && titleBar(data), _jsx(ViewContainer, { data: data, fields: fields, gridRef: gridRef, leftContainer: leftContainer, leftContainerLines: leftContainerLines, leftContainerProps: leftContainerProps, refresh: refresh, spacing: spacing }), actions !== null && (_jsx(
|
|
57
|
+
: operationMessageHandler })), titleBar && titleBar(data), _jsx(ViewContainer, { data: data, fields: fields, gridRef: gridRef, leftContainer: leftContainer, leftContainerLines: leftContainerLines, leftContainerProps: leftContainerProps, refresh: refresh, spacing: spacing }), actions !== null && (_jsx(ViewPageActionBar, { actionPaddings: actionPaddings, children: Utils.getResult(actions, data, refresh) })), Utils.getResult(children, data, refresh), pullToRefresh && (_jsx(PullToRefreshUI, { mainElement: pullContainer, triggerElement: pullContainer, instructionsPullToRefresh: labels.pullToRefresh, instructionsReleaseToRefresh: labels.releaseToRefresh, instructionsRefreshing: labels.refreshing, onRefresh: refresh, shouldPullToRefresh: () => {
|
|
49
58
|
const container = document.querySelector(pullContainer);
|
|
50
59
|
return !container?.scrollTop;
|
|
51
60
|
} })), _jsx(ScrollRestoration, {})] })) }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.49",
|
|
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,18 @@ function ButtonPopupList<D extends DnDItemType>(
|
|
|
139
139
|
// Ref
|
|
140
140
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
141
141
|
|
|
142
|
+
// State
|
|
143
|
+
const [selectedIds, setSelectedIdsBase] = React.useState<D["id"][]>([]);
|
|
144
|
+
|
|
145
|
+
// Sort items
|
|
146
|
+
const setSelectedIds = (ids: D["id"][]) => {
|
|
147
|
+
items.sortByProperty("id", ids);
|
|
148
|
+
setSelectedIdsBase(ids);
|
|
149
|
+
};
|
|
150
|
+
|
|
142
151
|
React.useEffect(() => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
dndRef.current.addItems(data);
|
|
146
|
-
});
|
|
152
|
+
// Set selected ids
|
|
153
|
+
setSelectedIds([...ids]);
|
|
147
154
|
}, [ids]);
|
|
148
155
|
|
|
149
156
|
return (
|
|
@@ -151,7 +158,7 @@ function ButtonPopupList<D extends DnDItemType>(
|
|
|
151
158
|
<FormGroup>
|
|
152
159
|
<Grid container spacing={0}>
|
|
153
160
|
<DnDList<D>
|
|
154
|
-
items={
|
|
161
|
+
items={items}
|
|
155
162
|
labelField={labelField}
|
|
156
163
|
itemRenderer={(item, index, nodeRef, actionNodeRef) => (
|
|
157
164
|
<Grid
|
|
@@ -175,7 +182,15 @@ function ButtonPopupList<D extends DnDItemType>(
|
|
|
175
182
|
<Checkbox
|
|
176
183
|
name="item"
|
|
177
184
|
value={item.id}
|
|
178
|
-
|
|
185
|
+
checked={selectedIds.includes(item.id)}
|
|
186
|
+
onChange={(e) => {
|
|
187
|
+
const checked = e.target.checked;
|
|
188
|
+
const newIds = [
|
|
189
|
+
...selectedIds.toggleItem(item.id, checked)
|
|
190
|
+
];
|
|
191
|
+
setSelectedIds(newIds);
|
|
192
|
+
onValueChange(newIds);
|
|
193
|
+
}}
|
|
179
194
|
/>
|
|
180
195
|
}
|
|
181
196
|
label={`${index + 1}. ${labelFormatter(item)}`}
|
|
@@ -253,7 +268,13 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
|
|
|
253
268
|
inputName,
|
|
254
269
|
label,
|
|
255
270
|
labelEnd,
|
|
256
|
-
labelFormatter
|
|
271
|
+
labelFormatter = (data) => {
|
|
272
|
+
if (labelField in data) {
|
|
273
|
+
return data[labelField] as string;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return data.id.toString();
|
|
277
|
+
},
|
|
257
278
|
labelField,
|
|
258
279
|
labels = {},
|
|
259
280
|
loadData,
|
|
@@ -273,43 +294,49 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
|
|
|
273
294
|
if (!labels.more) labels.more = app.get("more");
|
|
274
295
|
|
|
275
296
|
// State
|
|
276
|
-
const [
|
|
297
|
+
const [items, setItems] = React.useState<D[]>([]);
|
|
298
|
+
const [selectedIds, setSelectedIds] = React.useState<D["id"][]>();
|
|
299
|
+
|
|
300
|
+
React.useEffect(() => {
|
|
301
|
+
// Load data
|
|
302
|
+
loadData().then((data) => {
|
|
303
|
+
if (data != null) {
|
|
304
|
+
setItems(data);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}, []);
|
|
308
|
+
|
|
277
309
|
React.useEffect(() => {
|
|
278
|
-
|
|
279
|
-
|
|
310
|
+
// Set selected ids
|
|
311
|
+
setSelectedIds(ids);
|
|
280
312
|
}, [ids]);
|
|
281
313
|
|
|
314
|
+
// Selected ids
|
|
315
|
+
const tempSelectedIds = React.useRef<D["id"][]>();
|
|
316
|
+
|
|
282
317
|
// Click handler
|
|
283
318
|
const clickHandler = () => {
|
|
284
319
|
app.showInputDialog({
|
|
285
320
|
title: popupTitle,
|
|
286
321
|
message: popupMessage,
|
|
287
322
|
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);
|
|
323
|
+
if (form == null || tempSelectedIds.current == null) return;
|
|
324
|
+
const ids = tempSelectedIds.current;
|
|
325
|
+
setSelectedIds(ids);
|
|
326
|
+
onValueChange?.(ids);
|
|
303
327
|
},
|
|
304
328
|
inputs: (
|
|
305
329
|
<ButtonPopupList
|
|
306
330
|
addSplitter={addSplitter}
|
|
307
|
-
ids={
|
|
331
|
+
ids={selectedIds}
|
|
332
|
+
items={items}
|
|
308
333
|
labelFormatter={labelFormatter}
|
|
309
334
|
labelField={labelField}
|
|
310
335
|
labels={labels}
|
|
311
|
-
loadData={loadData}
|
|
312
336
|
onAdd={onAdd}
|
|
337
|
+
onValueChange={(ids) => {
|
|
338
|
+
tempSelectedIds.current = ids;
|
|
339
|
+
}}
|
|
313
340
|
/>
|
|
314
341
|
),
|
|
315
342
|
fullScreen: app.smDown
|
|
@@ -323,18 +350,22 @@ export function ButtonPopupCheckbox<D extends DnDItemType>(
|
|
|
323
350
|
style={{ position: "absolute", opacity: 0, width: 0 }}
|
|
324
351
|
name={inputName}
|
|
325
352
|
required={required}
|
|
326
|
-
defaultValue={
|
|
353
|
+
defaultValue={selectedIds?.join(",")}
|
|
327
354
|
/>
|
|
328
355
|
<Button
|
|
329
356
|
variant={variant}
|
|
330
357
|
sx={sx}
|
|
331
358
|
onClick={() => clickHandler()}
|
|
332
359
|
{...rest}
|
|
360
|
+
disabled={!items || items.length === 0}
|
|
333
361
|
>
|
|
334
362
|
{label && <Typography variant="body2">{label}:</Typography>}
|
|
335
|
-
{
|
|
336
|
-
|
|
337
|
-
|
|
363
|
+
{selectedIds?.map((id) => {
|
|
364
|
+
const item = items.find((item) => item.id === id);
|
|
365
|
+
if (item == null) return null;
|
|
366
|
+
|
|
367
|
+
return <Chip key={id} size="small" label={labelFormatter(item)} />;
|
|
368
|
+
})}
|
|
338
369
|
{labelEnd && <Typography variant="caption">{labelEnd}</Typography>}
|
|
339
370
|
</Button>
|
|
340
371
|
</React.Fragment>
|
package/src/pages/ViewPage.tsx
CHANGED
|
@@ -11,7 +11,34 @@ import type { RefreshHandler } from "../messages/RefreshHandler";
|
|
|
11
11
|
import { OperationMessageContainer } from "../messages/OperationMessageContainer";
|
|
12
12
|
import { ViewContainer, ViewContainerProps } from "../ViewContainer";
|
|
13
13
|
import LinearProgress from "@mui/material/LinearProgress";
|
|
14
|
-
import Stack from "@mui/material/Stack";
|
|
14
|
+
import Stack, { StackProps } from "@mui/material/Stack";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* View page action bar
|
|
18
|
+
* @param props Props
|
|
19
|
+
* @returns Component
|
|
20
|
+
*/
|
|
21
|
+
export function ViewPageActionBar(
|
|
22
|
+
props: StackProps & {
|
|
23
|
+
actionPaddings?: number | Record<string, string | number>;
|
|
24
|
+
}
|
|
25
|
+
) {
|
|
26
|
+
const { actionPaddings = MUGlobal.pagePaddings, ...rest } = props;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Stack
|
|
30
|
+
className="ET-ViewPage-Actions"
|
|
31
|
+
direction="row"
|
|
32
|
+
width="100%"
|
|
33
|
+
flexWrap="wrap"
|
|
34
|
+
justifyContent="center"
|
|
35
|
+
paddingTop={actionPaddings}
|
|
36
|
+
paddingBottom={actionPaddings}
|
|
37
|
+
gap={actionPaddings}
|
|
38
|
+
{...rest}
|
|
39
|
+
></Stack>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
15
42
|
|
|
16
43
|
/**
|
|
17
44
|
* View page props
|
|
@@ -166,18 +193,9 @@ export function ViewPage<T extends DataTypes.StringRecord>(
|
|
|
166
193
|
spacing={spacing}
|
|
167
194
|
/>
|
|
168
195
|
{actions !== null && (
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
width="100%"
|
|
173
|
-
flexWrap="wrap"
|
|
174
|
-
justifyContent="center"
|
|
175
|
-
paddingTop={actions == null ? undefined : actionPaddings}
|
|
176
|
-
paddingBottom={actionPaddings}
|
|
177
|
-
gap={actionPaddings}
|
|
178
|
-
>
|
|
179
|
-
{actions != null && Utils.getResult(actions, data, refresh)}
|
|
180
|
-
</Stack>
|
|
196
|
+
<ViewPageActionBar actionPaddings={actionPaddings}>
|
|
197
|
+
{Utils.getResult(actions, data, refresh)}
|
|
198
|
+
</ViewPageActionBar>
|
|
181
199
|
)}
|
|
182
200
|
{Utils.getResult(children, data, refresh)}
|
|
183
201
|
{pullToRefresh && (
|