import Tool from "./tool"; 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"; const SELECT_BOX_SELECTOR = BOX_SELECT_LAYER_ID + " #" + BOX_SELECT_ID_WH; /** * Only one instance of this should exist, since box-delete has to work on a global scale. */ var deleteToolInstance = undefined; // Used for box delete export default class DeleteTool extends Tool { constructor(key) { super("Delete", DeleteIcon, key, new ToolMenu()); this.setupBoxSelect(); this.isActive = false; if (deleteToolInstance === undefined) { deleteToolInstance = this; } } onBoxSelect(left, bottom, top, right) { // Filter out selected nodes const selectedNodes = []; 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 && br.y > node.y && node.y > tl.y ) { selectedNodes.push(node); } }); // Was anything even selected? if (selectedNodes.length <= 0) { return; } // Ask for confirmation to delete var nodeNames = selectedNodes.map((n) => n[Graph.NODE_LABEL]); //! Problem: If browser is not actually showing the alerts, it always returns false! var shouldDelete = confirm( "Are you sure you want to delete all these nodes?\n\n" + nodeNames.join("\n") ); // Delete if confirmed if (shouldDelete) { var nodeIds = selectedNodes.map((n) => n[Graph.NODE_ID]); Editor.globalGraph.deleteNodes(nodeIds); } } onNodeClick(node) { Editor.globalGraph.deleteNode(node[Graph.NODE_ID]); if (Editor.globalState.selectedItem == node) { Editor.globalState.setSelectedItem(undefined); } } onLinkClick(link) { Editor.globalGraph.deleteLink( link[Graph.LINK_SOURCE][Graph.NODE_ID], link[Graph.LINK_TARGET][Graph.NODE_ID] ); if (Editor.globalState.selectedItem == link) { Editor.globalState.setSelectedItem(undefined); } } setupBoxSelect() { window.addEventListener("load", () => { // Source: https://github.com/vasturiano/force-graph/issues/151#issuecomment-735850938 // forceGraph element is the element provided to the Force Graph Library jquery("#2d-graph").on("pointerdown", this.boxSelectOnPointerDown); jquery("#2d-graph").on("pointermove", this.boxSelectOnPointerMove); jquery("#2d-graph").on("pointerup", this.boxSelectOnPointerUp); }); } boxSelectOnPointerDown(e) { // Only do anything if delete tool is also active if ( deleteToolInstance === undefined || deleteToolInstance.isActive == false ) { return; } if (!e.shiftKey) { return; } e.preventDefault(); this.boxSelect = document.createElement("div"); this.boxSelect.id = BOX_SELECT_ID_WH; this.boxSelect.style.left = e.offsetX.toString() + "px"; this.boxSelect.style.top = e.offsetY.toString() + "px"; this.boxSelectStart = { x: e.offsetX, y: e.offsetY, }; // app element is the element just above the forceGraph element. jquery(BOX_SELECT_LAYER_ID).append(this.boxSelect); } boxSelectOnPointerMove(e) { if (!this.boxSelect) { return; } if (!e.shiftKey) { jquery(SELECT_BOX_SELECTOR).remove(); return; } e.preventDefault(); if (e.offsetX < this.boxSelectStart.x) { this.boxSelect.style.left = e.offsetX.toString() + "px"; this.boxSelect.style.width = (this.boxSelectStart.x - e.offsetX).toString() + "px"; } else { this.boxSelect.style.left = this.boxSelectStart.x.toString() + "px"; this.boxSelect.style.width = (e.offsetX - this.boxSelectStart.x).toString() + "px"; } if (e.offsetY < this.boxSelectStart.y) { this.boxSelect.style.top = e.offsetY.toString() + "px"; this.boxSelect.style.height = (this.boxSelectStart.y - e.offsetY).toString() + "px"; } else { this.boxSelect.style.top = this.boxSelectStart.y.toString() + "px"; this.boxSelect.style.height = (e.offsetY - this.boxSelectStart.y).toString() + "px"; } } boxSelectOnPointerUp(e) { if (!this.boxSelect) { return; } if (!e.shiftKey) { jquery(SELECT_BOX_SELECTOR).remove(); return; } e.preventDefault(); let left, bottom, top, right; if (e.offsetX < this.boxSelectStart.x) { left = e.offsetX; right = this.boxSelectStart.x; } else { left = this.boxSelectStart.x; right = e.offsetX; } if (e.offsetY < this.boxSelectStart.y) { top = e.offsetY; bottom = this.boxSelectStart.y; } else { top = this.boxSelectStart.y; bottom = e.offsetY; } this.boxSelect.remove(); deleteToolInstance.onBoxSelect(left, bottom, top, right); } }