diff --git a/examples/advanced.html b/examples/advanced.html
index 2f5e161a..0f8a7662 100644
--- a/examples/advanced.html
+++ b/examples/advanced.html
@@ -19,6 +19,12 @@
left: 10px;
z-index: 4;
}
+ .react-flow__handle.connecting {
+ background: orange;
+ }
+ .react-flow__handle.valid {
+ background: green;
+ }
diff --git a/examples/advanced/scripts/InputNode.js b/examples/advanced/scripts/InputNode.js
index e3f76d58..bdf5622b 100644
--- a/examples/advanced/scripts/InputNode.js
+++ b/examples/advanced/scripts/InputNode.js
@@ -9,6 +9,11 @@ export default({ data, styles }) => (
{data.input}
data.onChange(e.target.value, data)} />
-
+ +connection.target % 2 === 0}
+ />
);
diff --git a/examples/advanced/scripts/SpecialNode.js b/examples/advanced/scripts/SpecialNode.js
index 52e17b4d..2a738eae 100644
--- a/examples/advanced/scripts/SpecialNode.js
+++ b/examples/advanced/scripts/SpecialNode.js
@@ -26,7 +26,11 @@ export default ({ data, styles }) => {
-
+
);
};
diff --git a/src/components/Handle/BaseHandle.js b/src/components/Handle/BaseHandle.js
index 27415be5..4eaf4494 100644
--- a/src/components/Handle/BaseHandle.js
+++ b/src/components/Handle/BaseHandle.js
@@ -1,8 +1,9 @@
import React, { memo } from 'react';
import cx from 'classnames';
-function onMouseDown(evt, { nodeId, setSourceId, setPosition, onConnect, isTarget }) {
+function onMouseDown(evt, { nodeId, setSourceId, setPosition, onConnect, isTarget, isValidConnection }) {
const containerBounds = document.querySelector('.react-flow').getBoundingClientRect();
+ let recentHoveredHandle = null;
setPosition({
x: evt.clientX - containerBounds.x,
@@ -10,24 +11,71 @@ function onMouseDown(evt, { nodeId, setSourceId, setPosition, onConnect, isTarg
});
setSourceId(nodeId);
+ // checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
+ function checkElementBelowIsValid(evt) {
+ const elementBelow = document.elementFromPoint(evt.clientX, evt.clientY);
+ const result = {
+ elementBelow,
+ isValid: false,
+ connection: null,
+ isHoveringHandle: false
+ };
+
+ if (elementBelow && (elementBelow.classList.contains('target') || elementBelow.classList.contains('source'))) {
+ let connection = null;
+
+ if (isTarget) {
+ const sourceId = elementBelow.getAttribute('data-nodeid');
+ connection = { source: sourceId, target: nodeId };
+ } else {
+ const targetId = elementBelow.getAttribute('data-nodeid');
+ connection = { source: nodeId, target: targetId };
+ }
+
+ const isValid = isValidConnection(connection);
+
+ result.connection = connection;
+ result.isValid = isValid;
+ result.isHoveringHandle = true;
+ }
+
+ return result;
+ }
+
function onMouseMove(evt) {
setPosition({
x: evt.clientX - containerBounds.x,
y: evt.clientY - containerBounds.y,
});
+
+ const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(evt);
+
+ if (!isHoveringHandle) {
+ if (recentHoveredHandle) {
+ recentHoveredHandle.classList.remove('valid');
+ recentHoveredHandle.classList.remove('connecting');
+ }
+ return false;
+ }
+
+ const isOwnHandle = connection.source === connection.target;
+
+ if (!isOwnHandle) {
+ recentHoveredHandle = elementBelow;
+ elementBelow.classList.add('connecting');
+ elementBelow.classList.toggle('valid', isValid);
+ }
}
function onMouseUp(evt) {
- const elementBelow = document.elementFromPoint(evt.clientX, evt.clientY);
+ const { connection, isValid } = checkElementBelowIsValid(evt);
- if (elementBelow && (elementBelow.classList.contains('target') || elementBelow.classList.contains('source'))) {
- if (isTarget) {
- const sourceId = elementBelow.getAttribute('data-nodeid');
- onConnect({ source: sourceId, target: nodeId });
- } else {
- const targetId = elementBelow.getAttribute('data-nodeid');
- onConnect({ source: nodeId, target: targetId });
- }
+ if (isValid) {
+ onConnect(connection);
+ }
+
+ if (recentHoveredHandle) {
+ recentHoveredHandle.classList.remove('valid');
}
setSourceId(null);
@@ -42,7 +90,7 @@ function onMouseDown(evt, { nodeId, setSourceId, setPosition, onConnect, isTarg
const BaseHandle = memo(({
type, nodeId, onConnect, position,
setSourceId, setPosition, className,
- id = false, ...rest
+ id = false, isValidConnection, ...rest
}) => {
const isTarget = type === 'target';
const handleClasses = cx(
@@ -59,7 +107,10 @@ const BaseHandle = memo(({
data-nodeid={nodeIdWithHandleId}
data-handlepos={position}
className={handleClasses}
- onMouseDown={evt => onMouseDown(evt, { nodeId: nodeIdWithHandleId, setSourceId, setPosition, onConnect, isTarget })}
+ onMouseDown={evt => onMouseDown(evt, {
+ nodeId: nodeIdWithHandleId, setSourceId, setPosition,
+ onConnect, isTarget, isValidConnection
+ })}
{...rest}
/>
);
diff --git a/src/components/Handle/index.js b/src/components/Handle/index.js
index 4579305a..791f19f0 100644
--- a/src/components/Handle/index.js
+++ b/src/components/Handle/index.js
@@ -33,13 +33,15 @@ Handle.displayName = 'Handle';
Handle.propTypes = {
type: PropTypes.oneOf(['source', 'target']),
position: PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
- onConnect: PropTypes.func
+ onConnect: PropTypes.func,
+ isValidConnection: PropTypes.func
};
Handle.defaultProps = {
type: 'source',
position: 'top',
- onConnect: () => {}
+ onConnect: () => {},
+ isValidConnection: () => true
};
export default Handle;
diff --git a/src/container/Editor/index.js b/src/container/ReactFlow/index.js
similarity index 95%
rename from src/container/Editor/index.js
rename to src/container/ReactFlow/index.js
index 5187cfd3..4aaa54a8 100644
--- a/src/container/Editor/index.js
+++ b/src/container/ReactFlow/index.js
@@ -21,7 +21,7 @@ import store from '../../store';
import '../../style.css';
-const Editor = ({
+const ReactFlow = ({
style, onElementClick, elements, children,
nodeTypes, edgeTypes, onLoad, onMove,
onElementsRemove, onConnect, onNodeDragStop, connectionLineType,
@@ -54,9 +54,9 @@ const Editor = ({
);
};
-Editor.displayName = 'Editor';
+ReactFlow.displayName = 'ReactFlow';
-Editor.defaultProps = {
+ReactFlow.defaultProps = {
onElementClick: () => {},
onElementsRemove: () => {},
onNodeDragStop: () => {},
@@ -79,4 +79,4 @@ Editor.defaultProps = {
selectionKeyCode: 16
};
-export default Editor;
+export default ReactFlow;
diff --git a/src/index.js b/src/index.js
index 2ec15a60..492e447e 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,4 @@
-import ReactFlow from './container/Editor';
+import ReactFlow from './container/ReactFlow';
export default ReactFlow;