Newer
Older
import { State } from "../state";
import * as Interactions from "../interactions";

Maximilian Giller
committed
import { Graph, GraphData } from "../structures/graph/graph";
import { loadGraphJson } from "../../../datasets";
import { ToolDetails } from "./tooldetails";
import { SpaceSelect } from "./spaceselect";
import ReactForceGraph2d from "react-force-graph-2d";
import { Node } from "../structures/graph/node";
import { HistoryNavigator } from "./historynavigator";
type propTypes = any;
type stateTypes = {
state: State;
graph: Graph;
renderer: any;
};
export class Editor extends React.PureComponent<propTypes, stateTypes> {
// TODO: Not a long term solution!
public static globalState: State;
public static globalGraph: Graph;
public static globalRenderer: any;
this.loadGraph = this.loadGraph.bind(this);
this.loadSpace = this.loadSpace.bind(this);
this.handleNodeClick = this.handleNodeClick.bind(this);

Maximilian Giller
committed
this.onHistoryChange = this.onHistoryChange.bind(this);
// Set as new state
this.state = {
state: undefined,
graph: undefined,
renderer: 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 ...");
// Create global objects
const newState = new State();
const newGraph = Graph.parse(data);
Editor.globalState = newState;
Editor.globalGraph = newGraph;
// Is valid and parsed successfully?
// TODO: Check doesn't work for some reason, always returns true, even tho it should be considered defined
// if (Editor.globalGraph == undefined) {
// Editor.globalState = this.state.state;
// Editor.globalGraph = this.state.graph;
// return false;
// }
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// console.log("Creating renderer ...");
// const renderTarget = document.getElementById("2d-graph");
// const renderWidth = renderTarget.offsetWidth;
// Editor.globalRenderer = ForceGraph()(renderTarget);
// // Subscribe to interactions
// console.log("Subscribing to events ...");
// Editor.globalRenderer
// .height(600)
// .width(renderWidth)
// .graphData(Editor.globalGraph.data)
// .nodeLabel("label")
// .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)
// )
// .autoPauseRedraw(false) // keep redrawing after engine has stopped
// .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));
// // Connect update event
// Editor.globalGraph.onChangeCallbacks.push((data) => {
// Editor.globalRenderer.graphData(data);
// });
// Set as new state
console.log(newGraph);
state: newState,
graph: newGraph,

Maximilian Giller
committed
this.state.graph.onChangeCallbacks.push(this.onHistoryChange);
// Subscribe to global key-press events
document.onkeydown = newState.onKeyDown;
document.onkeyup = newState.onKeyUp;
return true;
}

Maximilian Giller
committed
/**
* 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) {
node.delete();
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}

Maximilian Giller
committed
onChange={this.onHistoryChange}
<hr />
<ToolPool state={this.state.state} />
<hr />
<ToolDetails />
</div>
<ReactForceGraph2d
graphData={this.state.graph.data}
onNodeClick={this.handleNodeClick}
autoPauseRedraw={false}
/>