Skip to content
Snippets Groups Projects
editor.tsx 7.41 KiB
Newer Older
  • Learn to ignore specific revisions
  • import React from "react";
    
    import { State } from "../state";
    import * as Interactions from "../interactions";
    
    import { Graph } from "../structures/graph/graph";
    
    import { loadGraphJson } from "../../../datasets";
    
    import { NodeDetails } from "./nodedetails";
    
    import { SpaceSelect } from "./spaceselect";
    
    import "./editor.css";
    
    import ReactForceGraph2d from "react-force-graph-2d";
    
    import { Node } from "../structures/graph/node";
    
    import { HistoryNavigator } from "./historynavigator";
    
    type propTypes = any;
    type stateTypes = {
        graph: Graph;
    };
    
    export class Editor extends React.PureComponent<propTypes, stateTypes> {
    
        private defaultWarmupTicks = 100;
        private warmupTicks = 100;
    
    
        private keyStates: { [name: string]: boolean } = {};
        private selectedNode: Node;
    
    
        constructor(props: propTypes) {
    
            this.loadGraph = this.loadGraph.bind(this);
    
            this.loadSpace = this.loadSpace.bind(this);
    
            this.handleNodeClick = this.handleNodeClick.bind(this);
    
            this.onHistoryChange = this.onHistoryChange.bind(this);
    
            this.handleEngineStop = this.handleEngineStop.bind(this);
    
            this.handleKeyDown = this.handleKeyDown.bind(this);
            this.handleKeyUp = this.handleKeyUp.bind(this);
    
            // Set as new state
            this.state = {
                graph: undefined,
            };
    
    
            Interactions.initInteractions();
    
    
            // Load initial space
            this.loadSpace("space");
    
        }
    
        /**
         * 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: any): boolean {
    
            console.log("Starting to load new graph ...");
    
            console.log(data);
    
            const newGraph = Graph.parse(data);
    
            this.warmupTicks = this.defaultWarmupTicks; // Should only run for each newly initialized graph, but then never again
    
    
            // Is valid and parsed successfully?
    
            if (newGraph === undefined) {
                return false;
            }
    
    
            //     .linkColor((link: any) => Editor.globalState.linkColor(link))
            //     .nodeColor((node: any) => Editor.globalState.nodeColor(node))
            //     .onNodeClick((node: any) => Editor.globalState.onNodeClick(node))
            //     .onNodeDragEnd((node: any, translate: any) =>
            //         Editor.globalState.onNodeDragEnd(node, translate)
            //     )
            //     .linkWidth((link: any) => Editor.globalState.linkWidth(link))
            //     .linkDirectionalParticles(
            //         Editor.globalState.linkDirectionalParticles()
            //     )
            //     .linkDirectionalParticleWidth((link: any) =>
            //         Editor.globalState.linkDirectionalParticleWidth(link)
            //     )
            //     .onBackgroundClick((event: any) =>
            //         Editor.globalState.onBackgroundClick(
            //             event,
            //             this.extractPositions(event)
            //         )
            //     )
            //     .nodeCanvasObjectMode((node: any) =>
            //         Editor.globalState.nodeCanvasObjectMode(node)
            //     )
            //     .nodeCanvasObject((node: any, ctx: any, globalScale: any) =>
            //         Editor.globalState.nodeCanvasObject(node, ctx, globalScale)
            //     )
            //     .linkCanvasObjectMode((link: any) =>
            //         Editor.globalState.linkCanvasObjectMode(link)
            //     )
            //     .linkCanvasObject((link: any, ctx: any, globalScale: any) =>
            //         Editor.globalState.linkCanvasObject(link, ctx, globalScale)
            //     )
            //     .onLinkClick((link: any) => Editor.globalState.onLinkClick(link));
    
    
                graph: newGraph,
    
            this.state.graph.onChangeCallbacks.push(this.onHistoryChange);
    
    
            // Subscribe to global key-press events
    
            document.onkeydown = this.handleKeyDown;
            document.onkeyup = this.handleKeyUp;
    
        private handleKeyDown(event: KeyboardEvent) {
            const key: string = event.key;
            this.keyStates[key] = true;
        }
    
        private handleKeyUp(event: KeyboardEvent) {
            const key: string = event.key;
            this.keyStates[key] = false;
        }
    
    
        /**
         * Propagates the changed state of the graph.
         */
        private onHistoryChange() {
            this.forceUpdate();
        }
    
    
        /**
         * Calculates the corresponding coordinates for a click event for easier further processing.
         * @param event The corresponding click event.
         * @returns Coordinates in graph and coordinates in browser window.
         */
        private extractPositions(event: any): {
    
            // graph: { x: number; y: number };
    
            window: { x: number; y: number };
        } {
            return {
    
                // graph: this.state.renderer.screen2GraphCoords(
                //     event.layerX,
                //     event.layerY
                // ),
    
                window: { x: event.clientX, y: event.clientY },
            };
        }
    
        private handleNodeClick(node: Node) {
    
            if (this.keyStates["Control"]) {
                node.delete();
            } else {
                this.selectedNode = node;
            }
    
            this.forceUpdate();
    
        private handleEngineStop() {
            // Only do something on first stop for each graph
            if (this.warmupTicks <= 0) {
                return;
            }
    
            this.warmupTicks = 0; // Only warm up once, so stop warming up after the first freeze
            this.state.graph.storeCurrentData("Initial state", false);
    
            this.forceUpdate();
        }
    
    
        render(): React.ReactNode {
            // The id "ks-editor" indicates, that the javascript associated with this should automatically be executed
            return (
                <div id="ks-editor">
                    <h1>Interface</h1>
    
                    <SpaceSelect onLoadSpace={this.loadSpace} />
    
                    <div id="content">
                        <div id="sidepanel">
    
                            <HistoryNavigator
                                spaceId="space"
                                historyObject={this.state.graph}
    
                            <NodeDetails
                                selectedNode={this.selectedNode}
                                allTypes={
                                    this.state.graph ? this.state.graph.types : []
                                }
                            />
    
                        {this.state.graph ? (
    
                            <ReactForceGraph2d
                                graphData={this.state.graph.data}
                                onNodeClick={this.handleNodeClick}
                                autoPauseRedraw={false}
    
                                cooldownTicks={0}
                                warmupTicks={this.warmupTicks}
                                onEngineStop={this.handleEngineStop}
    
                        ) : undefined}