double click to edit subtree

This commit is contained in:
codetypes committed 2024-03-13 00:28:37 +08:00
1 parent 6b5abae6a3
commit fed08a0f45
9 files changed
+176 -108

No files matched your search

+41 -18
View File
@@ -25,6 +25,7 @@ const { Item } = Form;
const { Option } = Select;
interface NodePanelProps {
isSubtreeNode: boolean;
model: BehaviorNodeModel;
settings: Settings;
updateNode: (id: string, forceUpdate: boolean) => void;
@@ -151,7 +152,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
};
render() {
const { model, settings } = this.props;
const { model, settings, isSubtreeNode } = this.props;
const conf = settings.getNodeConf(model.name);
const title = conf.desc;
@@ -178,6 +179,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
</Item>
<Item label="节点名称" name="name">
<AutoComplete
disabled={isSubtreeNode}
options={options}
onBlur={this.handleSubmit}
filterOption={(inputValue: string, option: any) => {
@@ -189,24 +191,24 @@ export default class NodePanel extends React.Component<NodePanelProps> {
/>
</Item>
<Item label="节点说明" name="desc">
<Input onBlur={this.handleSubmit} />
<Input disabled={isSubtreeNode} onBlur={this.handleSubmit} />
</Item>
<Item label="调试开关" name="debug" valuePropName="checked">
<Switch onChange={this.handleSubmit} />
<Switch disabled={isSubtreeNode} onChange={this.handleSubmit} />
</Item>
<Item label="子树" name="path">
<Input onBlur={this.handleSubmit} />
<Input disabled={isSubtreeNode && !model.path} onBlur={this.handleSubmit} />
</Item>
<Markdown source={conf.doc} />
{this.renderInputs(conf)}
{this.renderArgs(conf)}
{this.renderOutputs(conf)}
{this.renderInputs(conf, isSubtreeNode)}
{this.renderArgs(conf, isSubtreeNode)}
{this.renderOutputs(conf, isSubtreeNode)}
</Form>
</Card>
);
}
renderArgs(conf: BehaviorNodeTypeModel) {
renderArgs(conf: BehaviorNodeTypeModel, isSubtreeNode: boolean) {
if (!conf || !conf.args || conf.args.length == 0) {
return null;
}
@@ -215,24 +217,41 @@ export default class NodePanel extends React.Component<NodePanelProps> {
const normalArgs = (e: ArgsDefType) => {
const required = e.type.indexOf("?") == -1;
if (e.type.indexOf("string") >= 0) {
return <Input onBlur={this.handleSubmit} />;
return <Input disabled={isSubtreeNode} onBlur={this.handleSubmit} />;
} else if (e.type.indexOf("int") >= 0) {
return (
<InputNumber
disabled={isSubtreeNode}
precision={0}
style={{ width: "100%" }}
onBlur={this.handleSubmit}
/>
);
} else if (e.type.indexOf("float") >= 0) {
return <InputNumber style={{ width: "100%" }} onBlur={this.handleSubmit} />;
return (
<InputNumber
disabled={isSubtreeNode}
style={{ width: "100%" }}
onBlur={this.handleSubmit}
/>
);
} else if (e.type.indexOf("boolean") >= 0) {
return <Switch onChange={this.handleSubmit} />;
return <Switch disabled={isSubtreeNode} onChange={this.handleSubmit} />;
} else if (e.type.indexOf("code") >= 0) {
return <Input onBlur={this.handleSubmit} placeholder={"表达式"} />;
return (
<Input
disabled={isSubtreeNode}
onBlur={this.handleSubmit}
placeholder={"表达式"}
/>
);
} else if (e.type.indexOf("enum") >= 0) {
return (
<Select style={{ width: 120 }} onChange={this.handleSubmit}>
<Select
disabled={isSubtreeNode}
style={{ width: 120 }}
onChange={this.handleSubmit}
>
{e.options.map((e) => {
return (
<Option key={e.name} value={e.value}>
@@ -249,7 +268,11 @@ export default class NodePanel extends React.Component<NodePanelProps> {
const customArgs = () => {
return (
<Item name="customArgs" label="自定义" key="customArgs">
<Input.TextArea onBlur={this.handleSubmit} style={{ minHeight: 100 }} />
<Input.TextArea
disabled={isSubtreeNode}
onBlur={this.handleSubmit}
style={{ minHeight: 100 }}
/>
</Item>
);
};
@@ -283,7 +306,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
);
}
renderInputs(conf: BehaviorNodeTypeModel) {
renderInputs(conf: BehaviorNodeTypeModel, isSubtreeNode: boolean) {
if (!conf.input || !conf.input || conf.input.length == 0) {
return null;
}
@@ -296,7 +319,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
{conf.input.map((e, i) => {
return (
<Item label={e} name={`input.${i}`} key={`input.${i}`}>
<Input onBlur={this.handleSubmit} />
<Input disabled={isSubtreeNode} onBlur={this.handleSubmit} />
</Item>
);
})}
@@ -304,7 +327,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
);
}
renderOutputs(conf: BehaviorNodeTypeModel) {
renderOutputs(conf: BehaviorNodeTypeModel, isSubtreeNode: boolean) {
if (!conf.output || !conf.output || conf.output.length == 0) {
return null;
}
@@ -316,7 +339,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
{conf.output.map((e, i) => {
return (
<Item label={e} name={`output.${i}`} key={`output.${i}`}>
<Input onBlur={this.handleSubmit} />
<Input disabled={isSubtreeNode} onBlur={this.handleSubmit} />
</Item>
);
})}
+70 -30
View File
@@ -21,6 +21,7 @@ import "./Editor.css";
export interface EditorProps {
filepath: string;
onChangeSaveState: (unsave: boolean) => void;
onOpenSubtree: (path: string) => void;
}
interface EditorState {
@@ -32,9 +33,9 @@ interface EditorState {
export default class Editor extends React.Component<EditorProps, EditorState> {
private ref: React.RefObject<any>;
state: EditorState = {};
isInEditor: boolean = false;
private graph: TreeGraph;
private isInEditor: boolean = false;
private dragSrcId: string;
private dragDstId: string;
private autoId: number;
@@ -71,6 +72,47 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
}
}
findParent(node: GraphNodeModel) {
if (node.parent) {
return this.graph.findDataById(node.parent) as GraphNodeModel;
} else {
return null;
}
}
findSubtree(node?: GraphNodeModel): GraphNodeModel | null {
if (node?.path) {
return node;
} else if (node?.parent) {
return this.findSubtree(this.findParent(node));
} else {
return null;
}
}
isSubtreeNode(node?: GraphNodeModel): boolean {
if (node?.path) {
return true;
} else if (node?.parent) {
return this.isSubtreeNode(this.findParent(node));
} else {
return false;
}
}
isAncestor(ancestor: GraphNodeModel, node: GraphNodeModel): boolean {
if (ancestor.id === node.parent) {
return true;
} else if (node.parent) {
return this.isAncestor(
ancestor,
this.graph.findDataById(node.parent) as GraphNodeModel
);
} else {
return false;
}
}
componentDidMount() {
const graph = new TreeGraph({
container: this.ref.current,
@@ -80,28 +122,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
maxZoom: 2,
// fitCenter: true,
modes: {
default: [
"drag-canvas",
"zoom-canvas",
"click-select",
"hover",
{
type: "collapse-expand",
trigger: "dblclick",
onChange: (item, collapsed) => {
this.onSelectNode(item.getID());
const data = item.getModel();
data.collapsed = collapsed;
graph.setItemState(item, "collapsed", data.collapsed as boolean);
const icon = data.collapsed ? G6.Marker.expand : G6.Marker.collapse;
const marker = item
.get("group")
.find((ele: any) => ele.get("name") === "collapse-icon");
marker.attr("symbol", icon);
return true;
},
},
],
default: ["drag-canvas", "zoom-canvas", "click-select", "hover"],
},
defaultEdge: {
type: "cubic-horizontal",
@@ -147,6 +168,27 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
this.isInEditor = false;
});
graph.on("node:click", (e: G6GraphEvent) => {
if (e.shape.cfg.name === "collapse-icon") {
const item = e.item;
this.onSelectNode(item.getID());
const data = item.getModel();
if ((data as unknown as GraphNodeModel).children?.length > 0) {
data.collapsed = !data.collapsed;
graph.setItemState(item, "collapsed", data.collapsed as boolean);
e.shape.attr("symbol", data.collapsed ? G6.Marker.expand : G6.Marker.collapse);
this.graph.layout();
}
}
});
graph.on("node:dblclick", (e: G6GraphEvent) => {
const data = this.findSubtree(e.item.getModel() as unknown as GraphNodeModel);
if (data) {
this.props.onOpenSubtree(data.path);
}
});
graph.on("node:mouseenter", (e: G6GraphEvent) => {
const { item } = e;
if (item.hasState("selected")) {
@@ -258,15 +300,15 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
const rootData = graph.findDataById("1");
const srcData = graph.findDataById(srcNodeId) as GraphNodeModel;
const srcParent = Utils.findParent(this.graph, srcData);
const srcParent = this.findParent(srcData);
const dstData = graph.findDataById(dstNode.getID()) as GraphNodeModel;
const dstParent = Utils.findParent(this.graph, dstData);
const dstParent = this.findParent(dstData);
if (!srcParent) {
console.log("no parent!");
return;
}
if (Utils.isAncestor(this.graph, srcData, dstData)) {
if (this.isAncestor(srcData, dstData)) {
// 不能将父节点拖到自已的子孙节点
console.log("cannot move to child");
return;
@@ -379,10 +421,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
this.onSelectNode(null);
this.pushUndoStack();
const parentData = Utils.findParent(
this.graph,
this.graph.findDataById(curNodeId) as GraphNodeModel
);
const parentData = this.findParent(this.graph.findDataById(curNodeId) as GraphNodeModel);
parentData.children = parentData.children.filter((e) => e.id != curNodeId);
this.changeWithoutAnim();
}
@@ -583,6 +622,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
{curNode ? (
<NodePanel
model={curNode}
isSubtreeNode={this.isSubtreeNode(curNode)}
settings={this.settings}
updateNode={(id, forceUpdate) => {
if (forceUpdate) {