From 1d619c838665f253fba0fe6d2f6d9fe423a1a916 Mon Sep 17 00:00:00 2001 From: Felix Saaro Date: Thu, 18 Mar 2021 13:54:32 +0100 Subject: [PATCH 1/3] Add loopback edges --- src/components/graph-view.js | 10 ++++++---- src/examples/graph.js | 13 +++++-------- src/helpers/edge-helpers.js | 29 +++++++++++++++++++++++++++++ src/styles/main.scss | 1 + 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/components/graph-view.js b/src/components/graph-view.js index 3e75f492..47c33488 100644 --- a/src/components/graph-view.js +++ b/src/components/graph-view.js @@ -1009,12 +1009,15 @@ class GraphView extends React.Component { createNewEdge() { const { canCreateEdge, nodeKey, onCreateEdge } = this.props; - const { edgesMap, edgeEndNode, hoveredNodeData } = this.state; + const { edgesMap, hoveredNodeData } = this.state; if (!hoveredNodeData) { return; } + // If no edgeEndNode is defined it will add the edge to itself. + const edgeEndNode = this.state.edgeEndNode || hoveredNodeData; + this.removeCustomEdge(); if (edgeEndNode) { @@ -1023,7 +1026,6 @@ class GraphView extends React.Component { if ( edgesMap && - hoveredNodeData !== edgeEndNode && canCreateEdge && canCreateEdge(hoveredNodeData, edgeEndNode) && !edgesMap[mapId1] && @@ -1044,7 +1046,7 @@ class GraphView extends React.Component { handleNodeUpdate = (position: any, nodeId: string, shiftKey: boolean) => { const { onUpdateNode, readOnly } = this.props; - const { draggingEdge, hoveredNode, edgeEndNode } = this.state; + const { draggingEdge, hoveredNode } = this.state; if (readOnly) { return; @@ -1052,7 +1054,7 @@ class GraphView extends React.Component { // Detect if edge is being drawn and link to hovered node // This will handle a new edge - if (shiftKey && hoveredNode && edgeEndNode) { + if (shiftKey && hoveredNode) { this.createNewEdge(); } else { if (draggingEdge) { diff --git a/src/examples/graph.js b/src/examples/graph.js index 3945d280..9d6f780f 100644 --- a/src/examples/graph.js +++ b/src/examples/graph.js @@ -408,14 +408,11 @@ class Graph extends React.Component { type, }; - // Only add the edge when the source node is not the same as the target - if (viewEdge.source !== viewEdge.target) { - graph.edges = [...graph.edges, viewEdge]; - this.setState({ - graph, - selected: viewEdge, - }); - } + graph.edges = [...graph.edges, viewEdge]; + this.setState({ + graph, + selected: viewEdge, + }); }; // Called when an edge is reattached to a different target. diff --git a/src/helpers/edge-helpers.js b/src/helpers/edge-helpers.js index 3deb5cec..2132f4c8 100644 --- a/src/helpers/edge-helpers.js +++ b/src/helpers/edge-helpers.js @@ -222,6 +222,35 @@ export function getPathDescription( viewWrapperElem ); + if (sourceNode === targetNode) { + const arrowOffset = nodeSize * 0.8; + + const returningLinePoints = [ + { + x: srcX - srcOff.xOff + nodeSize / 2, + y: srcY - srcOff.yOff, + }, + { + x: srcX - srcOff.xOff + arrowOffset, + y: srcY - srcOff.yOff, + }, + { + x: srcX - srcOff.xOff + arrowOffset, + y: srcY - srcOff.yOff - arrowOffset, + }, + { + x: srcX - srcOff.xOff, + y: srcY - srcOff.yOff - arrowOffset, + }, + { + x: srcX - trgOff.xOff, + y: srcY - trgOff.yOff - nodeSize / 2, + }, + ]; + + return getLine(returningLinePoints); + } + const linePoints = [ { x: srcX - srcOff.xOff, diff --git a/src/styles/main.scss b/src/styles/main.scss index d85b562a..77371494 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -76,6 +76,7 @@ $button-size: 31px; } .edge { + fill: transparent; color: $light-color; stroke: $primary-color; stroke-width: 2px; From 6517d15152f1b28966860a095db932816b16242c Mon Sep 17 00:00:00 2001 From: Felix Saaro Date: Thu, 18 Mar 2021 21:39:53 +0100 Subject: [PATCH 2/3] Add allowLoopbackEdge configuration --- src/components/graph-view.js | 17 ++++++++++------- src/examples/graph.js | 1 + 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/graph-view.js b/src/components/graph-view.js index 47c33488..6f79f452 100644 --- a/src/components/graph-view.js +++ b/src/components/graph-view.js @@ -89,6 +89,7 @@ class GraphView extends React.Component { zoomDur: 750, rotateEdgeHandle: true, centerNodeOnMove: true, + allowLoopbackEdge: false, }; static getDerivedStateFromProps( @@ -1009,15 +1010,12 @@ class GraphView extends React.Component { createNewEdge() { const { canCreateEdge, nodeKey, onCreateEdge } = this.props; - const { edgesMap, hoveredNodeData } = this.state; + const { edgesMap, edgeEndNode, hoveredNodeData } = this.state; if (!hoveredNodeData) { return; } - // If no edgeEndNode is defined it will add the edge to itself. - const edgeEndNode = this.state.edgeEndNode || hoveredNodeData; - this.removeCustomEdge(); if (edgeEndNode) { @@ -1046,7 +1044,7 @@ class GraphView extends React.Component { handleNodeUpdate = (position: any, nodeId: string, shiftKey: boolean) => { const { onUpdateNode, readOnly } = this.props; - const { draggingEdge, hoveredNode } = this.state; + const { draggingEdge, hoveredNode, edgeEndNode } = this.state; if (readOnly) { return; @@ -1054,7 +1052,7 @@ class GraphView extends React.Component { // Detect if edge is being drawn and link to hovered node // This will handle a new edge - if (shiftKey && hoveredNode) { + if (shiftKey && hoveredNode && edgeEndNode) { this.createNewEdge(); } else { if (draggingEdge) { @@ -1091,9 +1089,14 @@ class GraphView extends React.Component { handleNodeMouseEnter = (event: any, data: any) => { const { draggingEdge, hoveredNodeData } = this.state; + const { allowLoopbackEdge } = this.props; // hovered is false when creating edges - if (hoveredNodeData && data !== hoveredNodeData && draggingEdge) { + if ( + hoveredNodeData && + (data !== hoveredNodeData || allowLoopbackEdge) && + draggingEdge + ) { this.setState({ edgeEndNode: data, }); diff --git a/src/examples/graph.js b/src/examples/graph.js index 9d6f780f..44e7d482 100644 --- a/src/examples/graph.js +++ b/src/examples/graph.js @@ -661,6 +661,7 @@ class Graph extends React.Component { onCopySelected={this.onCopySelected} onPasteSelected={this.onPasteSelected} layoutEngineType={this.state.layoutEngineType} + allowLoopbackEdge={true} /> From b53ec7882e53c48b629ef301b2db280bb3409a16 Mon Sep 17 00:00:00 2001 From: Felix Saaro Date: Thu, 18 Mar 2021 21:46:41 +0100 Subject: [PATCH 3/3] Extend props table --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 008863bf..c2ab368a 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,7 @@ All props are detailed below. | `nodes` | `Array` | `true` | Array of graph nodes. | | `edges` | `Array` | `true` | Array of graph edges. | | `allowMultiselect` | `boolean` | `false` | Use Ctrl-Shift-LeftMouse to draw a multiple selection box | +| `allowLoopbackEdge` | `boolean` | `false` | When set to `true` edges that have the same start and end node are allowed to be set by the user.| | `selected` | `object` | `true` | The currently selected graph entity. | | `selectedNodes` | `Array` | `false` | If allowMultiselect is true, this should be the currently selected array of nodes. | | `selectedEdges` | `Array` | `true` | If allowMultiselect is true, this should be the currently selected array of edges. |