edit variable definition
This commit is contained in:
1 parent
46d13cac7b
commit
b9345c6706
14 files changed
+515
-44
No files matched your search
@@ -464,6 +464,8 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
|
||||
editor.export = editTree.data.export !== false;
|
||||
if (editor.data.firstid !== editTree.data.firstid) {
|
||||
editor.firstid = editTree.data.firstid ?? 1;
|
||||
editor.declare.vars = editTree.data.declare.vars || [];
|
||||
editor.declare.imports = editTree.data.declare.imports || [];
|
||||
refresh();
|
||||
}
|
||||
onChange();
|
||||
@@ -1042,7 +1044,10 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
|
||||
useKeyDown(Hotkey.Undo, keysRef, () => undo());
|
||||
useKeyDown(Hotkey.Redo, keysRef, () => redo());
|
||||
useKeyDown([Hotkey.Insert, Hotkey.Enter], keysRef, () => createNode());
|
||||
useKeyDown([Hotkey.Delete, Hotkey.Backspace], keysRef, () => deleteNode());
|
||||
useKeyDown([Hotkey.Delete, Hotkey.Backspace], keysRef, () => {
|
||||
console.log("delete");
|
||||
deleteNode();
|
||||
});
|
||||
|
||||
editor.dispatch = (event: EditEvent, data: unknown) => {
|
||||
switch (event) {
|
||||
@@ -1055,6 +1060,7 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
|
||||
break;
|
||||
}
|
||||
case "delete": {
|
||||
console.log("delete1");
|
||||
deleteNode();
|
||||
break;
|
||||
}
|
||||
@@ -1217,6 +1223,8 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
|
||||
} else if ((e.ctrlKey || e.metaKey) && e.code === "KeyG") {
|
||||
searchByType("id");
|
||||
}
|
||||
console.log(e.code);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const recursiveOpenCollapsed = (id: string) => {
|
||||
@@ -1281,7 +1289,7 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
|
||||
}
|
||||
onFocus={() => setFilterOption({ ...filterOption, isFocused: true })}
|
||||
onBlur={() => setFilterOption({ ...filterOption, isFocused: false })}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyDownCapture={handleKeyDown}
|
||||
suffix={
|
||||
<Flex gap="2px" style={{ alignItems: "center" }}>
|
||||
{filterOption.filterType !== "id" && (
|
||||
|
||||
+278
-22
@@ -1,4 +1,4 @@
|
||||
import { EditOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import { AimOutlined, EditOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
AutoComplete,
|
||||
Button,
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
} from "antd";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
@@ -19,6 +20,7 @@ import { useDebounceCallback } from "usehooks-ts";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import { EditNode, EditTree, useWorkspace } from "../contexts/workspace-context";
|
||||
import {
|
||||
ImportDef,
|
||||
isBoolType,
|
||||
isEnumType,
|
||||
isExprType,
|
||||
@@ -28,6 +30,7 @@ import {
|
||||
isStringType,
|
||||
NodeModel,
|
||||
TreeGraphData,
|
||||
VarDef,
|
||||
} from "../misc/b3type";
|
||||
import {
|
||||
checkNodeArgValue,
|
||||
@@ -37,6 +40,7 @@ import {
|
||||
isNodeArgOptional,
|
||||
isValidVariableName,
|
||||
isVariadic,
|
||||
loadImportedVars,
|
||||
} from "../misc/b3util";
|
||||
import { Hotkey, isMacos } from "../misc/keys";
|
||||
import { mergeClassNames } from "../misc/util";
|
||||
@@ -50,6 +54,9 @@ const TreeInspector: FC = () => {
|
||||
useShallow((state) => ({
|
||||
editing: state.editing,
|
||||
editingTree: state.editingTree!,
|
||||
allFiles: state.allFiles,
|
||||
relative: state.relative,
|
||||
fileTree: state.fileTree,
|
||||
}))
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
@@ -58,13 +65,31 @@ const TreeInspector: FC = () => {
|
||||
// set form values
|
||||
useEffect(() => {
|
||||
const data = workspace.editingTree.data;
|
||||
loadImportedVars(data.declare.imports);
|
||||
form.resetFields();
|
||||
form.setFieldValue("name", data.name);
|
||||
form.setFieldValue("desc", data.desc);
|
||||
form.setFieldValue("export", data.export !== false);
|
||||
form.setFieldValue("firstid", data.firstid);
|
||||
form.setFieldValue("declare.vars", data.declare?.vars);
|
||||
form.setFieldValue("declare.imports", data.declare?.imports);
|
||||
}, [workspace.editingTree]);
|
||||
|
||||
// auto complete for subtree
|
||||
const subtreeOptions = useMemo(() => {
|
||||
const options: OptionType[] = [];
|
||||
workspace.allFiles.forEach((file) => {
|
||||
const value = workspace.relative(file.path);
|
||||
const desc = ""; //fileNode.desc ? `(${fileNode.desc})` : "";
|
||||
options.push({
|
||||
label: `${value}${desc}`,
|
||||
value: value,
|
||||
});
|
||||
});
|
||||
options.sort((a, b) => a.value.localeCompare(b.value));
|
||||
return options;
|
||||
}, [workspace.allFiles, workspace.fileTree]);
|
||||
|
||||
const finish = (values: any) => {
|
||||
workspace.editing?.dispatch("updateTree", {
|
||||
data: {
|
||||
@@ -72,6 +97,18 @@ const TreeInspector: FC = () => {
|
||||
desc: values.desc || undefined,
|
||||
export: values.export,
|
||||
firstid: Number(values.firstid),
|
||||
declare: {
|
||||
vars: (values["declare.vars"] as VarDef[])
|
||||
.filter((v) => v && v.name)
|
||||
.sort((a, b) => a.name.localeCompare(b.name)),
|
||||
imports: (values["declare.imports"] as ImportDef[])
|
||||
.filter((v) => v && v.path)
|
||||
.sort((a, b) => a.path.localeCompare(b.path))
|
||||
.map((v) => ({
|
||||
path: v.path,
|
||||
vars: v.vars ?? [],
|
||||
})),
|
||||
},
|
||||
},
|
||||
} as EditTree);
|
||||
};
|
||||
@@ -85,19 +122,238 @@ const TreeInspector: FC = () => {
|
||||
className={mergeClassNames("b3-inspector-content", isMacos ? "" : "b3-overflow")}
|
||||
style={{ overflow: "auto", height: "100%" }}
|
||||
>
|
||||
<Form form={form} labelCol={{ span: 8 }} onFinish={finish}>
|
||||
<Form.Item name="name" label={t("tree.name")}>
|
||||
<Input disabled={true} />
|
||||
</Form.Item>
|
||||
<Form.Item name="desc" label={t("tree.desc")}>
|
||||
<TextArea autoSize onBlur={form.submit} />
|
||||
</Form.Item>
|
||||
<Form.Item name="firstid" label={t("tree.firstid")}>
|
||||
<InputNumber min={1} onBlur={form.submit} />
|
||||
</Form.Item>
|
||||
<Form.Item name="export" label={t("tree.export")} valuePropName="checked">
|
||||
<Switch onChange={() => form.submit()} />
|
||||
</Form.Item>
|
||||
<Form
|
||||
form={form}
|
||||
wrapperCol={{ span: "auto" }}
|
||||
labelCol={{ span: "auto" }}
|
||||
onFinish={finish}
|
||||
>
|
||||
<>
|
||||
<Form.Item name="name" label={t("tree.name")}>
|
||||
<Input disabled={true} />
|
||||
</Form.Item>
|
||||
<Form.Item name="desc" label={t("tree.desc")}>
|
||||
<TextArea autoSize onBlur={form.submit} />
|
||||
</Form.Item>
|
||||
<Form.Item name="firstid" label={t("tree.firstid")}>
|
||||
<InputNumber min={1} onBlur={form.submit} />
|
||||
</Form.Item>
|
||||
<Form.Item name="export" label={t("tree.export")} valuePropName="checked">
|
||||
<Switch onChange={() => form.submit()} />
|
||||
</Form.Item>
|
||||
</>
|
||||
<>
|
||||
<Divider orientation="left">
|
||||
<h4>{t("tree.vars")}</h4>
|
||||
</Divider>
|
||||
<Form.List name="declare.vars">
|
||||
{(items, { add, remove }, { errors }) => (
|
||||
<div style={{ display: "flex", flexDirection: "column", rowGap: 0 }}>
|
||||
{items.map((item) => (
|
||||
<Flex key={item.key} gap={4}>
|
||||
<Space.Compact
|
||||
className="b3-inspector-vars-item"
|
||||
style={{ width: "100%", marginBottom: 2 }}
|
||||
>
|
||||
<Form.Item
|
||||
noStyle
|
||||
name={[item.name, "name"]}
|
||||
validateTrigger={["onChange", "onBlur"]}
|
||||
rules={[
|
||||
{
|
||||
validator(_, value) {
|
||||
if (value && /^[_\w]+\w*$/.test(value)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(new Error(t("tree.vars.invalidName")));
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
addonBefore={
|
||||
<div style={{ display: "flex", alignItems: "center", width: "45px" }}>
|
||||
<AimOutlined />
|
||||
<span style={{ marginLeft: 4 }}>0</span>
|
||||
</div>
|
||||
}
|
||||
placeholder={t("tree.vars.name")}
|
||||
onBlur={form.submit}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
noStyle
|
||||
name={[item.name, "desc"]}
|
||||
validateTrigger={["onChange", "onBlur"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("fieldRequired", { field: t("tree.vars.desc") }),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
style={{ width: "75%" }}
|
||||
placeholder={t("tree.vars.desc")}
|
||||
onBlur={form.submit}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Space.Compact>
|
||||
<MinusCircleOutlined
|
||||
style={{ marginBottom: "6px" }}
|
||||
onClick={() => {
|
||||
remove(item.name);
|
||||
form.submit();
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
))}
|
||||
<Form.Item
|
||||
style={{
|
||||
marginRight: items.length === 0 ? undefined : "18px",
|
||||
marginTop: 4,
|
||||
alignItems: "end",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add({});
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
{t("add")}
|
||||
</Button>
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
</Form.List>
|
||||
</>
|
||||
<>
|
||||
<Divider orientation="left">
|
||||
<h4>{t("tree.vars.imports")}</h4>
|
||||
</Divider>
|
||||
<Form.List name="declare.imports">
|
||||
{(items, { add, remove }, { errors }) => (
|
||||
<div style={{ display: "flex", flexDirection: "column", rowGap: 4 }}>
|
||||
{items.map((item) => (
|
||||
<Space.Compact
|
||||
key={item.key}
|
||||
className="b3-inspector-import-item"
|
||||
direction="vertical"
|
||||
style={{ marginBottom: 5 }}
|
||||
>
|
||||
<Flex gap={4} style={{ width: "100%" }}>
|
||||
<Form.Item
|
||||
name={[item.name, "path"]}
|
||||
style={{ width: "100%", marginBottom: 2 }}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
options={subtreeOptions}
|
||||
onBlur={form.submit}
|
||||
onInputKeyDown={(e) => e.code === Hotkey.Escape && e.preventDefault()}
|
||||
filterOption={(value, option) => {
|
||||
const label = option!.label as string;
|
||||
return label.toUpperCase().includes(value.toUpperCase());
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<MinusCircleOutlined
|
||||
style={{ marginBottom: "6px" }}
|
||||
onClick={() => {
|
||||
remove(item.name);
|
||||
form.submit();
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Form.List name={[item.name, "vars"]}>
|
||||
{(vars) => (
|
||||
<div style={{ display: "flex", flexDirection: "column", rowGap: 0 }}>
|
||||
{vars.map((v, idx, arr) => (
|
||||
<Flex key={v.key} gap={4}>
|
||||
<Space.Compact
|
||||
className="b3-inspector-vars-item"
|
||||
style={{ width: "100%", marginBottom: 0, paddingRight: 18 }}
|
||||
>
|
||||
<Form.Item noStyle name={[v.name, "id"]}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
paddingLeft: "8px",
|
||||
paddingRight: "8px",
|
||||
maxWidth: "60px",
|
||||
minWidth: "60px",
|
||||
borderTopLeftRadius: idx === 0 ? "4px" : 0,
|
||||
borderBottomLeftRadius: idx === arr.length - 1 ? "4px" : 0,
|
||||
borderLeft: "1px solid #3d506c",
|
||||
borderTop: "1px solid #3d506c",
|
||||
borderBottom:
|
||||
idx === arr.length - 1 ? "1px solid #3d506c" : "none",
|
||||
}}
|
||||
>
|
||||
<AimOutlined />
|
||||
<span style={{ marginLeft: 4 }}>0</span>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle name={[v.name, "name"]}>
|
||||
<Input
|
||||
disabled={true}
|
||||
placeholder={t("tree.vars.name")}
|
||||
style={{
|
||||
borderRadius: "0",
|
||||
borderTop: "1px solid #3d506c",
|
||||
borderBottom:
|
||||
idx === arr.length - 1 ? "1px solid #3d506c" : "none",
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle name={[v.name, "desc"]}>
|
||||
<Input
|
||||
disabled={true}
|
||||
placeholder={t("tree.vars.desc")}
|
||||
style={{
|
||||
borderTopRightRadius: idx === 0 ? "4px" : 0,
|
||||
borderBottomRightRadius: idx === arr.length - 1 ? "4px" : 0,
|
||||
borderTop: "1px solid #3d506c",
|
||||
borderBottom:
|
||||
idx === arr.length - 1 ? "1px solid #3d506c" : "none",
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Space.Compact>
|
||||
</Flex>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Form.List>
|
||||
</Space.Compact>
|
||||
))}
|
||||
<Form.Item
|
||||
style={{
|
||||
marginRight: items.length === 0 ? undefined : "18px",
|
||||
alignItems: "end",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add({});
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
{t("add")}
|
||||
</Button>
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
</Form.List>
|
||||
</>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
@@ -490,7 +746,7 @@ const NodeInspector: FC = () => {
|
||||
style={{ width: fields.length === 0 ? "100%" : "200px" }}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
{t("node.input.add")}
|
||||
{t("add")}
|
||||
</Button>
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Form.Item>
|
||||
@@ -506,7 +762,7 @@ const NodeInspector: FC = () => {
|
||||
name={`input.${i}`}
|
||||
key={`input.${i}`}
|
||||
rules={[
|
||||
{ required, message: t("node.fileRequired", { field: desc }) },
|
||||
{ required, message: t("fieldRequired", { field: desc }) },
|
||||
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
||||
validator(_, value) {
|
||||
if (value && !isValidVariableName(value)) {
|
||||
@@ -548,7 +804,7 @@ const NodeInspector: FC = () => {
|
||||
onInputKeyDown={(e) => e.code === Hotkey.Escape && e.preventDefault()}
|
||||
filterOption={(inputValue: string, option?: OptionType) => {
|
||||
const label = option!.label as string;
|
||||
return label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1;
|
||||
return label.toUpperCase().includes(inputValue.toUpperCase());
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -585,7 +841,7 @@ const NodeInspector: FC = () => {
|
||||
rules={[
|
||||
{
|
||||
required,
|
||||
message: t("node.fileRequired", { field: arg.desc }),
|
||||
message: t("fieldRequired", { field: arg.desc }),
|
||||
},
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
@@ -681,7 +937,7 @@ const NodeInspector: FC = () => {
|
||||
icon={<PlusOutlined />}
|
||||
danger={items.length === 0 && !isNodeArgOptional(arg)}
|
||||
>
|
||||
{t("node.args.add")}
|
||||
{t("add")}
|
||||
</Button>
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Form.Item>
|
||||
@@ -699,7 +955,7 @@ const NodeInspector: FC = () => {
|
||||
initialValue={isBoolType(type) ? arg.default ?? false : arg.default}
|
||||
valuePropName={isBoolType(type) ? "checked" : undefined}
|
||||
rules={[
|
||||
{ required, message: t("node.fileRequired", { field: arg.desc }) },
|
||||
{ required, message: t("fieldRequired", { field: arg.desc }) },
|
||||
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
||||
validator(_, value) {
|
||||
if (value && isJsonType(type)) {
|
||||
@@ -861,7 +1117,7 @@ const NodeInspector: FC = () => {
|
||||
style={{ width: fields.length === 0 ? "100%" : "200px" }}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
{t("node.output.add")}
|
||||
{t("add")}
|
||||
</Button>
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Form.Item>
|
||||
@@ -877,7 +1133,7 @@ const NodeInspector: FC = () => {
|
||||
name={`output.${i}`}
|
||||
key={`output.${i}`}
|
||||
rules={[
|
||||
{ required, message: t("node.fileRequired", { field: desc }) },
|
||||
{ required, message: t("fieldRequired", { field: desc }) },
|
||||
{
|
||||
validator(_, value) {
|
||||
if (value && !isValidVariableName(value)) {
|
||||
|
||||
Reference in new issue
Block a user