Newer
Older
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 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> {

Maximilian Giller
committed
private defaultWarmupTicks = 100;
private warmupTicks = 100;
private keyStates: { [name: string]: boolean } = {};
private selectedNode: Node;
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);

Maximilian Giller
committed
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 ...");

Maximilian Giller
committed
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;
}
74
75
76
77
78
79
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
// .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));
console.log(newGraph);

Maximilian Giller
committed
this.state.graph.onChangeCallbacks.push(this.onHistoryChange);
// Subscribe to global key-press events
document.onkeydown = this.handleKeyDown;
document.onkeyup = this.handleKeyUp;
return true;
}
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;
}

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) {
if (this.keyStates["Control"]) {
node.delete();
} else {
this.selectedNode = node;
}

Maximilian Giller
committed
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}

Maximilian Giller
committed
onChange={this.onHistoryChange}
<NodeDetails
selectedNode={this.selectedNode}
allTypes={
this.state.graph ? this.state.graph.types : []
}
/>
<ReactForceGraph2d
graphData={this.state.graph.data}
onNodeClick={this.handleNodeClick}
autoPauseRedraw={false}

Maximilian Giller
committed
cooldownTicks={0}
warmupTicks={this.warmupTicks}
onEngineStop={this.handleEngineStop}