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 {
// Create global objects
// TODO: is not loading properly
Editor.globalState = new State();
Editor.globalGraph = Graph.parse(data);
// Is valid and parsed successfully?
if (Editor.globalGraph == undefined) {
Editor.globalState = this.state.state;
Editor.globalGraph = this.state.graph;
return false;
}
// Create renderer
const renderTarget = document.getElementById("2d-graph");
const renderWidth = renderTarget.offsetWidth;
Editor.globalRenderer = ForceGraph()(renderTarget);
// Subscribe to interactions
.height(600)
.width(renderWidth)
.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
this.setState({
state: Editor.globalState,
graph: Editor.globalGraph,
renderer: Editor.globalRenderer,
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
});
// Subscribe to global key-press events
document.onkeydown = this.state.state.onKeyDown;
document.onkeyup = this.state.state.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>
<GraphRenderer />