From 472bbbfbb8ff0d5fd09a81dbd0cf1b2f4f5c9742 Mon Sep 17 00:00:00 2001 From: codetypes Date: Tue, 23 Jul 2024 10:07:48 +0800 Subject: [PATCH] support object type and oneof check --- public/locales/en.json | 13 +++-- public/locales/zh.json | 11 ++-- src/components/inspector.tsx | 110 +++++++++++++++++++++++++++++++++-- src/misc/b3type.ts | 5 +- src/misc/b3util.ts | 92 +++++++++++++++++++---------- 5 files changed, 183 insertions(+), 48 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 3576688..f72653c 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -82,24 +82,27 @@ "regnode.subtree": "Subtree: ", "node.args": "Const arguments", - "node.children": "Subnode limit", "node.children.unlimited": "Unlimited", + "node.children": "Subnode limit", "node.debug": "Debug", - "node.disabled": "Disabled", "node.deleteRootNodeDenied": "Unable to delete the root node", - "node.editSubtreeDenied": "Unable to edit the subtree node", "node.desc": "Node desc", - "node.fileRequired": "field '{{field}}' is required", + "node.disabled": "Disabled", + "node.editSubtreeDenied": "Unable to edit the subtree node", + "node.fileRequired": "Field '{{field}}' is required", "node.id": "Node id", "node.inputVariable": "Input variables", + "node.invalidValue": "Invalid value", "node.name": "Node name", - "node.type": "Node type", "node.noNodeSelected": "No node selected", + "node.oneof.error": "Only one is allowed between input '{{input}}' and argument '{{arg}}({{desc}})'", + "node.oneof.inputNotfound": "Input '{{input}}' not found", "node.outputVariable": "Output variables", "node.pasteDataError": "Paste data error", "node.rootNodeAsSubtree": "Root node cann't be save as subtree", "node.subtree": "Subtree", "node.subtreePathError": "You should save the subtree inside the current workspace", + "node.type": "Node type", "node.unknown.desc": "New node" } diff --git a/public/locales/zh.json b/public/locales/zh.json index 29df5f1..19c8695 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -82,24 +82,27 @@ "regnode.subtree": "子树:", "node.args": "常量参数", - "node.children": "子节点数量", "node.children.unlimited": "无限制", + "node.children": "子节点数量", "node.debug": "调试", - "node.disabled": "禁用", "node.deleteRootNodeDenied": "不能删除根节点", - "node.editSubtreeDenied": "不能编辑子树节点", "node.desc": "节点说明", + "node.disabled": "禁用", + "node.editSubtreeDenied": "不能编辑子树节点", "node.fileRequired": "字段“{{field}}”必填", "node.id": "节点标识", "node.inputVariable": "输入变量", + "node.invalidValue": "无效的值", "node.name": "节点名称", - "node.type": "节点类型", "node.noNodeSelected": "未选择任务节点", + "node.oneof.error": "输入项 “{{input}}” 和参数 “{{arg}}({{desc}})” 之中,只能选择设置其中之一", + "node.oneof.inputNotfound": "输入项“{{input}}”不存在", "node.outputVariable": "输出变量", "node.pasteDataError": "粘贴数据出错", "node.rootNodeAsSubtree": "根节点不能保存为子树", "node.subtree": "子树", "node.subtreePathError": "子树必须保存在当前工作区的目录内", + "node.type": "节点类型", "node.unknown.desc": "新建节点" } diff --git a/src/components/inspector.tsx b/src/components/inspector.tsx index ee9b9cc..6585c27 100644 --- a/src/components/inspector.tsx +++ b/src/components/inspector.tsx @@ -1,5 +1,5 @@ import { EditNode, EditTree, useWorkspace } from "@/contexts/workspace-context"; -import { NodeArgType, NodeModel, TreeGraphData } from "@/misc/b3type"; +import { NodeArg, NodeArgType, NodeModel, TreeGraphData } from "@/misc/b3type"; import { Hotkey, isMacos } from "@/misc/keys"; import { EditOutlined } from "@ant-design/icons"; import { @@ -107,7 +107,14 @@ const NodeInspector: FC = () => { form.setFieldValue("children", def.children); } def.args?.forEach((v) => { - form.setFieldValue(`args.${v.name}`, data.args?.[v.name] ?? v.default); + if (v.type === "object" || v.type === "object?") { + form.setFieldValue( + `args.${v.name}`, + JSON.stringify(data.args?.[v.name] ?? v.default, null, 2) + ); + } else { + form.setFieldValue(`args.${v.name}`, data.args?.[v.name] ?? v.default); + } }); def.input?.forEach((_, i) => { form.setFieldValue(`input.${i}`, data.input?.[i]); @@ -115,6 +122,7 @@ const NodeInspector: FC = () => { def.output?.forEach((_, i) => { form.setFieldValue(`output.${i}`, data.output?.[i]); }); + form.validateFields(); }, [workspace.editingNode]); // auto complete for node @@ -183,7 +191,11 @@ const NodeInspector: FC = () => { const v = values[`args.${arg.name}`]; if (v !== null && v !== undefined && v !== "") { data.args ||= {}; - data.args[arg.name] = v; + if (arg.type === "object" || arg.type === "object?") { + data.args[arg.name] = JSON.parse(v); + } else { + data.args[arg.name] = v; + } } }); @@ -305,7 +317,44 @@ const NodeInspector: FC = () => { label={desc} name={`input.${i}`} key={`input.${i}`} - rules={[{ required, message: t("node.fileRequired", { field: desc }) }]} + rules={[ + { required, message: t("node.fileRequired", { field: desc }) }, + ({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({ + validator(_, value) { + const arg = def.args?.find((a) => a.oneof && v.startsWith(a.oneof)); + if (arg) { + const argName = `args.${arg.name}`; + const argValue = getFieldValue(argName) ?? ""; + value = value ?? ""; + if ( + (value === "" && argValue === "") || + (value !== "" && argValue !== "") + ) { + if (!isFieldValidating(argName)) { + setFieldValue(`input.${i}`, value); + validateFields([argName]); + } + return Promise.reject( + new Error( + t("node.oneof.error", { + input: v, + arg: arg.name, + desc: arg.desc ?? "", + }) + ) + ); + } else { + if (!isFieldValidating(argName)) { + validateFields([argName]); + } + return Promise.resolve(); + } + } + + return Promise.resolve(); + }, + }), + ]} > { key={`args.${v.name}`} initialValue={type === "boolean" ? v.default ?? false : v.default} valuePropName={type === "boolean" ? "checked" : undefined} - rules={[{ required, message: t("node.fileRequired", { field: v.desc }) }]} + rules={[ + { required, message: t("node.fileRequired", { field: v.desc }) }, + ({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({ + validator(_, value) { + if (value && (v.type == "object" || v.type == "object?")) { + try { + JSON.parse(value); + } catch (e) { + return Promise.reject(new Error(t("node.invalidValue"))); + } + } + if (v.oneof === undefined) { + return Promise.resolve(); + } + const idx = def.input?.findIndex((input) => input.startsWith(v.oneof!)); + if (idx === undefined || idx < 0) { + return Promise.reject( + new Error(t("node.oneof.inputNotfound", { input: v.oneof })) + ); + } + const inputName = `input.${idx}`; + const inputValue = getFieldValue(inputName) ?? ""; + value = value ?? ""; + if ( + (value === "" && inputValue === "") || + (value !== "" && inputValue !== "") + ) { + if (!isFieldValidating(inputName)) { + setFieldValue(`args.${v.name}`, value); + validateFields([inputName]); + } + return Promise.reject( + new Error( + t("node.oneof.error", { + input: def.input![idx], + arg: v.name, + desc: v.desc ?? "", + }) + ) + ); + } else { + if (!isFieldValidating(inputName)) { + validateFields([inputName]); + } + return Promise.resolve(); + } + }, + }), + ]} > {type === "string" && (