Skip to content
Snippets Groups Projects
editor.tsx 10.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • import React from "react";
    
    import { DynamicGraph } from "./graph";
    
    import { loadGraphJson } from "../common/datasets";
    import { NodeDetails } from "./components/nodedetails";
    import { SpaceSelect } from "./components/spaceselect";
    
    import "./editor.css";
    
    import * as Helpers from "../common/helpers";
    
    import { Node } from "../common/graph/node";
    
    import { NodeTypesEditor } from "./components/nodetypeseditor";
    import { SpaceManager } from "./components/spacemanager";
    import { SelectLayer } from "./components/selectlayer";
    
    import { GraphData } from "../common/graph/graph";
    import { NodeType } from "../common/graph/nodetype";
    
    import { GraphRenderer2D } from "./renderer";
    import Instructions from "./components/instructions";
    import Settings from "./components/settings";
    
    type propTypes = {
        spaceId: string;
    };
    
    type stateTypes = {
    
        /**
         * Graph structure holding the basic information.
         */
    
    
        /**
         * Should labels on nodes be rendered, or none at all.
         */
    
    
        /**
         * Should feature be enabled, that nodes get connected with a link of dragged close enough to each other?
         */
    
         * Current width of graph object. Used to specifically adjust and correct the graph size.
    
    
        /**
         * True for each key, that is currently considered pressed. If key has not been pressed yet, it will not exist as dict-key.
         */
    
         * Collection of all currently selected nodes. Can also be undefined or empty.
    
    /**
     * Knowledge space graph editor. Allows easy editing of the graph structure.
     */
    
    export class Editor extends React.PureComponent<propTypes, stateTypes> {
    
        private graphContainer: React.RefObject<HTMLDivElement>;
    
        private rendererRef: React.RefObject<GraphRenderer2D>;
    
        constructor(props: propTypes) {
    
    
            // Making sure, all functions retain the proper this-bind
    
            this.loadGraph = this.loadGraph.bind(this);
    
            this.loadSpace = this.loadSpace.bind(this);
    
            this.forceUpdate = this.forceUpdate.bind(this);
    
            this.handleNodeTypeSelect = this.handleNodeTypeSelect.bind(this);
    
            this.handleBoxSelect = this.handleBoxSelect.bind(this);
            this.selectNodes = this.selectNodes.bind(this);
    
            document.addEventListener("keydown", (e) => {
                this.keyPressed(e.key);
            });
            document.addEventListener("keyup", (e) => {
                this.keyReleased(e.key);
            });
    
            this.graphContainer = React.createRef();
    
            // Set as new state
            this.state = {
                graph: undefined,
    
                connectOnDrag: false,
    
        keyPressed(key: string) {
            const keys = this.state.keys;
            keys[key] = true;
            this.setState({ keys: keys });
        }
    
        keyReleased(key: string) {
            const keys = this.state.keys;
            keys[key] = false;
            this.setState({ keys: keys });
        }
    
    
        /**
         * Tries to load initial graph after webpage finished loading.
         */
    
        componentDidMount() {
            if (this.props.spaceId !== undefined) {
                // Load initial space
                this.loadSpace(this.props.spaceId);
            }
    
        }
    
        /**
         * Loads a space from the database to the editor.
         * @param spaceId Id of space to load.
         * @returns Promise with boolean value that is true, if successful.
         */
        public loadSpace(spaceId: string): any {
            return loadGraphJson(spaceId).then(this.loadGraph);
        }
    
        /**
         * Loads another graph based on the data supplied. Note: Naming currently suggests that this only loads a GRAPH, not a SPACE. Needs further work and implementation to see if that makes sense or not.
         * @param data Serialized graph data.
         * @returns True, if successful.
         */
    
        public loadGraph(data: GraphData): boolean {
    
            console.log("Starting to load new graph ...");
    
            console.log(data);
    
            const graph = new DynamicGraph();
            graph.fromSerializedObject(data);
    
            //graph.onChangeCallbacks.push(this.onGraphDataChange);
    
            // Subscribe to global events
    
            window.addEventListener("resize", () => this.handleResize());
    
        /**
         * Processes resize window event. Focusses on resizing the graph accordingly.
         */
    
            const newGraphWidth = Helpers.getClientWidth("knowledge-space-editor");
    
            this.setState({
                graphWidth: newGraphWidth,
            });
        }
    
    
        // /**
        //  * Makes sure to always offer a valid format of the selected nodes. Is either undefined or contains at least one valid node. An empty array is never returned.
        //  */
        // private get selectedNodes(): Node[] {
        //     // TODO: Here are a lot of things that should not be possible by design
        //
        //     // Remove undefines
        //     let selectedNodes = this.state.selectedNodes.filter(
        //         (n: Node) => n !== undefined
        //     );
        //
        //     // Remove duplicates
        //     selectedNodes = [...new Set(selectedNodes)];
        //
        //     if (selectedNodes.length > 0) {
        //         return selectedNodes;
        //     }
        //
        //     return undefined;
        // }
    
        handleBoxSelect(selectedNodes: Node[]) {
            if (selectedNodes !== undefined && selectedNodes.length <= 0) {
    
            this.selectNodes(selectedNodes.concat(this.state.selectedNodes));
    
        /**
         * Selects multiple nodes, or clears selection if given undefined or empty array.
         * @param nodes Multiple nodes to mark as selected.
         */
        public selectNodes(nodes: Node[]) {
            this.setState({
                selectedNodes: nodes,
            });
    
        private handleNodeTypeSelect(type: NodeType) {
            const nodesWithType = this.state.graph.nodes.filter((n: Node) =>
                n.type.equals(type)
            );
            this.selectNodes(nodesWithType);
        }
    
    
        render(): React.ReactNode {
            return (
                <div id="ks-editor">
                    <h1>Interface</h1>
    
                    <SpaceSelect onLoadSpace={this.loadSpace} />
    
                    <div id="content">
    
                        {this.state.graph && (
                            <div
                                id="force-graph-renderer"
                                ref={this.graphContainer}
    
                                <SelectLayer
                                    allNodes={
                                        this.state.graph
                                            ? this.state.graph.nodes
                                            : []
                                    }
                                    screen2GraphCoords={
                                        this.rendererRef
                                            ? this.rendererRef.current
                                                  .screen2GraphCoords
                                            : undefined
                                    }
                                    isEnabled={this.state.keys["Shift"]}
                                    onBoxSelect={this.handleBoxSelect}
                                >
                                    <GraphRenderer2D
                                        ref={this.rendererRef}
                                        graph={this.state.graph}
    
    Maximilian Giller's avatar
    Maximilian Giller committed
                                        width={this.state.graphWidth}
    
                                        onNodeSelectionChanged={this.selectNodes}
                                        selectedNodes={this.state.selectedNodes}
    
    Maximilian Giller's avatar
    Maximilian Giller committed
                                    />
    
                        {this.state.graph && (
                            <div id="sidepanel">
    
                                {/*<HistoryNavigator*/}
                                {/*    spaceId="space"*/}
                                {/*    history={this.state.graph.history}*/}
                                {/*    onChange={this.onGraphDataChange}*/}
                                {/*/>*/}
    
                                <hr />
                                <NodeDetails
                                    selectedNodes={this.state.selectedNodes}
                                    allTypes={
                                        this.state.graph
                                            ? this.state.graph.objectGroups
                                            : []
    
                                    onChange={this.forceUpdate}
                                />
                                <hr />
                                <h3>Node types</h3>
                                <NodeTypesEditor
                                    onChange={this.forceUpdate}
                                    graph={this.state.graph}
                                    onSelectAll={this.handleNodeTypeSelect}
                                />
                                <hr />
    
                                <Settings
                                    labelVisibility={this.state.visibleLabels}
                                    onLabelVisibilityChange={(visible) =>
                                        this.setState({ visibleLabels: visible })
                                    }
                                    connectOnDrag={this.state.connectOnDrag}
                                    onConnectOnDragChange={(connectOnDrag) =>
    
                                        this.setState({
    
                                <Instructions
                                    connectOnDragEnabled={this.state.connectOnDrag}