UNPKG

@etsoo/materialui

Version:

TypeScript Material-UI Implementation

192 lines (191 loc) 11.4 kB
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 }); }