@etsoo/materialui
Version:
TypeScript Material-UI Implementation
192 lines (191 loc) • 11.4 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import IconButton from "@mui/material/IconButton";
import AddIcon from "@mui/icons-material/Add";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
import DragIndicatorIcon from "@mui/icons-material/DragIndicator";
import React from "react";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Grid from "@mui/material/Grid";
import { CustomFieldSpaceValues } from "@etsoo/appscript";
import Button from "@mui/material/Button";
import { Typography } from "@mui/material";
import { DataTypes, DomUtils } from "@etsoo/shared";
import { CustomFieldUtils } from "./CustomFieldUtils";
import { ComboBox } from "../ComboBox";
import { InputField } from "../InputField";
import { JsonTextField } from "../JsonTextField";
import { HBox } from "../FlexBox";
import { DnDSortableList } from "../DnDSortableList";
import { useRequiredAppContext } from "../app/ReactApp";
const size = { xs: 6, sm: 4, lg: 3, xl: 2 };
const smallSize = { xs: 3, sm: 2, xl: 1 };
const random4Digit = () => {
return Math.floor(1000 + Math.random() * 9000);
};
const isCamelCase = (name) => {
return /^[a-z][a-zA-Z0-9]*$/.test(name);
};
function InputItemUIs({ data }) {
// Global app
const app = useRequiredAppContext();
// Labels
const labels = app.getLabels("gridItemProps", "helperText", "label", "mainSlotProps", "nameB", "options", "optionsFormat", "refs", "size", "type");
const types = Object.keys(CustomFieldUtils.customFieldCreators);
const nameRef = React.useRef(null);
const optionsRef = React.useRef(null);
return (_jsxs(Grid, { container: true, spacing: 2, marginTop: 1, children: [_jsx(Grid, { size: { xs: 12, sm: 6 }, children: _jsx(ComboBox, { name: "type", label: labels.type, inputRequired: true, size: "small", loadData: () => Promise.resolve(types.map((t) => ({ id: t, label: t }))), onValueChange: (item) => {
const type = item?.id;
optionsRef.current.disabled =
type !== "combobox" &&
type !== "select" &&
type !== "checkbox" &&
type !== "radio";
const nameInput = nameRef.current;
if (nameInput.value === "" &&
(type === "amountlabel" || type === "divider" || type === "label")) {
nameInput.value = type + random4Digit();
}
}, idValue: data?.type, fullWidth: true }) }), _jsx(Grid, { size: { xs: 12, sm: 6 }, children: _jsx(ComboBox, { name: "space", label: labels.size, inputRequired: true, size: "small", loadData: () => Promise.resolve(CustomFieldSpaceValues.map((t) => ({ id: t, label: t }))), idValue: data?.space, fullWidth: true }) }), _jsx(Grid, { size: { xs: 12, sm: 6 }, children: _jsx(InputField, { fullWidth: true, required: true, name: "name", size: "small", inputRef: nameRef, slotProps: { htmlInput: { maxLength: 128, readOnly: !!data } }, label: labels.nameB, defaultValue: data?.name ?? "" }) }), _jsx(Grid, { size: { xs: 12, sm: 6 }, children: _jsx(InputField, { fullWidth: true, name: "label", size: "small", slotProps: { htmlInput: { maxLength: 128 } }, label: labels.label, defaultValue: data?.label ?? "" }) }), _jsx(Grid, { size: { xs: 12, sm: 12 }, children: _jsx(InputField, { fullWidth: true, name: "helperText", size: "small", label: labels.helperText, defaultValue: data?.helperText ?? "" }) }), _jsx(Grid, { size: { xs: 12, sm: 12 }, children: _jsx(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
? data.options
.map((o) => `${o.id}=${DataTypes.getListItemLabel(o)}`)
.join("\n")
: "" }) }), _jsx(Grid, { size: { xs: 12, sm: 12 }, children: _jsx(JsonTextField, { isArray: true, name: "refs", size: "small", multiline: false, label: labels.refs + " (JSON)", defaultValue: data?.refs ? JSON.stringify(data.refs) : "" }) }), _jsx(Grid, { size: { xs: 12, sm: 12 }, children: _jsx(JsonTextField, { name: "gridItemProps", size: "small", multiline: false, label: labels.gridItemProps + " (JSON)", defaultValue: data?.gridItemProps ? JSON.stringify(data.gridItemProps) : "" }) }), _jsx(Grid, { size: { xs: 12, sm: 12 }, children: _jsx(JsonTextField, { name: "mainSlotProps", size: "small", multiline: false, label: labels.mainSlotProps + " (JSON)", defaultValue: data?.mainSlotProps ? JSON.stringify(data.mainSlotProps) : "", helperText: '{"required":true}' }) })] }));
}
function InputUIs({ source, onChange }) {
// Global app
const app = useRequiredAppContext();
// Labels
const labels = app.getLabels("add", "delete", "edit", "sortTip", "dragIndicator");
const [items, setItems] = React.useState([]);
const doChange = (items) => {
setItems(items);
onChange(items);
};
const editItem = (item) => {
app.showInputDialog({
title: item ? labels.edit : labels.add,
message: "",
callback: async (form) => {
// Cancelled
if (form == null) {
return;
}
// Validate form
if (!form.reportValidity()) {
return false;
}
// Form data
const { typeInput: type, spaceInput: space, name, label, helperText, options, refs, gridItemProps, mainSlotProps } = DomUtils.dataAs(new FormData(form), {
typeInput: "string",
spaceInput: "string",
name: "string",
label: "string",
helperText: "string",
options: "string",
refs: "string",
gridItemProps: "string",
mainSlotProps: "string"
});
if (!type || !space || !name) {
return app.get("noData");
}
if (!isCamelCase(name)) {
DomUtils.setFocus("name", form);
return app.get("invalidNaming") + " (camelCase)";
}
if (type !== "divider" && !label) {
DomUtils.setFocus("label", form);
return false;
}
if (item == null && items.some((item) => item.name === name)) {
return app.get("itemExists")?.format(name);
}
const optionsJson = options
? options.split("\n").map((line) => {
const [id, ...labelParts] = line.split("=");
return { id, label: labelParts.join("=") };
})
: undefined;
const refsJson = refs ? JSON.parse(refs) : undefined;
const gridItemPropsJson = gridItemProps
? JSON.parse(gridItemProps)
: undefined;
const mainSlotPropsJson = mainSlotProps
? JSON.parse(mainSlotProps)
: undefined;
if (item == null) {
const newItem = {
type,
name,
space: space,
label,
helperText,
options: optionsJson,
refs: refsJson,
gridItemProps: gridItemPropsJson,
mainSlotProps: mainSlotPropsJson
};
doChange([...items, newItem]);
}
else {
item.type = type;
item.space = space;
item.name = name;
item.label = label;
item.helperText = helperText;
item.options = optionsJson;
item.refs = refsJson;
item.gridItemProps = gridItemPropsJson;
item.mainSlotProps = mainSlotPropsJson;
doChange([...items]);
}
return;
},
inputs: _jsx(InputItemUIs, { data: item })
});
};
React.useEffect(() => {
try {
if (source) {
const parsed = JSON.parse(source);
if (Array.isArray(parsed)) {
setItems(parsed);
}
}
}
catch (error) {
console.error("Failed to parse source:", error);
}
}, [source]);
return (_jsxs(React.Fragment, { children: [_jsxs(HBox, { marginBottom: 0.5, gap: 1, alignItems: "center", children: [_jsx(Typography, { children: labels.sortTip }), _jsx(Button, { size: "small", color: "primary", variant: "outlined", startIcon: _jsx(AddIcon, {}), onClick: () => editItem(), children: labels.add })] }), _jsx(Card, { children: _jsx(CardContent, { children: _jsx(Grid, { container: true, spacing: 0, children: _jsx(DnDSortableList, { items: items, idField: (item) => item.name, labelField: (item) => item.label || item.name, onChange: (items) => doChange(items), itemRenderer: (data, style, { sortable: { index }, ref, handleRef }) => (_jsxs(Grid, { container: true, size: { xs: 12, sm: 12 }, ref: ref, style: style, gap: 1, children: [_jsx(Grid, { size: smallSize, children: _jsx(IconButton, { style: { cursor: "move" }, size: "small", title: labels.dragIndicator, ref: handleRef, children: _jsx(DragIndicatorIcon, {}) }) }), _jsxs(Grid, { size: size, children: [index + 1, " - ", data.type, " / ", data.space] }), _jsxs(Grid, { size: size, children: [data.name, " - ", data.label] }), _jsxs(Grid, { size: smallSize, children: [_jsx(IconButton, { size: "small", title: labels.delete, onClick: () => doChange(items.filter((item) => item.name !== data.name)), children: _jsx(DeleteIcon, {}) }), _jsx(IconButton, { size: "small", title: labels.edit, onClick: () => editItem(data), children: _jsx(EditIcon, {}) })] })] })) }) }) }) })] }));
}
/**
* Custom attribute area
* @param props Properties
* @returns Component
*/
export function CustomAttributeArea(props) {
// Global app
const app = useRequiredAppContext();
// Destruct
const { label = app.get("attributeDefinition"), ...rest } = props;
const ref = React.useRef([]);
const showUI = (input) => {
app.showInputDialog({
title: label,
message: "",
fullScreen: true,
callback: (form) => {
if (form == null) {
return;
}
input.value = ref.current.length > 0 ? JSON.stringify(ref.current) : "";
},
inputs: (_jsx(InputUIs, { source: input.value, onChange: (items) => (ref.current = items) }))
});
};
// Layout
return _jsx(JsonTextField, { label: label, onEdit: showUI, isArray: true, ...rest });
}