From 5584663f2906322e19ac17ac3b889b8a0959af84 Mon Sep 17 00:00:00 2001
From: Maximilian Giller <m.giller@tu-bs.de>
Date: Tue, 19 Jul 2022 00:51:12 +0200
Subject: [PATCH] Implements somewhat working node type editor

---
 src/editor/js/components/editor.tsx          | 84 +++++++++++---------
 src/editor/js/components/nodetypeentry.css   |  7 ++
 src/editor/js/components/nodetypeentry.tsx   | 83 +++++++++++++++++++
 src/editor/js/components/nodetypeseditor.css |  5 ++
 src/editor/js/components/nodetypeseditor.tsx | 45 +++++++++++
 5 files changed, 185 insertions(+), 39 deletions(-)
 create mode 100644 src/editor/js/components/nodetypeentry.css
 create mode 100644 src/editor/js/components/nodetypeentry.tsx
 create mode 100644 src/editor/js/components/nodetypeseditor.css
 create mode 100644 src/editor/js/components/nodetypeseditor.tsx

diff --git a/src/editor/js/components/editor.tsx b/src/editor/js/components/editor.tsx
index ac1d650..6c33719 100644
--- a/src/editor/js/components/editor.tsx
+++ b/src/editor/js/components/editor.tsx
@@ -10,6 +10,7 @@ import { Node } from "../structures/graph/node";
 import { HistoryNavigator } from "./historynavigator";
 import { GraphElement } from "../structures/graph/graphelement";
 import { Link } from "../structures/graph/link";
+import { NodeTypesEditor } from "./nodetypeseditor";
 
 type propTypes = any;
 type stateTypes = {
@@ -420,45 +421,50 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
                             onChange={this.forceUpdate}
                         />
                         <hr />
-                        <div>
-                            <input
-                                id="node-labe-visibility"
-                                type={"checkbox"}
-                                checked={this.state.visibleLabels}
-                                onChange={(event) => {
-                                    const newValue = event.target.checked;
-                                    if (newValue == this.state.visibleLabels) {
-                                        return;
-                                    }
-
-                                    this.setState({
-                                        visibleLabels: newValue,
-                                    });
-                                }}
-                            />
-                            <label htmlFor="node-labe-visibility">
-                                Node labels
-                            </label>
-                            <br />
-                            <input
-                                id="connect-on-drag"
-                                type={"checkbox"}
-                                checked={this.state.connectOnDrag}
-                                onChange={(event) => {
-                                    const newValue = event.target.checked;
-                                    if (newValue == this.state.connectOnDrag) {
-                                        return;
-                                    }
-
-                                    this.setState({
-                                        connectOnDrag: newValue,
-                                    });
-                                }}
-                            />
-                            <label htmlFor="connect-on-drag">
-                                Connect nodes when dragged
-                            </label>
-                        </div>
+                        <h3>Node types</h3>
+                        <NodeTypesEditor
+                            onChange={this.forceUpdate}
+                            graph={this.state.graph}
+                        />
+                        <hr />
+                        <h3>Settings</h3>
+                        <input
+                            id="node-labe-visibility"
+                            type={"checkbox"}
+                            checked={this.state.visibleLabels}
+                            onChange={(event) => {
+                                const newValue = event.target.checked;
+                                if (newValue == this.state.visibleLabels) {
+                                    return;
+                                }
+
+                                this.setState({
+                                    visibleLabels: newValue,
+                                });
+                            }}
+                        />
+                        <label htmlFor="node-labe-visibility">
+                            Node labels
+                        </label>
+                        <br />
+                        <input
+                            id="connect-on-drag"
+                            type={"checkbox"}
+                            checked={this.state.connectOnDrag}
+                            onChange={(event) => {
+                                const newValue = event.target.checked;
+                                if (newValue == this.state.connectOnDrag) {
+                                    return;
+                                }
+
+                                this.setState({
+                                    connectOnDrag: newValue,
+                                });
+                            }}
+                        />
+                        <label htmlFor="connect-on-drag">
+                            Connect nodes when dragged
+                        </label>
                     </div>
                     {this.state.graph ? (
                         <ReactForceGraph2d
diff --git a/src/editor/js/components/nodetypeentry.css b/src/editor/js/components/nodetypeentry.css
new file mode 100644
index 0000000..d40ed0c
--- /dev/null
+++ b/src/editor/js/components/nodetypeentry.css
@@ -0,0 +1,7 @@
+div#ks-editor .node-type-name {
+    width: 45%;
+}
+
+div#ks-editor .node-type-color {
+    width: 5rem;
+}
diff --git a/src/editor/js/components/nodetypeentry.tsx b/src/editor/js/components/nodetypeentry.tsx
new file mode 100644
index 0000000..42fa108
--- /dev/null
+++ b/src/editor/js/components/nodetypeentry.tsx
@@ -0,0 +1,83 @@
+import React from "react";
+import { ReactNode } from "react";
+import { Graph } from "../structures/graph/graph";
+import { NodeType } from "../structures/graph/nodetype";
+import "./nodetypeentry.css";
+
+type propTypes = {
+    graph: Graph;
+    type: NodeType;
+    onChange: { (): void };
+};
+
+export class NodeTypeEntry extends React.Component<propTypes> {
+    constructor(props: propTypes) {
+        super(props);
+        this.deleteType = this.deleteType.bind(this);
+        this.handleTextChange = this.handleTextChange.bind(this);
+    }
+
+    private deleteType() {
+        this.props.type.delete();
+    }
+
+    private isValidColor(color: string): boolean {
+        if (color.length <= 0 || color[0] !== "#") {
+            return false;
+        }
+
+        const colorCode = color.substring(1);
+
+        if (!(colorCode.length === 3 || colorCode.length === 6)) {
+            return false;
+        }
+
+        colorCode;
+    }
+
+    /**
+     * Generic function for handeling a changing text input and applying the new value to the node type.
+     * @param event Change event of text input.
+     * @param property Property to give new value.
+     */
+    private handleTextChange(event: any, property: string) {
+        const newValue = event.target.value;
+
+        // Actual change?
+        if ((this.props.type as any)[property] == newValue) {
+            return;
+        }
+
+        //TODO: Make sure, that this event is not triggered to quickly!
+        (this.props.type as any)[property] = newValue;
+        this.props.type.graph.storeCurrentData(
+            "Changed " + property + " of type [" + this.props.type + "]"
+        );
+
+        this.props.onChange();
+    }
+
+    render(): ReactNode {
+        return (
+            <li className="node-type">
+                <input
+                    className="node-type-name"
+                    type={"text"}
+                    value={this.props.type.name}
+                    onChange={(event) => this.handleTextChange(event, "name")}
+                />
+                <input
+                    className="node-type-color"
+                    type={"text"}
+                    value={this.props.type.color}
+                    onChange={(event) => this.handleTextChange(event, "color")}
+                />
+                {this.props.graph && this.props.graph.types.length > 1 ? (
+                    <button onClick={this.deleteType}>Delete</button>
+                ) : (
+                    ""
+                )}
+            </li>
+        );
+    }
+}
diff --git a/src/editor/js/components/nodetypeseditor.css b/src/editor/js/components/nodetypeseditor.css
new file mode 100644
index 0000000..db57894
--- /dev/null
+++ b/src/editor/js/components/nodetypeseditor.css
@@ -0,0 +1,5 @@
+div#ks-editor #node-types-editor ul {
+    list-style-type: none;
+    margin: 0;
+    padding: 0;
+}
diff --git a/src/editor/js/components/nodetypeseditor.tsx b/src/editor/js/components/nodetypeseditor.tsx
new file mode 100644
index 0000000..b649a1d
--- /dev/null
+++ b/src/editor/js/components/nodetypeseditor.tsx
@@ -0,0 +1,45 @@
+import React from "react";
+import { ReactNode } from "react";
+import { Graph } from "../structures/graph/graph";
+import "./nodetypeseditor.css";
+import { NodeTypeEntry } from "./nodetypeentry";
+import { NodeType } from "../structures/graph/nodetype";
+
+type propTypes = {
+    graph: Graph;
+    onChange: { (): void };
+};
+
+export class NodeTypesEditor extends React.Component<propTypes> {
+    constructor(props: propTypes) {
+        super(props);
+        this.addType = this.addType.bind(this);
+    }
+
+    private addType() {
+        const type = new NodeType(this.props.graph);
+        type.add();
+    }
+
+    render(): ReactNode {
+        if (this.props.graph === undefined) {
+            return "No graph selected.";
+        }
+
+        return (
+            <div id="node-types-editor">
+                <ul>
+                    {this.props.graph.types.map((type) => (
+                        <NodeTypeEntry
+                            onChange={this.props.onChange}
+                            key={type.id}
+                            type={type}
+                            graph={this.props.graph}
+                        />
+                    ))}
+                </ul>
+                <button onClick={this.addType}>Add type</button>
+            </div>
+        );
+    }
+}
-- 
GitLab