@etsoo/materialui 1.6.12 → 1.6.14
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.d.ts +6 -0
- package/lib/cjs/ButtonPopupCheckbox.js +4 -4
- package/lib/cjs/ButtonPopupRadio.d.ts +6 -0
- package/lib/cjs/ButtonPopupRadio.js +4 -4
- package/lib/cjs/DnDSortableList.d.ts +1 -1
- package/lib/cjs/DnDSortableList.js +22 -1
- package/lib/cjs/JsonTextField.d.ts +20 -0
- package/lib/cjs/JsonTextField.js +62 -0
- package/lib/cjs/custom/CustomAttributeArea.d.ts +11 -0
- package/lib/cjs/custom/CustomAttributeArea.js +194 -0
- package/lib/cjs/index.d.ts +2 -0
- package/lib/cjs/index.js +2 -0
- package/lib/mjs/ButtonPopupCheckbox.d.ts +6 -0
- package/lib/mjs/ButtonPopupCheckbox.js +4 -4
- package/lib/mjs/ButtonPopupRadio.d.ts +6 -0
- package/lib/mjs/ButtonPopupRadio.js +4 -4
- package/lib/mjs/DnDSortableList.d.ts +1 -1
- package/lib/mjs/DnDSortableList.js +23 -2
- package/lib/mjs/JsonTextField.d.ts +20 -0
- package/lib/mjs/JsonTextField.js +56 -0
- package/lib/mjs/custom/CustomAttributeArea.d.ts +11 -0
- package/lib/mjs/custom/CustomAttributeArea.js +188 -0
- package/lib/mjs/index.d.ts +2 -0
- package/lib/mjs/index.js +2 -0
- package/package.json +3 -3
- package/src/ButtonPopupCheckbox.tsx +12 -3
- package/src/ButtonPopupRadio.tsx +12 -3
- package/src/DnDSortableList.tsx +31 -4
- package/src/JsonTextField.tsx +104 -0
- package/src/custom/CustomAttributeArea.tsx +454 -0
- package/src/index.ts +2 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ButtonProps } from "@mui/material/Button";
|
|
2
2
|
import { IdType } from "@etsoo/shared";
|
|
3
|
+
import { GridSize } from "@mui/material/Grid";
|
|
3
4
|
import { DnDSortableListProps } from "./DnDSortableList";
|
|
5
|
+
import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
|
|
4
6
|
type DnDItemType = {
|
|
5
7
|
id: IdType;
|
|
6
8
|
};
|
|
@@ -64,6 +66,10 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<ButtonProps,
|
|
|
64
66
|
* The field is required or not
|
|
65
67
|
*/
|
|
66
68
|
required?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Item size
|
|
71
|
+
*/
|
|
72
|
+
itemSize?: ResponsiveStyleValue<GridSize>;
|
|
67
73
|
/**
|
|
68
74
|
* Value
|
|
69
75
|
*/
|
|
@@ -22,7 +22,7 @@ const FormLabel_1 = __importDefault(require("@mui/material/FormLabel"));
|
|
|
22
22
|
const DnDSortableList_1 = require("./DnDSortableList");
|
|
23
23
|
function ButtonPopupList(props) {
|
|
24
24
|
// Destruct
|
|
25
|
-
const { addSplitter = /\s*[,;]\s*/, value = [], items, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
25
|
+
const { addSplitter = /\s*[,;]\s*/, value = [], items, itemSize = { xs: 12, md: 6, lx: 4 }, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
26
26
|
// Methods
|
|
27
27
|
const dndRef = react_1.default.createRef();
|
|
28
28
|
// Refs
|
|
@@ -45,7 +45,7 @@ function ButtonPopupList(props) {
|
|
|
45
45
|
.filter((item) => tempSelectedIds.current.includes(item.id))
|
|
46
46
|
.map((item) => item.id);
|
|
47
47
|
onValueChange(ids);
|
|
48
|
-
}, itemRenderer: (item, style, { sortable: { index }, ref, handleRef }) => ((0, jsx_runtime_1.jsxs)(Grid_1.default, { size:
|
|
48
|
+
}, itemRenderer: (item, style, { sortable: { index }, ref, handleRef }) => ((0, jsx_runtime_1.jsxs)(Grid_1.default, { size: itemSize, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ref: ref, style: style, children: [(0, jsx_runtime_1.jsx)(IconButton_1.default, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ref: handleRef, 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) => {
|
|
49
49
|
const checked = e.target.checked;
|
|
50
50
|
const newIds = [
|
|
51
51
|
...selectedIds.toggleItem(item.id, checked)
|
|
@@ -81,7 +81,7 @@ function ButtonPopupCheckbox(props) {
|
|
|
81
81
|
// App
|
|
82
82
|
const app = (0, ReactApp_1.useRequiredAppContext)();
|
|
83
83
|
// Destruct
|
|
84
|
-
const { addSplitter, value = [], inputName, label, labelEnd, labelFormatter = (data) => {
|
|
84
|
+
const { addSplitter, value = [], inputName, itemSize, label, labelEnd, labelFormatter = (data) => {
|
|
85
85
|
if (typeof labelField === "function")
|
|
86
86
|
return labelField(data);
|
|
87
87
|
if (labelField in data) {
|
|
@@ -130,7 +130,7 @@ function ButtonPopupCheckbox(props) {
|
|
|
130
130
|
setSelectedIds(ids);
|
|
131
131
|
onValueChange?.(ids);
|
|
132
132
|
},
|
|
133
|
-
inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, value: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
|
|
133
|
+
inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, value: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, itemSize: itemSize, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
|
|
134
134
|
tempSelectedIds.current = ids;
|
|
135
135
|
} })),
|
|
136
136
|
fullScreen: app.smDown
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ButtonProps } from "@mui/material/Button";
|
|
2
2
|
import { DataTypes, IdType } from "@etsoo/shared";
|
|
3
|
+
import { GridSize } from "@mui/material/Grid";
|
|
4
|
+
import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
|
|
3
5
|
type DnDItemType = {
|
|
4
6
|
id: IdType;
|
|
5
7
|
};
|
|
@@ -63,6 +65,10 @@ export type ButtonPopupRadioProps<D extends DnDItemType> = Omit<ButtonProps, "ch
|
|
|
63
65
|
* The field is required or not
|
|
64
66
|
*/
|
|
65
67
|
required?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Item size
|
|
70
|
+
*/
|
|
71
|
+
itemSize?: ResponsiveStyleValue<GridSize>;
|
|
66
72
|
/**
|
|
67
73
|
* Value
|
|
68
74
|
*/
|
|
@@ -21,7 +21,7 @@ const FlexBox_1 = require("./FlexBox");
|
|
|
21
21
|
const ReactApp_1 = require("./app/ReactApp");
|
|
22
22
|
function ButtonPopupList(props) {
|
|
23
23
|
// Destruct
|
|
24
|
-
const { addSplitter = /\s*[,;]\s*/, value, items, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
24
|
+
const { addSplitter = /\s*[,;]\s*/, value, items, labelFormatter, labels, itemSize = { xs: 12, md: 6, lx: 4 }, onAdd, onValueChange } = props;
|
|
25
25
|
// Ref
|
|
26
26
|
const inputRef = react_1.default.useRef(null);
|
|
27
27
|
// State
|
|
@@ -38,7 +38,7 @@ function ButtonPopupList(props) {
|
|
|
38
38
|
: undefined;
|
|
39
39
|
setCurrentValue(value);
|
|
40
40
|
onValueChange(value);
|
|
41
|
-
}, children: (0, jsx_runtime_1.jsx)(Grid_1.default, { container: true, spacing: 0, children: items.map((item) => ((0, jsx_runtime_1.jsx)(Grid_1.default, { size:
|
|
41
|
+
}, children: (0, jsx_runtime_1.jsx)(Grid_1.default, { container: true, spacing: 0, children: items.map((item) => ((0, jsx_runtime_1.jsx)(Grid_1.default, { size: itemSize, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, children: (0, jsx_runtime_1.jsx)(FormControlLabel_1.default, { control: (0, jsx_runtime_1.jsx)(Radio_1.default, { value: item.id }), label: `${labelFormatter(item)}` }) }, item.id))) }) }), 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 () => {
|
|
42
42
|
if (inputRef.current == null)
|
|
43
43
|
return;
|
|
44
44
|
const input = inputRef.current.value.trim();
|
|
@@ -66,7 +66,7 @@ function ButtonPopupRadio(props) {
|
|
|
66
66
|
// App
|
|
67
67
|
const app = (0, ReactApp_1.useRequiredAppContext)();
|
|
68
68
|
// Destruct
|
|
69
|
-
const { addSplitter, inputName, label, labelEnd, labelFormatter = (data) => {
|
|
69
|
+
const { addSplitter, inputName, itemSize, label, labelEnd, labelFormatter = (data) => {
|
|
70
70
|
if (labelField in data) {
|
|
71
71
|
return data[labelField];
|
|
72
72
|
}
|
|
@@ -115,7 +115,7 @@ function ButtonPopupRadio(props) {
|
|
|
115
115
|
setCurrentValue(id);
|
|
116
116
|
onValueChange?.(id);
|
|
117
117
|
},
|
|
118
|
-
inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, value: currentValue, items: items, labelFormatter: labelFormatter, labels: labels, onAdd: onAdd, onValueChange: (id) => {
|
|
118
|
+
inputs: ((0, jsx_runtime_1.jsx)(ButtonPopupList, { addSplitter: addSplitter, value: currentValue, items: items, itemSize: itemSize, labelFormatter: labelFormatter, labels: labels, onAdd: onAdd, onValueChange: (id) => {
|
|
119
119
|
tempSelectedId.current = id;
|
|
120
120
|
} })),
|
|
121
121
|
fullScreen: app.smDown
|
|
@@ -91,7 +91,7 @@ export type DnDSortableListProps<D extends object, E extends React.ElementType =
|
|
|
91
91
|
/**
|
|
92
92
|
* Drag start handler
|
|
93
93
|
*/
|
|
94
|
-
onDragStart?: (items: D[],
|
|
94
|
+
onDragStart?: (items: D[], event: Parameters<DragDropEvents["dragstart"]>[0]) => void;
|
|
95
95
|
/**
|
|
96
96
|
* Drag end handler
|
|
97
97
|
*/
|
|
@@ -123,7 +123,28 @@ function DnDSortableList(props) {
|
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
125
|
}, [items, labelFn, changeItems]);
|
|
126
|
-
|
|
126
|
+
function handleDragEnd(...args) {
|
|
127
|
+
// Event
|
|
128
|
+
const event = args[0];
|
|
129
|
+
// Cancelled
|
|
130
|
+
if (event.canceled)
|
|
131
|
+
return;
|
|
132
|
+
if ((0, sortable_1.isSortableOperation)(event.operation) && event.operation.source) {
|
|
133
|
+
const { initialIndex, index } = event.operation.source;
|
|
134
|
+
if (initialIndex === index)
|
|
135
|
+
return;
|
|
136
|
+
// Clone
|
|
137
|
+
const newItems = [...items];
|
|
138
|
+
// Removed item
|
|
139
|
+
const [removed] = newItems.splice(initialIndex, 1);
|
|
140
|
+
// Insert to the destination index
|
|
141
|
+
newItems.splice(index, 0, removed);
|
|
142
|
+
changeItems(newItems);
|
|
143
|
+
// Drag end handler
|
|
144
|
+
onDragEnd?.(newItems, ...args);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return ((0, jsx_runtime_1.jsx)(react_2.DragDropProvider, { onDragStart: (event) => onDragStart?.(items, event), onDragEnd: (event, manager) => handleDragEnd(event, manager), children: (0, jsx_runtime_1.jsx)(Component, { ...componentProps, children: items.map((item, index) => {
|
|
127
148
|
const id = idFn(item);
|
|
128
149
|
return ((0, jsx_runtime_1.jsx)(SortableItem, { id: id, index: index, data: item, itemRenderer: itemRenderer, itemStyle: itemStyle }, id));
|
|
129
150
|
}) }) }));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TextFieldProps } from "@mui/material/TextField";
|
|
2
|
+
/**
|
|
3
|
+
* JSON text field props
|
|
4
|
+
*/
|
|
5
|
+
export type JsonTextFieldProps = TextFieldProps & {
|
|
6
|
+
/**
|
|
7
|
+
* Whether the value is an array
|
|
8
|
+
*/
|
|
9
|
+
isArray?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Edit button click handler
|
|
12
|
+
*/
|
|
13
|
+
onEdit?: (input: HTMLInputElement) => void;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* JSON text field component
|
|
17
|
+
* @param props Props
|
|
18
|
+
* @returns Component
|
|
19
|
+
*/
|
|
20
|
+
export declare function JsonTextField(props: JsonTextFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.JsonTextField = JsonTextField;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_1 = require("@etsoo/react");
|
|
9
|
+
const IconButton_1 = __importDefault(require("@mui/material/IconButton"));
|
|
10
|
+
const InputAdornment_1 = __importDefault(require("@mui/material/InputAdornment"));
|
|
11
|
+
const TextField_1 = __importDefault(require("@mui/material/TextField"));
|
|
12
|
+
const Edit_1 = __importDefault(require("@mui/icons-material/Edit"));
|
|
13
|
+
const react_2 = __importDefault(require("react"));
|
|
14
|
+
const MUGlobal_1 = require("./MUGlobal");
|
|
15
|
+
const ReactApp_1 = require("./app/ReactApp");
|
|
16
|
+
/**
|
|
17
|
+
* JSON text field component
|
|
18
|
+
* @param props Props
|
|
19
|
+
* @returns Component
|
|
20
|
+
*/
|
|
21
|
+
function JsonTextField(props) {
|
|
22
|
+
// Destruct
|
|
23
|
+
const { fullWidth = true, inputRef, isArray = false, multiline = true, onChange, onEdit, rows = 3, slotProps, ...rest } = props;
|
|
24
|
+
// Slot props
|
|
25
|
+
const { input, inputLabel, ...restSlotProps } = slotProps ?? {};
|
|
26
|
+
const localRef = react_2.default.useRef(null);
|
|
27
|
+
// Global app
|
|
28
|
+
const app = (0, ReactApp_1.useRequiredAppContext)();
|
|
29
|
+
return ((0, jsx_runtime_1.jsx)(TextField_1.default, { fullWidth: fullWidth, inputRef: (0, react_1.useCombinedRefs)(inputRef, localRef), multiline: multiline, onChange: (event) => {
|
|
30
|
+
const value = event.target.value.trim();
|
|
31
|
+
let errorMessage = "";
|
|
32
|
+
if (value.length > 0) {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(value);
|
|
35
|
+
if (isArray && !Array.isArray(parsed)) {
|
|
36
|
+
errorMessage =
|
|
37
|
+
app.get("jsonDataArrayError") || "Value must be a JSON array";
|
|
38
|
+
}
|
|
39
|
+
if (typeof parsed !== "object") {
|
|
40
|
+
throw new Error("Parsed value is not an object");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
errorMessage =
|
|
45
|
+
(app.get("jsonDataError") || "Invalid JSON text") + " - " + e;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
event.target.setCustomValidity(errorMessage);
|
|
49
|
+
event.target.reportValidity();
|
|
50
|
+
onChange?.(event);
|
|
51
|
+
}, rows: rows, slotProps: {
|
|
52
|
+
input: {
|
|
53
|
+
endAdornment: onEdit ? ((0, jsx_runtime_1.jsx)(InputAdornment_1.default, { position: "end", children: (0, jsx_runtime_1.jsx)(IconButton_1.default, { onClick: () => onEdit?.(localRef.current), children: (0, jsx_runtime_1.jsx)(Edit_1.default, {}) }) })) : undefined,
|
|
54
|
+
...input
|
|
55
|
+
},
|
|
56
|
+
inputLabel: {
|
|
57
|
+
shrink: MUGlobal_1.MUGlobal.inputFieldShrink,
|
|
58
|
+
...inputLabel
|
|
59
|
+
},
|
|
60
|
+
...restSlotProps
|
|
61
|
+
}, ...rest }));
|
|
62
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TextFieldProps } from "@mui/material/TextField";
|
|
2
|
+
/**
|
|
3
|
+
* Custom attribute area properties
|
|
4
|
+
*/
|
|
5
|
+
export type CustomAttributeAreaProps = TextFieldProps & {};
|
|
6
|
+
/**
|
|
7
|
+
* Custom attribute area
|
|
8
|
+
* @param props Properties
|
|
9
|
+
* @returns Component
|
|
10
|
+
*/
|
|
11
|
+
export declare function CustomAttributeArea(props: CustomAttributeAreaProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CustomAttributeArea = CustomAttributeArea;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const IconButton_1 = __importDefault(require("@mui/material/IconButton"));
|
|
9
|
+
const Add_1 = __importDefault(require("@mui/icons-material/Add"));
|
|
10
|
+
const Edit_1 = __importDefault(require("@mui/icons-material/Edit"));
|
|
11
|
+
const Delete_1 = __importDefault(require("@mui/icons-material/Delete"));
|
|
12
|
+
const DragIndicator_1 = __importDefault(require("@mui/icons-material/DragIndicator"));
|
|
13
|
+
const react_1 = __importDefault(require("react"));
|
|
14
|
+
const Card_1 = __importDefault(require("@mui/material/Card"));
|
|
15
|
+
const CardContent_1 = __importDefault(require("@mui/material/CardContent"));
|
|
16
|
+
const Grid_1 = __importDefault(require("@mui/material/Grid"));
|
|
17
|
+
const appscript_1 = require("@etsoo/appscript");
|
|
18
|
+
const Button_1 = __importDefault(require("@mui/material/Button"));
|
|
19
|
+
const material_1 = require("@mui/material");
|
|
20
|
+
const shared_1 = require("@etsoo/shared");
|
|
21
|
+
const CustomFieldUtils_1 = require("./CustomFieldUtils");
|
|
22
|
+
const ComboBox_1 = require("../ComboBox");
|
|
23
|
+
const InputField_1 = require("../InputField");
|
|
24
|
+
const JsonTextField_1 = require("../JsonTextField");
|
|
25
|
+
const FlexBox_1 = require("../FlexBox");
|
|
26
|
+
const DnDSortableList_1 = require("../DnDSortableList");
|
|
27
|
+
const ReactApp_1 = require("../app/ReactApp");
|
|
28
|
+
const size = { xs: 6, sm: 4, lg: 3, xl: 2 };
|
|
29
|
+
const smallSize = { xs: 3, sm: 2, xl: 1 };
|
|
30
|
+
const random4Digit = () => {
|
|
31
|
+
return Math.floor(1000 + Math.random() * 9000);
|
|
32
|
+
};
|
|
33
|
+
const isCamelCase = (name) => {
|
|
34
|
+
return /^[a-z][a-zA-Z0-9]*$/.test(name);
|
|
35
|
+
};
|
|
36
|
+
function InputItemUIs({ data }) {
|
|
37
|
+
// Global app
|
|
38
|
+
const app = (0, ReactApp_1.useRequiredAppContext)();
|
|
39
|
+
// Labels
|
|
40
|
+
const labels = app.getLabels("gridItemProps", "helperText", "label", "mainSlotProps", "nameB", "options", "optionsFormat", "refs", "size", "type");
|
|
41
|
+
const types = Object.keys(CustomFieldUtils_1.CustomFieldUtils.customFieldCreators);
|
|
42
|
+
const nameRef = react_1.default.useRef(null);
|
|
43
|
+
const optionsRef = react_1.default.useRef(null);
|
|
44
|
+
return ((0, jsx_runtime_1.jsxs)(Grid_1.default, { container: true, spacing: 2, marginTop: 1, children: [(0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, sm: 6 }, children: (0, jsx_runtime_1.jsx)(ComboBox_1.ComboBox, { name: "type", label: labels.type, inputRequired: true, size: "small", loadData: () => Promise.resolve(types.map((t) => ({ id: t, label: t }))), onValueChange: (item) => {
|
|
45
|
+
const type = item?.id;
|
|
46
|
+
optionsRef.current.disabled =
|
|
47
|
+
type !== "combobox" && type !== "select";
|
|
48
|
+
const nameInput = nameRef.current;
|
|
49
|
+
if (nameInput.value === "" &&
|
|
50
|
+
(type === "amountlabel" || type === "divider" || type === "label")) {
|
|
51
|
+
nameInput.value = type + random4Digit();
|
|
52
|
+
}
|
|
53
|
+
}, idValue: data?.type, fullWidth: true }) }), (0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, sm: 6 }, children: (0, jsx_runtime_1.jsx)(ComboBox_1.ComboBox, { name: "space", label: labels.size, inputRequired: true, size: "small", loadData: () => Promise.resolve(appscript_1.CustomFieldSpaceValues.map((t) => ({ id: t, label: t }))), idValue: data?.space, fullWidth: true }) }), (0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, sm: 6 }, children: (0, jsx_runtime_1.jsx)(InputField_1.InputField, { fullWidth: true, required: true, name: "name", size: "small", inputRef: nameRef, slotProps: { htmlInput: { maxLength: 128, readOnly: !!data } }, label: labels.nameB, defaultValue: data?.name ?? "" }) }), (0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, sm: 6 }, children: (0, jsx_runtime_1.jsx)(InputField_1.InputField, { fullWidth: true, name: "label", size: "small", slotProps: { htmlInput: { maxLength: 128 } }, label: labels.label, defaultValue: data?.label ?? "" }) }), (0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, sm: 12 }, children: (0, jsx_runtime_1.jsx)(InputField_1.InputField, { fullWidth: true, name: "helperText", size: "small", label: labels.helperText, defaultValue: data?.helperText ?? "" }) }), (0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, sm: 12 }, children: (0, jsx_runtime_1.jsx)(InputField_1.InputField, { fullWidth: true, name: "options", size: "small", multiline: true, rows: 2, label: labels.options, inputRef: optionsRef, helperText: labels.optionsFormat, slotProps: { htmlInput: { disabled: true } }, defaultValue: data?.options
|
|
54
|
+
? data.options
|
|
55
|
+
.map((o) => `${o.id}=${shared_1.DataTypes.getListItemLabel(o)}`)
|
|
56
|
+
.join("\n")
|
|
57
|
+
: "" }) }), (0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, sm: 12 }, children: (0, jsx_runtime_1.jsx)(JsonTextField_1.JsonTextField, { isArray: true, name: "refs", size: "small", multiline: false, label: labels.refs + " (JSON)", defaultValue: data?.refs ? JSON.stringify(data.refs) : "" }) }), (0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, sm: 12 }, children: (0, jsx_runtime_1.jsx)(JsonTextField_1.JsonTextField, { name: "gridItemProps", size: "small", multiline: false, label: labels.gridItemProps + " (JSON)", defaultValue: data?.gridItemProps ? JSON.stringify(data.gridItemProps) : "" }) }), (0, jsx_runtime_1.jsx)(Grid_1.default, { size: { xs: 12, sm: 12 }, children: (0, jsx_runtime_1.jsx)(JsonTextField_1.JsonTextField, { name: "mainSlotProps", size: "small", multiline: false, label: labels.mainSlotProps + " (JSON)", defaultValue: data?.mainSlotProps ? JSON.stringify(data.mainSlotProps) : "", helperText: '{"required":true}' }) })] }));
|
|
58
|
+
}
|
|
59
|
+
function InputUIs({ source, onChange }) {
|
|
60
|
+
// Global app
|
|
61
|
+
const app = (0, ReactApp_1.useRequiredAppContext)();
|
|
62
|
+
// Labels
|
|
63
|
+
const labels = app.getLabels("add", "delete", "edit", "sortTip", "dragIndicator");
|
|
64
|
+
const [items, setItems] = react_1.default.useState([]);
|
|
65
|
+
const doChange = (items) => {
|
|
66
|
+
setItems(items);
|
|
67
|
+
onChange(items);
|
|
68
|
+
};
|
|
69
|
+
const editItem = (item) => {
|
|
70
|
+
app.showInputDialog({
|
|
71
|
+
title: item ? labels.edit : labels.add,
|
|
72
|
+
message: "",
|
|
73
|
+
callback: async (form) => {
|
|
74
|
+
// Cancelled
|
|
75
|
+
if (form == null) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Validate form
|
|
79
|
+
if (!form.reportValidity()) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
// Form data
|
|
83
|
+
const { typeInput: type, spaceInput: space, name, label, helperText, options, refs, gridItemProps, mainSlotProps } = shared_1.DomUtils.dataAs(new FormData(form), {
|
|
84
|
+
typeInput: "string",
|
|
85
|
+
spaceInput: "string",
|
|
86
|
+
name: "string",
|
|
87
|
+
label: "string",
|
|
88
|
+
helperText: "string",
|
|
89
|
+
options: "string",
|
|
90
|
+
refs: "string",
|
|
91
|
+
gridItemProps: "string",
|
|
92
|
+
mainSlotProps: "string"
|
|
93
|
+
});
|
|
94
|
+
if (!type || !space || !name) {
|
|
95
|
+
return app.get("noData");
|
|
96
|
+
}
|
|
97
|
+
if (!isCamelCase(name)) {
|
|
98
|
+
shared_1.DomUtils.setFocus("name", form);
|
|
99
|
+
return app.get("invalidNaming") + " (camelCase)";
|
|
100
|
+
}
|
|
101
|
+
if (type !== "divider" && !label) {
|
|
102
|
+
shared_1.DomUtils.setFocus("label", form);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (item == null && items.some((item) => item.name === name)) {
|
|
106
|
+
return app.get("itemExists")?.format(name);
|
|
107
|
+
}
|
|
108
|
+
const optionsJson = options
|
|
109
|
+
? options.split("\n").map((line) => {
|
|
110
|
+
const [id, ...labelParts] = line.split("=");
|
|
111
|
+
return { id, label: labelParts.join("=") };
|
|
112
|
+
})
|
|
113
|
+
: undefined;
|
|
114
|
+
const refsJson = refs ? JSON.parse(refs) : undefined;
|
|
115
|
+
const gridItemPropsJson = gridItemProps
|
|
116
|
+
? JSON.parse(gridItemProps)
|
|
117
|
+
: undefined;
|
|
118
|
+
const mainSlotPropsJson = mainSlotProps
|
|
119
|
+
? JSON.parse(mainSlotProps)
|
|
120
|
+
: undefined;
|
|
121
|
+
if (item == null) {
|
|
122
|
+
const newItem = {
|
|
123
|
+
type,
|
|
124
|
+
name,
|
|
125
|
+
space: space,
|
|
126
|
+
label,
|
|
127
|
+
helperText,
|
|
128
|
+
options: optionsJson,
|
|
129
|
+
refs: refsJson,
|
|
130
|
+
gridItemProps: gridItemPropsJson,
|
|
131
|
+
mainSlotProps: mainSlotPropsJson
|
|
132
|
+
};
|
|
133
|
+
doChange([...items, newItem]);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
item.type = type;
|
|
137
|
+
item.space = space;
|
|
138
|
+
item.name = name;
|
|
139
|
+
item.label = label;
|
|
140
|
+
item.helperText = helperText;
|
|
141
|
+
item.options = optionsJson;
|
|
142
|
+
item.refs = refsJson;
|
|
143
|
+
item.gridItemProps = gridItemPropsJson;
|
|
144
|
+
item.mainSlotProps = mainSlotPropsJson;
|
|
145
|
+
doChange([...items]);
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
},
|
|
149
|
+
inputs: (0, jsx_runtime_1.jsx)(InputItemUIs, { data: item })
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
react_1.default.useEffect(() => {
|
|
153
|
+
try {
|
|
154
|
+
if (source) {
|
|
155
|
+
const parsed = JSON.parse(source);
|
|
156
|
+
if (Array.isArray(parsed)) {
|
|
157
|
+
setItems(parsed);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.error("Failed to parse source:", error);
|
|
163
|
+
}
|
|
164
|
+
}, [source]);
|
|
165
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsxs)(FlexBox_1.HBox, { marginBottom: 0.5, gap: 1, alignItems: "center", children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { children: labels.sortTip }), (0, jsx_runtime_1.jsx)(Button_1.default, { size: "small", color: "primary", variant: "outlined", startIcon: (0, jsx_runtime_1.jsx)(Add_1.default, {}), onClick: () => editItem(), children: labels.add })] }), (0, jsx_runtime_1.jsx)(Card_1.default, { children: (0, jsx_runtime_1.jsx)(CardContent_1.default, { children: (0, jsx_runtime_1.jsx)(Grid_1.default, { container: true, spacing: 0, children: (0, jsx_runtime_1.jsx)(DnDSortableList_1.DnDSortableList, { items: items, idField: (item) => item.name, labelField: (item) => item.label || item.name, onChange: (items) => doChange(items), itemRenderer: (data, style, { sortable: { index }, ref, handleRef }) => ((0, jsx_runtime_1.jsxs)(Grid_1.default, { container: true, size: { xs: 12, sm: 12 }, ref: ref, style: style, gap: 1, children: [(0, jsx_runtime_1.jsx)(Grid_1.default, { size: smallSize, children: (0, jsx_runtime_1.jsx)(IconButton_1.default, { style: { cursor: "move" }, size: "small", title: labels.dragIndicator, ref: handleRef, children: (0, jsx_runtime_1.jsx)(DragIndicator_1.default, {}) }) }), (0, jsx_runtime_1.jsxs)(Grid_1.default, { size: size, children: [index + 1, " - ", data.type, " / ", data.space] }), (0, jsx_runtime_1.jsxs)(Grid_1.default, { size: size, children: [data.name, " - ", data.label] }), (0, jsx_runtime_1.jsxs)(Grid_1.default, { size: smallSize, children: [(0, jsx_runtime_1.jsx)(IconButton_1.default, { size: "small", title: labels.delete, onClick: () => doChange(items.filter((item) => item.name !== data.name)), children: (0, jsx_runtime_1.jsx)(Delete_1.default, {}) }), (0, jsx_runtime_1.jsx)(IconButton_1.default, { size: "small", title: labels.edit, onClick: () => editItem(data), children: (0, jsx_runtime_1.jsx)(Edit_1.default, {}) })] })] })) }) }) }) })] }));
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Custom attribute area
|
|
169
|
+
* @param props Properties
|
|
170
|
+
* @returns Component
|
|
171
|
+
*/
|
|
172
|
+
function CustomAttributeArea(props) {
|
|
173
|
+
// Global app
|
|
174
|
+
const app = (0, ReactApp_1.useRequiredAppContext)();
|
|
175
|
+
// Destruct
|
|
176
|
+
const { label = app.get("attributeDefinition"), ...rest } = props;
|
|
177
|
+
const ref = react_1.default.useRef([]);
|
|
178
|
+
const showUI = (input) => {
|
|
179
|
+
app.showInputDialog({
|
|
180
|
+
title: label,
|
|
181
|
+
message: "",
|
|
182
|
+
fullScreen: true,
|
|
183
|
+
callback: (form) => {
|
|
184
|
+
if (form == null) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
input.value = ref.current.length > 0 ? JSON.stringify(ref.current) : "";
|
|
188
|
+
},
|
|
189
|
+
inputs: ((0, jsx_runtime_1.jsx)(InputUIs, { source: input.value, onChange: (items) => (ref.current = items) }))
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
// Layout
|
|
193
|
+
return (0, jsx_runtime_1.jsx)(JsonTextField_1.JsonTextField, { label: label, onEdit: showUI, isArray: true, ...rest });
|
|
194
|
+
}
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./app/ISmartERPUser";
|
|
|
6
6
|
export * from "./app/Labels";
|
|
7
7
|
export * from "./app/ReactApp";
|
|
8
8
|
export * from "./app/ServiceApp";
|
|
9
|
+
export * from "./custom/CustomAttributeArea";
|
|
9
10
|
export * from "./custom/CustomFieldUtils";
|
|
10
11
|
export * from "./custom/CustomFieldViewer";
|
|
11
12
|
export * from "./custom/CustomFieldWindow";
|
|
@@ -73,6 +74,7 @@ export * from "./InputTipField";
|
|
|
73
74
|
export * from "./IntInputField";
|
|
74
75
|
export * from "./ItemList";
|
|
75
76
|
export * from "./JsonDataInput";
|
|
77
|
+
export * from "./JsonTextField";
|
|
76
78
|
export * from "./LineChart";
|
|
77
79
|
export * from "./LinkEx";
|
|
78
80
|
export * from "./ListChooser";
|
package/lib/cjs/index.js
CHANGED
|
@@ -22,6 +22,7 @@ __exportStar(require("./app/ISmartERPUser"), exports);
|
|
|
22
22
|
__exportStar(require("./app/Labels"), exports);
|
|
23
23
|
__exportStar(require("./app/ReactApp"), exports);
|
|
24
24
|
__exportStar(require("./app/ServiceApp"), exports);
|
|
25
|
+
__exportStar(require("./custom/CustomAttributeArea"), exports);
|
|
25
26
|
__exportStar(require("./custom/CustomFieldUtils"), exports);
|
|
26
27
|
__exportStar(require("./custom/CustomFieldViewer"), exports);
|
|
27
28
|
__exportStar(require("./custom/CustomFieldWindow"), exports);
|
|
@@ -89,6 +90,7 @@ __exportStar(require("./InputTipField"), exports);
|
|
|
89
90
|
__exportStar(require("./IntInputField"), exports);
|
|
90
91
|
__exportStar(require("./ItemList"), exports);
|
|
91
92
|
__exportStar(require("./JsonDataInput"), exports);
|
|
93
|
+
__exportStar(require("./JsonTextField"), exports);
|
|
92
94
|
__exportStar(require("./LineChart"), exports);
|
|
93
95
|
__exportStar(require("./LinkEx"), exports);
|
|
94
96
|
__exportStar(require("./ListChooser"), exports);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ButtonProps } from "@mui/material/Button";
|
|
2
2
|
import { IdType } from "@etsoo/shared";
|
|
3
|
+
import { GridSize } from "@mui/material/Grid";
|
|
3
4
|
import { DnDSortableListProps } from "./DnDSortableList";
|
|
5
|
+
import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
|
|
4
6
|
type DnDItemType = {
|
|
5
7
|
id: IdType;
|
|
6
8
|
};
|
|
@@ -64,6 +66,10 @@ export type ButtonPopupCheckboxProps<D extends DnDItemType> = Omit<ButtonProps,
|
|
|
64
66
|
* The field is required or not
|
|
65
67
|
*/
|
|
66
68
|
required?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Item size
|
|
71
|
+
*/
|
|
72
|
+
itemSize?: ResponsiveStyleValue<GridSize>;
|
|
67
73
|
/**
|
|
68
74
|
* Value
|
|
69
75
|
*/
|
|
@@ -16,7 +16,7 @@ import FormLabel from "@mui/material/FormLabel";
|
|
|
16
16
|
import { DnDSortableList } from "./DnDSortableList";
|
|
17
17
|
function ButtonPopupList(props) {
|
|
18
18
|
// Destruct
|
|
19
|
-
const { addSplitter = /\s*[,;]\s*/, value = [], items, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
19
|
+
const { addSplitter = /\s*[,;]\s*/, value = [], items, itemSize = { xs: 12, md: 6, lx: 4 }, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
20
20
|
// Methods
|
|
21
21
|
const dndRef = React.createRef();
|
|
22
22
|
// Refs
|
|
@@ -39,7 +39,7 @@ function ButtonPopupList(props) {
|
|
|
39
39
|
.filter((item) => tempSelectedIds.current.includes(item.id))
|
|
40
40
|
.map((item) => item.id);
|
|
41
41
|
onValueChange(ids);
|
|
42
|
-
}, itemRenderer: (item, style, { sortable: { index }, ref, handleRef }) => (_jsxs(Grid, { size:
|
|
42
|
+
}, itemRenderer: (item, style, { sortable: { index }, ref, handleRef }) => (_jsxs(Grid, { size: itemSize, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ref: ref, style: style, children: [_jsx(IconButton, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ref: handleRef, children: _jsx(DragIndicatorIcon, {}) }), _jsx(FormControlLabel, { control: _jsx(Checkbox, { name: "item", value: item.id, checked: selectedIds.includes(item.id), onChange: (e) => {
|
|
43
43
|
const checked = e.target.checked;
|
|
44
44
|
const newIds = [
|
|
45
45
|
...selectedIds.toggleItem(item.id, checked)
|
|
@@ -75,7 +75,7 @@ export function ButtonPopupCheckbox(props) {
|
|
|
75
75
|
// App
|
|
76
76
|
const app = useRequiredAppContext();
|
|
77
77
|
// Destruct
|
|
78
|
-
const { addSplitter, value = [], inputName, label, labelEnd, labelFormatter = (data) => {
|
|
78
|
+
const { addSplitter, value = [], inputName, itemSize, label, labelEnd, labelFormatter = (data) => {
|
|
79
79
|
if (typeof labelField === "function")
|
|
80
80
|
return labelField(data);
|
|
81
81
|
if (labelField in data) {
|
|
@@ -124,7 +124,7 @@ export function ButtonPopupCheckbox(props) {
|
|
|
124
124
|
setSelectedIds(ids);
|
|
125
125
|
onValueChange?.(ids);
|
|
126
126
|
},
|
|
127
|
-
inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, value: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
|
|
127
|
+
inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, value: selectedIds, items: items, labelFormatter: labelFormatter, labelField: labelField, itemSize: itemSize, labels: labels, onAdd: onAdd, onValueChange: (ids) => {
|
|
128
128
|
tempSelectedIds.current = ids;
|
|
129
129
|
} })),
|
|
130
130
|
fullScreen: app.smDown
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ButtonProps } from "@mui/material/Button";
|
|
2
2
|
import { DataTypes, IdType } from "@etsoo/shared";
|
|
3
|
+
import { GridSize } from "@mui/material/Grid";
|
|
4
|
+
import { ResponsiveStyleValue } from "./ResponsiveStyleValue";
|
|
3
5
|
type DnDItemType = {
|
|
4
6
|
id: IdType;
|
|
5
7
|
};
|
|
@@ -63,6 +65,10 @@ export type ButtonPopupRadioProps<D extends DnDItemType> = Omit<ButtonProps, "ch
|
|
|
63
65
|
* The field is required or not
|
|
64
66
|
*/
|
|
65
67
|
required?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Item size
|
|
70
|
+
*/
|
|
71
|
+
itemSize?: ResponsiveStyleValue<GridSize>;
|
|
66
72
|
/**
|
|
67
73
|
* Value
|
|
68
74
|
*/
|
|
@@ -15,7 +15,7 @@ import { HBox, VBox } from "./FlexBox";
|
|
|
15
15
|
import { useRequiredAppContext } from "./app/ReactApp";
|
|
16
16
|
function ButtonPopupList(props) {
|
|
17
17
|
// Destruct
|
|
18
|
-
const { addSplitter = /\s*[,;]\s*/, value, items, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
18
|
+
const { addSplitter = /\s*[,;]\s*/, value, items, labelFormatter, labels, itemSize = { xs: 12, md: 6, lx: 4 }, onAdd, onValueChange } = props;
|
|
19
19
|
// Ref
|
|
20
20
|
const inputRef = React.useRef(null);
|
|
21
21
|
// State
|
|
@@ -32,7 +32,7 @@ function ButtonPopupList(props) {
|
|
|
32
32
|
: undefined;
|
|
33
33
|
setCurrentValue(value);
|
|
34
34
|
onValueChange(value);
|
|
35
|
-
}, children: _jsx(Grid, { container: true, spacing: 0, children: items.map((item) => (_jsx(Grid, { size:
|
|
35
|
+
}, children: _jsx(Grid, { container: true, spacing: 0, children: items.map((item) => (_jsx(Grid, { size: itemSize, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, children: _jsx(FormControlLabel, { control: _jsx(Radio, { value: item.id }), label: `${labelFormatter(item)}` }) }, item.id))) }) }), 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 () => {
|
|
36
36
|
if (inputRef.current == null)
|
|
37
37
|
return;
|
|
38
38
|
const input = inputRef.current.value.trim();
|
|
@@ -60,7 +60,7 @@ export function ButtonPopupRadio(props) {
|
|
|
60
60
|
// App
|
|
61
61
|
const app = useRequiredAppContext();
|
|
62
62
|
// Destruct
|
|
63
|
-
const { addSplitter, inputName, label, labelEnd, labelFormatter = (data) => {
|
|
63
|
+
const { addSplitter, inputName, itemSize, label, labelEnd, labelFormatter = (data) => {
|
|
64
64
|
if (labelField in data) {
|
|
65
65
|
return data[labelField];
|
|
66
66
|
}
|
|
@@ -109,7 +109,7 @@ export function ButtonPopupRadio(props) {
|
|
|
109
109
|
setCurrentValue(id);
|
|
110
110
|
onValueChange?.(id);
|
|
111
111
|
},
|
|
112
|
-
inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, value: currentValue, items: items, labelFormatter: labelFormatter, labels: labels, onAdd: onAdd, onValueChange: (id) => {
|
|
112
|
+
inputs: (_jsx(ButtonPopupList, { addSplitter: addSplitter, value: currentValue, items: items, itemSize: itemSize, labelFormatter: labelFormatter, labels: labels, onAdd: onAdd, onValueChange: (id) => {
|
|
113
113
|
tempSelectedId.current = id;
|
|
114
114
|
} })),
|
|
115
115
|
fullScreen: app.smDown
|
|
@@ -91,7 +91,7 @@ export type DnDSortableListProps<D extends object, E extends React.ElementType =
|
|
|
91
91
|
/**
|
|
92
92
|
* Drag start handler
|
|
93
93
|
*/
|
|
94
|
-
onDragStart?: (items: D[],
|
|
94
|
+
onDragStart?: (items: D[], event: Parameters<DragDropEvents["dragstart"]>[0]) => void;
|
|
95
95
|
/**
|
|
96
96
|
* Drag end handler
|
|
97
97
|
*/
|