feat(handles): validate connection
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user