diff --git a/src/components/edge.js b/src/components/edge.js index aed78c15..a6cbbeff 100644 --- a/src/components/edge.js +++ b/src/components/edge.js @@ -425,7 +425,7 @@ class Edge extends React.Component { ) { let response = Edge.getDefaultIntersectResponse(); - if (trg[nodeKey] == null) { + if (trg == null || trg[nodeKey] == null) { return response; } diff --git a/src/components/graph-view.js b/src/components/graph-view.js index 56349afb..a377fbdd 100644 --- a/src/components/graph-view.js +++ b/src/components/graph-view.js @@ -188,10 +188,7 @@ class GraphView extends React.Component { const { initialBBox, zoomDelay, minZoom, maxZoom } = this.props; if (this.viewWrapper.current) { - this.viewWrapper.current.addEventListener( - 'keydown', - this.handleWrapperKeydown - ); + document.addEventListener('keydown', this.handleWrapperKeydown); this.viewWrapper.current.addEventListener( 'click', this.handleDocumentClick @@ -239,10 +236,7 @@ class GraphView extends React.Component { } componentWillUnmount() { - this.viewWrapper.current.removeEventListener( - 'keydown', - this.handleWrapperKeydown - ); + document.removeEventListener('keydown', this.handleWrapperKeydown); this.viewWrapper.current.removeEventListener( 'click', this.handleDocumentClick @@ -400,10 +394,15 @@ class GraphView extends React.Component { }); // remove node - // The timeout avoids a race condition - setTimeout(() => { - GraphUtils.removeElementFromDom(`node-${nodeId}-container`); - }); + const timeoutId = `nodes-${nodeId}`; + + // cancel an asyncRenderNode animation + cancelAnimationFrame(this.nodeTimeouts[timeoutId]); + + GraphUtils.removeElementFromDom( + `node-${nodeId}-container`, + this.viewWrapper.current + ); } } @@ -463,7 +462,10 @@ class GraphView extends React.Component { removeEdgeElement(source: string, target: string) { const id = `${source}-${target}`; - GraphUtils.removeElementFromDom(`edge-${id}-container`); + GraphUtils.removeElementFromDom( + `edge-${id}-container`, + this.viewWrapper.current + ); } canSwap(sourceNode: INode, hoveredNode: INode | null, swapEdge: any) { @@ -492,7 +494,10 @@ class GraphView extends React.Component { }); // remove from UI - GraphUtils.removeElementFromDom(`node-${nodeId}-container`); + GraphUtils.removeElementFromDom( + `node-${nodeId}-container`, + this.viewWrapper.current + ); // inform consumer this.props.onSelectNode(null); @@ -526,10 +531,12 @@ class GraphView extends React.Component { if (selectedEdge.source != null && selectedEdge.target != null) { // remove extra custom containers just in case. GraphUtils.removeElementFromDom( - `edge-${selectedEdge.source}-${selectedEdge.target}-custom-container` + `edge-${selectedEdge.source}-${selectedEdge.target}-custom-container`, + this.viewWrapper.current ); GraphUtils.removeElementFromDom( - `edge-${selectedEdge.source}-${selectedEdge.target}-container` + `edge-${selectedEdge.source}-${selectedEdge.target}-container`, + this.viewWrapper.current ); } @@ -605,7 +612,7 @@ class GraphView extends React.Component { } }; - handleEdgeSelected = e => { + handleEdgeSelected = (e: any) => { const { source, target } = e.target.dataset; let newState = { svgClicked: true, @@ -777,7 +784,10 @@ class GraphView extends React.Component { return; } - GraphUtils.removeElementFromDom('edge-custom-container'); + GraphUtils.removeElementFromDom( + 'edge-custom-container', + this.viewWrapper.current + ); if (edgeEndNode) { const mapId1 = `${hoveredNodeData[nodeKey]}_${edgeEndNode[nodeKey]}`; @@ -1085,7 +1095,10 @@ class GraphView extends React.Component { const draggedEdgeCopy = { ...this.state.draggedEdge }; // remove custom edge - GraphUtils.removeElementFromDom('edge-custom-container'); + GraphUtils.removeElementFromDom( + 'edge-custom-container', + this.viewWrapper.current + ); this.setState( { draggedEdge: null, @@ -1320,6 +1333,7 @@ class GraphView extends React.Component { } const containerId = `${id}-container`; + let nodeContainer: | HTMLElement | Element diff --git a/src/examples/multiple-graphs/index.js b/src/examples/multiple-graphs/index.js index a42db69f..b2541492 100644 --- a/src/examples/multiple-graphs/index.js +++ b/src/examples/multiple-graphs/index.js @@ -26,7 +26,12 @@ import sample1 from './graph1-sample'; import sample2 from './graph2-sample'; type IGraphProps = {}; -type IGraphState = {}; +type IGraphState = { + selectedNode: any, + selectedNode2: any, + sample1: any, + sample2: any, +}; class Graph extends React.Component { GraphViewRef; @@ -34,13 +39,41 @@ class Graph extends React.Component { constructor(props: IGraphProps) { super(props); this.GraphViewRef = React.createRef(); + this.state = { + selectedNode: null, + selectedNode2: null, + sample1: sample1, + sample2: sample2, + }; } + onDeleteNode = ( + viewNode: INode, + nodeId: string, + nodeArr: INode[], + configVar: any, + stateKey: string + ) => { + // Delete any connected edges + const newEdges = configVar.edges.filter((edge, i) => { + return ( + edge.source !== viewNode[NODE_KEY] && edge.target !== viewNode[NODE_KEY] + ); + }); + + configVar.nodes = nodeArr; + configVar.edges = newEdges; + this.setState({ + [stateKey]: configVar, + }); + }; + /* * Render */ render() { + const { sample1, sample2 } = this.state; const { nodes: graph1Nodes, edges: graph1Edges } = sample1; const { nodes: graph2Nodes, edges: graph2Edges } = sample2; const { NodeTypes, NodeSubtypes, EdgeTypes } = GraphConfig; @@ -61,15 +94,19 @@ class Graph extends React.Component { nodeKey={NODE_KEY} nodes={graph1Nodes} edges={graph1Edges} - selected={null} + selected={this.state.selectedNode} nodeTypes={NodeTypes} nodeSubtypes={NodeSubtypes} edgeTypes={EdgeTypes} readOnly={false} - onSelectNode={() => {}} + onSelectNode={node => { + this.setState({ selectedNode: node }); + }} onCreateNode={() => {}} onUpdateNode={() => {}} - onDeleteNode={() => {}} + onDeleteNode={(selected, nodeId, nodeArr) => { + this.onDeleteNode(selected, nodeId, nodeArr, sample1, 'sample1'); + }} onSelectEdge={() => {}} onCreateEdge={() => {}} onSwapEdge={() => {}} @@ -82,15 +119,19 @@ class Graph extends React.Component { nodeKey={NODE_KEY} nodes={graph2Nodes} edges={graph2Edges} - selected={null} + selected={this.state.selectedNode2} nodeTypes={NodeTypes} nodeSubtypes={NodeSubtypes} edgeTypes={EdgeTypes} readOnly={false} - onSelectNode={() => {}} + onSelectNode={node => { + this.setState({ selectedNode2: node }); + }} onCreateNode={() => {}} onUpdateNode={() => {}} - onDeleteNode={() => {}} + onDeleteNode={(selected, nodeId, nodeArr) => { + this.onDeleteNode(selected, nodeId, nodeArr, sample2, 'sample2'); + }} onSelectEdge={() => {}} onCreateEdge={() => {}} onSwapEdge={() => {}} diff --git a/src/index.js b/src/index.js index ee74f850..35ab23d6 100644 --- a/src/index.js +++ b/src/index.js @@ -25,6 +25,7 @@ export type IEdgeType = IEdge; export { default as GraphUtils } from './utilities/graph-util'; export { default as Node } from './components/node'; export type INodeType = INode; +// eslint-disable-next-line prettier/prettier export { default as BwdlTransformer } from './utilities/transformers/bwdl-transformer'; export { GV as GraphView }; export type LayoutEngineType = LayoutEngineConfigTypes; diff --git a/src/utilities/graph-util.js b/src/utilities/graph-util.js index 1d865f04..b6d43a5b 100644 --- a/src/utilities/graph-util.js +++ b/src/utilities/graph-util.js @@ -95,8 +95,8 @@ class GraphUtils { } } - static removeElementFromDom(id: string) { - const container = document.getElementById(id); + static removeElementFromDom(id: string, searchElement?: any = document) { + const container = searchElement.querySelector(`#${id}`); if (container && container.parentNode) { container.parentNode.removeChild(container);