@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.
@@ -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: { xs: 12, md: 6, lg: 4 }, 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) => {
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: { xs: 12, md: 6, lg: 4 }, 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 () => {
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[], source: Parameters<DragDropEvents["dragstart"]>[0]) => void;
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
- return ((0, jsx_runtime_1.jsx)(react_2.DragDropProvider, { onDragStart: (source) => onDragStart?.(items, source), onDragEnd: (source, target) => onDragEnd?.(items, source, target), children: (0, jsx_runtime_1.jsx)(Component, { ...componentProps, children: items.map((item, index) => {
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
+ }
@@ -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: { xs: 12, md: 6, lg: 4 }, 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) => {
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: { xs: 12, md: 6, lg: 4 }, 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 () => {
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[], source: Parameters<DragDropEvents["dragstart"]>[0]) => void;
94
+ onDragStart?: (items: D[], event: Parameters<DragDropEvents["dragstart"]>[0]) => void;
95
95
  /**
96
96
  * Drag end handler
97
97
  */