Skip to content
Snippets Groups Projects
Commit dc20514c authored by Maximilian Giller's avatar Maximilian Giller :squid:
Browse files

Moved editor.js completely over to editor.tsx

parent 08b7223b
No related branches found
No related tags found
1 merge request!2Implemented editor in the react framework
Pipeline #56406 passed
Showing
with 200 additions and 158 deletions
import { Editor } from "./editor/js/components/editor";
import "./editor/css/editor.css";
import "./editor/js/editor";
import ReactDOM from "react-dom";
import React from "react";
......
import React from "react";
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";
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);
Interactions.initInteractions();
}
/**
* 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
const state = new State();
const graph = Graph.parse(data);
// Is valid and parsed successfully?
if (graph == undefined) {
return false;
}
// Create renderer
const renderTarget = document.getElementById("2d-graph");
const renderWidth = renderTarget.offsetWidth;
const renderer = ForceGraph()(renderTarget);
// Subscribe to interactions
renderer
.height(600)
.width(renderWidth)
.graphData(graph.data)
.nodeLabel("label")
.linkColor((link) => state.linkColor(link))
.nodeColor((node) => state.nodeColor(node))
.onNodeClick((node) => state.onNodeClick(node))
.onNodeDragEnd((node, translate) =>
state.onNodeDragEnd(node, translate)
)
.autoPauseRedraw(false) // keep redrawing after engine has stopped
.linkWidth((link) => state.linkWidth(link))
.linkDirectionalParticles(state.linkDirectionalParticles())
.linkDirectionalParticleWidth((link) =>
state.linkDirectionalParticleWidth(link)
)
.onBackgroundClick((event) =>
state.onBackgroundClick(event, this.extractPositions(event))
)
.nodeCanvasObjectMode((node) => state.nodeCanvasObjectMode(node))
.nodeCanvasObject((node, ctx, globalScale) =>
state.nodeCanvasObject(node, ctx, globalScale)
)
.linkCanvasObjectMode((link) => state.linkCanvasObjectMode(link))
.linkCanvasObject((link, ctx, globalScale) =>
state.linkCanvasObject(link, ctx, globalScale)
)
.onLinkClick((link) => state.onLinkClick(link));
// Connect update event
graph.onChangeCallbacks.push((data) => {
renderer.graphData(data);
});
// Set as new state
this.setState({
state: state,
graph: graph,
renderer: renderer,
});
Editor.globalState = state;
Editor.globalGraph = graph;
Editor.globalRenderer = renderer;
// 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 },
};
}
export class Editor extends React.PureComponent {
render(): React.ReactNode {
// The id "ks-editor" indicates, that the javascript associated with this should automatically be executed
return (
......
import { State } from "./state";
import { loadGraphJson } from "../../datasets";
import ForceGraph from "force-graph";
import * as Interactions from "./interactions";
import { setSpace, SPACE } from "../../config";
import { Graph } from "./structures/graph/graph";
export let state: any = undefined;
export let graph: Graph = undefined;
export let renderer: any;
window.onload = function () {
// Only execute, if corresponding dom is present
if (document.getElementById("ks-editor") === null) {
return;
}
document.onkeydown = (e) => state.onKeyDown(e);
document.onkeyup = (e) => state.onKeyUp(e);
Interactions.initInteractions();
loadSpace(SPACE);
};
export function loadSpace(spaceId: string) {
if (state !== undefined && spaceId === SPACE) {
return;
}
setSpace(spaceId);
return loadGraphJson(SPACE).then((graphConfig) => {
state = new State();
graph = Graph.parse(graphConfig);
load();
// graph.restartSimulation();
});
}
function extractPositions(event: any) {
return {
graph: renderer.screen2GraphCoords(event.layerX, event.layerY),
window: { x: event.clientX, y: event.clientY },
};
}
function load() {
const graphContainer = document.getElementById("2d-graph");
const width = graphContainer.offsetWidth;
renderer = ForceGraph()(graphContainer)
.height(600)
.width(width)
.graphData(graph.data)
.nodeLabel("label")
.linkColor((link) => state.linkColor(link))
.nodeColor((node) => state.nodeColor(node))
.onNodeClick((node) => state.onNodeClick(node))
.onNodeDragEnd((node, translate) =>
state.onNodeDragEnd(node, translate)
)
.autoPauseRedraw(false) // keep redrawing after engine has stopped
.linkWidth((link) => state.linkWidth(link))
.linkDirectionalParticles(state.linkDirectionalParticles())
.linkDirectionalParticleWidth((link) =>
state.linkDirectionalParticleWidth(link)
)
.onBackgroundClick((event) =>
state.onBackgroundClick(event, extractPositions(event))
)
.nodeCanvasObjectMode((node) => state.nodeCanvasObjectMode(node))
.nodeCanvasObject((node, ctx, globalScale) =>
state.nodeCanvasObject(node, ctx, globalScale)
)
.linkCanvasObjectMode((link) => state.linkCanvasObjectMode(link))
.linkCanvasObject((link, ctx, globalScale) =>
state.linkCanvasObject(link, ctx, globalScale)
)
.onLinkClick((link) => state.onLinkClick(link));
graph.onChangeCallbacks.push((data) => {
renderer.graphData(data);
});
}
import jQuery from "jquery";
import { state } from "./editor";
import { listAllSpaces, saveGraphJson } from "../../datasets";
import { SPACE } from "../../config";
import { Editor } from "./components/editor";
/**
* Initiates all the handlers for button presses and input changes.
*/
export function initInteractions() {
jQuery("button#clear-collection").on("click", () => {
state.clearSelectedItems();
Editor.globalState.clearSelectedItems();
});
jQuery("button#import-space-btn").on("click", () =>
......
......@@ -7,10 +7,10 @@ import AddNodeTool from "./tools/addnodetool";
import ConnectTool from "./tools/connecttool";
import SettingsTool from "./tools/settingstool";
import SaveTool from "./tools/savetool";
import { graph } from "./editor";
import Toolbar from "./toolbar";
import * as Graph from "./graph";
import { DRAG_THRESHOLD_2D } from "../../config";
import { Editor } from "./components/editor";
export const TOOLS = {
undo: new UndoTool("undo"),
......@@ -264,8 +264,8 @@ export class State extends Tool {
);
// Have reversed colors
// Color at source node referencing the target node and vice versa
gradient.addColorStop("0", graph.getNodeColor(link.target));
gradient.addColorStop("1", graph.getNodeColor(link.source));
gradient.addColorStop("0", Editor.globalGraph.getNodeColor(link.target));
gradient.addColorStop("1", Editor.globalGraph.getNodeColor(link.source));
ctx.beginPath();
ctx.moveTo(link.source.x, link.source.y);
......@@ -323,7 +323,7 @@ export class State extends Tool {
isLinkHighlighted(link) {
return (
this.selectedItem === link ||
graph.isLinkOnNode(link, this.selectedItem)
Editor.globalGraph.isLinkOnNode(link, this.selectedItem)
);
}
......
import jQuery from "jquery";
import { state } from "./editor";
import { Editor } from "./components/editor";
const ID_TOOLBAR = "#toolbar";
const SAVE_BUTTON_ID = "div#ks-editor #toolbar-save";
......@@ -63,11 +63,11 @@ export default class Toolbar {
"",
tool,
(e) => {
var sameTool = state.tool === tool;
var sameTool = Editor.globalState.tool === tool;
if (sameTool && tool.toggleBehaviour) {
state.setTool(state.previousTool);
Editor.globalState.setTool(Editor.globalState.previousTool);
} else {
state.setTool(e.data);
Editor.globalState.setTool(e.data);
}
}
);
......
import Tool from "./tool";
import { graph, state } from "../editor";
import AddNodeIcon from "../../images/tools/addnode.png";
import { Editor } from "../components/editor";
export default class AddNodeTool extends Tool {
constructor(key) {
......@@ -14,13 +14,13 @@ export default class AddNodeTool extends Tool {
node.fx = positions.graph.x;
node.fy = positions.graph.y;
node = graph.addNode(node);
node = Editor.globalState.addNode(node);
if (node === undefined) {
console.error("Couldn't add new node");
return;
}
state.setSelectedItem(node);
Editor.globalState.setSelectedItem(node);
}
}
import Tool from "./tool";
import { state } from "../editor";
import { CONTEXT } from "../state";
import { CollectMenu, COLLECTION_KEY } from "./menus/collectmenu";
import CollectIcon from "../../images/tools/collect.png";
import { Editor } from "../components/editor";
export default class CollectTool extends Tool {
constructor(key) {
......@@ -10,38 +10,38 @@ export default class CollectTool extends Tool {
}
onNodeClick(node) {
if (state.itemsContext !== CONTEXT.node) {
state.clearSelectedItems();
state.itemsContext = CONTEXT.node;
if (Editor.globalState.itemsContext !== CONTEXT.node) {
Editor.globalState.clearSelectedItems();
Editor.globalState.itemsContext = CONTEXT.node;
}
if (state.selectedItems.has(node)) {
state.removeSelectedItem(node);
if (Editor.globalState.selectedItems.has(node)) {
Editor.globalState.removeSelectedItem(node);
} else {
state.addSelectedItem(node);
Editor.globalState.addSelectedItem(node);
}
this.menu.value(COLLECTION_KEY, state.selectedItems);
this.menu.value(COLLECTION_KEY, Editor.globalState.selectedItems);
}
onLinkClick(link) {
if (state.itemsContext !== CONTEXT.link) {
state.clearSelectedItems();
state.itemsContext = CONTEXT.link;
if (Editor.globalState.itemsContext !== CONTEXT.link) {
Editor.globalState.clearSelectedItems();
Editor.globalState.itemsContext = CONTEXT.link;
}
if (state.selectedItems.has(link)) {
state.removeSelectedItem(link);
if (Editor.globalState.selectedItems.has(link)) {
Editor.globalState.removeSelectedItem(link);
} else {
state.addSelectedItem(link);
Editor.globalState.addSelectedItem(link);
}
this.menu.value(COLLECTION_KEY, state.selectedItems);
this.menu.value(COLLECTION_KEY, Editor.globalState.selectedItems);
}
onMenuChange(key, value) {
if (key === COLLECTION_KEY && value === undefined) {
state.clearSelectedItems();
Editor.globalState.clearSelectedItems();
this.menu.value(COLLECTION_KEY, []);
}
}
......
import Tool from "./tool";
import { graph, state } from "../editor";
import * as Graph from "../graph";
import ConnectIcon from "../../images/tools/connect.png";
import { Editor } from "../components/editor";
const KEEP_SOURCE_KEY_ID = 17;
......@@ -14,18 +14,18 @@ export default class ConnectTool extends Tool {
onNodeClick(node) {
// Is a first node selected?
if (
state.selectedItem === undefined ||
state.selectedItem.node === false
Editor.globalState.selectedItem === undefined ||
Editor.globalState.selectedItem.node === false
) {
state.setSelectedItem(node);
Editor.globalState.setSelectedItem(node);
return;
}
// Add new link
var details = {};
var link = graph.addLink(
state.selectedItem[Graph.NODE_ID],
var link = Editor.globalGraph.addLink(
Editor.globalState.selectedItem[Graph.NODE_ID],
node[Graph.NODE_ID],
details
);
......@@ -38,12 +38,12 @@ export default class ConnectTool extends Tool {
if (this.keepSource === false) {
// Deselect the current first node
// TODO: Returned object not yet converted to normal one
state.setSelectedItem(link);
Editor.globalState.setSelectedItem(link);
}
}
onBackgroundClick(event, positions) {
state.setSelectedItem(undefined);
Editor.globalState.setSelectedItem(undefined);
}
onKeyDown(key) {
......
import Tool from "./tool";
import { graph, state, renderer } from "../editor";
import * as Graph from "../graph";
import jquery from "jquery";
import ToolMenu from "./menus/toolmenu";
import DeleteIcon from "../../images/tools/delete.png";
import { Editor } from "../components/editor";
const BOX_SELECT_LAYER_ID = "#box-select-layer";
const BOX_SELECT_ID_WH = "box-select";
......@@ -27,9 +27,9 @@ export default class DeleteTool extends Tool {
onBoxSelect(left, bottom, top, right) {
// Filter out selected nodes
const selectedNodes = [];
const tl = renderer.screen2GraphCoords(left, top);
const br = renderer.screen2GraphCoords(right, bottom);
graph.data[Graph.GRAPH_NODES].forEach((node) => {
const tl = Editor.globalRenderer.screen2GraphCoords(left, top);
const br = Editor.globalRenderer.screen2GraphCoords(right, bottom);
Editor.globalGraph.data[Graph.GRAPH_NODES].forEach((node) => {
if (
tl.x < node.x &&
node.x < br.x &&
......@@ -56,26 +56,26 @@ export default class DeleteTool extends Tool {
// Delete if confirmed
if (shouldDelete) {
var nodeIds = selectedNodes.map((n) => n[Graph.NODE_ID]);
graph.deleteNodes(nodeIds);
Editor.globalGraph.deleteNodes(nodeIds);
}
}
onNodeClick(node) {
graph.deleteNode(node[Graph.NODE_ID]);
Editor.globalGraph.deleteNode(node[Graph.NODE_ID]);
if (state.selectedItem == node) {
state.setSelectedItem(undefined);
if (Editor.globalState.selectedItem == node) {
Editor.globalState.setSelectedItem(undefined);
}
}
onLinkClick(link) {
graph.deleteLink(
Editor.globalGraph.deleteLink(
link[Graph.LINK_SOURCE][Graph.NODE_ID],
link[Graph.LINK_TARGET][Graph.NODE_ID]
);
if (state.selectedItem == link) {
state.setSelectedItem(undefined);
if (Editor.globalState.selectedItem == link) {
Editor.globalState.setSelectedItem(undefined);
}
}
......
import { SPACE } from "../../../../config";
import { loadSpace } from "../../editor";
import ToolMenu from "./toolmenu";
const LABEL_TOGGLE_ID = "#label-toggle";
......@@ -37,7 +36,8 @@ export class SettingsMenu extends ToolMenu {
return;
}
loadSpace(newSpace);
alert("Unable to load space at the moment! Editor object not implemented yet. See settingsmenu.js for further details.");
// loadSpace(newSpace);
this.hide();
});
}
......
import Tool from "./tool";
import { graph, state } from "../editor";
import RedoIcon from "../../images/tools/delete.png";
import { Editor } from "../components/editor";
export default class RedoTool extends Tool {
constructor(key) {
......@@ -8,7 +8,7 @@ export default class RedoTool extends Tool {
}
onToolActivate() {
graph.redo();
state.setTool(state.previousTool);
Editor.globalGraph.redo();
Editor.globalState.setTool(Editor.globalState.previousTool);
}
}
import Tool from "./tool";
import { saveGraphJson } from "../../../datasets";
import { state, graph } from "../editor";
import { SPACE } from "../../../config";
import SaveIcon from "../../images/tools/save.png";
import { Editor } from "../components/editor";
export default class SaveTool extends Tool {
constructor(key) {
......@@ -10,9 +10,9 @@ export default class SaveTool extends Tool {
}
onToolActivate() {
saveGraphJson(SPACE, graph.getCleanData());
graph.saveChanges();
state.setTool(state.previousTool);
saveGraphJson(SPACE, Editor.globalGraph.serialize());
Editor.globalGraph.saveChanges();
Editor.globalState.setTool(Editor.globalState.previousTool);
alert("Graph has been saved.");
}
}
import Tool from "./tool";
import { graph, state } from "../editor";
import * as SelectMenu from "./menus/selectmenu";
import SelectIcon from "../../images/tools/select.png";
import { Editor } from "../components/editor";
export default class SelectTool extends Tool {
constructor(key) {
......@@ -9,32 +9,32 @@ export default class SelectTool extends Tool {
}
onNodeClick(node) {
state.setSelectedItem(node);
Editor.globalState.setSelectedItem(node);
this.menu.value(SelectMenu.SELECTION_KEY, node);
}
onLinkClick(link) {
state.setSelectedItem(link);
Editor.globalState.setSelectedItem(link);
this.menu.value(SelectMenu.SELECTION_KEY, link);
}
onBackgroundClick(event, positions) {
state.setSelectedItem(undefined);
Editor.globalState.setSelectedItem(undefined);
this.menu.value(SelectMenu.SELECTION_KEY, {});
}
onMenuChange(key, value) {
if (key === SelectMenu.SELECTION_KEY) {
graph.changeDetails(value);
Editor.globalGraph.changeDetails(value);
}
}
onToolActivate() {
if (state === undefined) {
if (Editor.globalState === undefined) {
return;
}
var newSelection = state.selectedItem;
var newSelection = Editor.globalState.selectedItem;
if (newSelection === undefined) {
newSelection = {};
}
......
import { graph, state } from "../editor";
import {
SettingsMenu,
LABELS_KEY,
......@@ -7,6 +6,7 @@ import {
} from "./menus/settingsmenu";
import Tool from "./tool";
import SettingsIcon from "../../images/tools/settings.png";
import { Editor } from "../components/editor";
export default class SettingsTool extends Tool {
constructor(key) {
......@@ -15,11 +15,11 @@ export default class SettingsTool extends Tool {
onMenuChange(key, value) {
if (key === LABELS_KEY) {
state.setLabelVisibility(value);
Editor.globalState.setLabelVisibility(value);
} else if (key === RESIMULATE_KEY) {
graph.restartSimulation();
Editor.globalGraph.restartSimulation();
} else if (key === PHYSICS_DELAY_KEY) {
graph.physicsDelay = Number(value) * 1000; // Convert seconds to ms
Editor.globalGraph.physicsDelay = Number(value) * 1000; // Convert seconds to ms
}
}
}
import Tool from "./tool";
import { graph, state } from "../editor";
import UndoIcon from "../../images/tools/undo.png";
import { Editor } from "../components/editor";
export default class UndoTool extends Tool {
constructor(key) {
......@@ -8,7 +8,7 @@ export default class UndoTool extends Tool {
}
onToolActivate() {
graph.undo();
state.setTool(state.previousTool);
Editor.globalGraph.undo();
Editor.globalState.setTool(Editor.globalState.previousTool);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment