Newer
Older
import PropTypes, { InferType } from "prop-types";
import { State } from "../state";
import * as Interactions from "../interactions";
import { Graph } from "../structures/graph/graph";
import ForceGraph from "force-graph";
import { loadGraphJson } from "../../../datasets";
import { ToolDetails } from "./tooldetails";
import { GraphRenderer } from "./graphrenderer";
import { SpaceSelect } from "./spaceselect";
export class Editor extends React.PureComponent<
InferType<typeof Editor.propTypes>,
InferType<typeof Editor.stateTypes>
> {
static propTypes = {};
static stateTypes = {
state: State,
graph: Graph,
renderer: PropTypes.any,
};
// TODO: Not a long term solution!
public static globalState: State;
public static globalGraph: Graph;
public static globalRenderer: any;
constructor(props: InferType<typeof Editor.propTypes>) {
super(props);
// 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 ...");
console.log(data);
console.log(Graph.parse(data));
// Create global objects
console.log("Parsing ...");
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;
// }
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
130
// 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
state: newState,
graph: newGraph,
});
// Subscribe to global key-press events
document.onkeydown = newState.onKeyDown;
document.onkeyup = newState.onKeyUp;
return true;
}
/**
* 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 },
};
}
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>
<div id="content">
<div id="sidepanel">
<SpaceSelect />
<hr />
<ToolPool state={this.state.state} />
<hr />
<ToolDetails />
</div>
{this.state.graph ? (
<GraphRenderer graphData={this.state.graph.data} />
) : undefined}