refactor(handle): first check element below then connection radius
This commit is contained in:
@@ -9,6 +9,7 @@ import CustomNode from '../examples/CustomNode';
|
|||||||
import DefaultNodes from '../examples/DefaultNodes';
|
import DefaultNodes from '../examples/DefaultNodes';
|
||||||
import DragHandle from '../examples/DragHandle';
|
import DragHandle from '../examples/DragHandle';
|
||||||
import DragNDrop from '../examples/DragNDrop';
|
import DragNDrop from '../examples/DragNDrop';
|
||||||
|
import EasyConnect from '../examples/EasyConnect';
|
||||||
import Edges from '../examples/Edges';
|
import Edges from '../examples/Edges';
|
||||||
import EdgeRenderer from '../examples/EdgeRenderer';
|
import EdgeRenderer from '../examples/EdgeRenderer';
|
||||||
import EdgeTypes from '../examples/EdgeTypes';
|
import EdgeTypes from '../examples/EdgeTypes';
|
||||||
@@ -96,6 +97,11 @@ const routes: IRoute[] = [
|
|||||||
path: '/dragndrop',
|
path: '/dragndrop',
|
||||||
component: DragNDrop,
|
component: DragNDrop,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'EasyConnect',
|
||||||
|
path: '/easy-connect',
|
||||||
|
component: EasyConnect,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Edges',
|
name: 'Edges',
|
||||||
path: '/edges',
|
path: '/edges',
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { ConnectionLineComponentProps, getStraightPath } from 'reactflow';
|
||||||
|
|
||||||
|
function CustomConnectionLine({ fromX, fromY, toX, toY, connectionLineStyle }: ConnectionLineComponentProps) {
|
||||||
|
const [edgePath] = getStraightPath({
|
||||||
|
sourceX: fromX,
|
||||||
|
sourceY: fromY,
|
||||||
|
targetX: toX,
|
||||||
|
targetY: toY,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g>
|
||||||
|
<path style={connectionLineStyle} fill="none" d={edgePath} />
|
||||||
|
<circle cx={toX} cy={toY} fill="black" r={3} stroke="black" strokeWidth={1.5} />
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomConnectionLine;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Handle, NodeProps, Position, ReactFlowState, useStore } from 'reactflow';
|
||||||
|
|
||||||
|
const connectionNodeIdSelector = (state: ReactFlowState) => state.connectionNodeId;
|
||||||
|
|
||||||
|
export default function CustomNode({ id }: NodeProps) {
|
||||||
|
const connectionNodeId = useStore(connectionNodeIdSelector);
|
||||||
|
const isTarget = connectionNodeId && connectionNodeId !== id;
|
||||||
|
|
||||||
|
const targetHandleStyle = { zIndex: isTarget ? 3 : 1 };
|
||||||
|
const label = isTarget ? 'Drop here' : 'Drag to connect';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="customNode">
|
||||||
|
<div
|
||||||
|
className="customNodeBody"
|
||||||
|
style={{
|
||||||
|
borderStyle: isTarget ? 'dashed' : 'solid',
|
||||||
|
backgroundColor: isTarget ? '#ffcce3' : '#ccd9f6',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Handle className="targetHandle" style={{ zIndex: 2 }} position={Position.Right} type="source" />
|
||||||
|
<Handle className="targetHandle" style={targetHandleStyle} position={Position.Left} type="target" />
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useStore, getStraightPath, EdgeProps } from 'reactflow';
|
||||||
|
|
||||||
|
import { getEdgeParams } from './utils.js';
|
||||||
|
|
||||||
|
function FloatingEdge({ id, source, target, markerEnd, style }: EdgeProps) {
|
||||||
|
const sourceNode = useStore(useCallback((store) => store.nodeInternals.get(source), [source]));
|
||||||
|
const targetNode = useStore(useCallback((store) => store.nodeInternals.get(target), [target]));
|
||||||
|
|
||||||
|
if (!sourceNode || !targetNode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { sx, sy, tx, ty } = getEdgeParams(sourceNode, targetNode);
|
||||||
|
|
||||||
|
const [edgePath] = getStraightPath({
|
||||||
|
sourceX: sx,
|
||||||
|
sourceY: sy,
|
||||||
|
targetX: tx,
|
||||||
|
targetY: ty,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <path id={id} className="react-flow__edge-path" d={edgePath} markerEnd={markerEnd} style={style} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FloatingEdge;
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import ReactFlow, { Node, Edge, addEdge, useNodesState, useEdgesState, MarkerType, OnConnect } from 'reactflow';
|
||||||
|
|
||||||
|
import CustomNode from './CustomNode';
|
||||||
|
import FloatingEdge from './FloatingEdge';
|
||||||
|
import CustomConnectionLine from './CustomConnectionLine';
|
||||||
|
|
||||||
|
import 'reactflow/dist/style.css';
|
||||||
|
import './style.css';
|
||||||
|
|
||||||
|
const initialNodes: Node[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 250, y: 320 },
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 40, y: 300 },
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
type: 'custom',
|
||||||
|
position: { x: 300, y: 0 },
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const initialEdges: Edge[] = [];
|
||||||
|
|
||||||
|
const connectionLineStyle = {
|
||||||
|
strokeWidth: 3,
|
||||||
|
stroke: 'black',
|
||||||
|
};
|
||||||
|
|
||||||
|
const nodeTypes = {
|
||||||
|
custom: CustomNode,
|
||||||
|
};
|
||||||
|
|
||||||
|
const edgeTypes = {
|
||||||
|
floating: FloatingEdge,
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultEdgeOptions = {
|
||||||
|
style: { strokeWidth: 3, stroke: 'black' },
|
||||||
|
type: 'floating',
|
||||||
|
markerEnd: {
|
||||||
|
type: MarkerType.ArrowClosed,
|
||||||
|
color: 'black',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const EasyConnectExample = () => {
|
||||||
|
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||||
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
|
|
||||||
|
const onConnect: OnConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), [setEdges]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactFlow
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
onNodesChange={onNodesChange}
|
||||||
|
onEdgesChange={onEdgesChange}
|
||||||
|
onConnect={onConnect}
|
||||||
|
fitView
|
||||||
|
nodeTypes={nodeTypes}
|
||||||
|
edgeTypes={edgeTypes}
|
||||||
|
defaultEdgeOptions={defaultEdgeOptions}
|
||||||
|
connectionLineComponent={CustomConnectionLine}
|
||||||
|
connectionLineStyle={connectionLineStyle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EasyConnectExample;
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
.customNodeBody {
|
||||||
|
width: 150px;
|
||||||
|
height: 80px;
|
||||||
|
border: 3px solid black;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customNode:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
left: 50%;
|
||||||
|
height: 20px;
|
||||||
|
width: 40px;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
background: #d6d5e6;
|
||||||
|
z-index: 1000;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 9px;
|
||||||
|
border: 2px solid #222138;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sourceHandle {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
transform: none;
|
||||||
|
border: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.targetHandle {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: blue;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
transform: none;
|
||||||
|
border: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import { Node, Position, MarkerType, XYPosition } from 'reactflow';
|
||||||
|
|
||||||
|
// this helper function returns the intersection point
|
||||||
|
// of the line between the center of the intersectionNode and the target node
|
||||||
|
function getNodeIntersection(intersectionNode: Node, targetNode: Node) {
|
||||||
|
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
||||||
|
const {
|
||||||
|
width: intersectionNodeWidth,
|
||||||
|
height: intersectionNodeHeight,
|
||||||
|
positionAbsolute: intersectionNodePosition,
|
||||||
|
} = intersectionNode;
|
||||||
|
const targetPosition = targetNode.positionAbsolute!;
|
||||||
|
|
||||||
|
const w = intersectionNodeWidth! / 2;
|
||||||
|
const h = intersectionNodeHeight! / 2;
|
||||||
|
|
||||||
|
const x2 = intersectionNodePosition!.x + w;
|
||||||
|
const y2 = intersectionNodePosition!.y + h;
|
||||||
|
const x1 = targetPosition.x + w;
|
||||||
|
const y1 = targetPosition.y + h;
|
||||||
|
|
||||||
|
const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h);
|
||||||
|
const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h);
|
||||||
|
const a = 1 / (Math.abs(xx1) + Math.abs(yy1));
|
||||||
|
const xx3 = a * xx1;
|
||||||
|
const yy3 = a * yy1;
|
||||||
|
const x = w * (xx3 + yy3) + x2;
|
||||||
|
const y = h * (-xx3 + yy3) + y2;
|
||||||
|
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the position (top,right,bottom or right) passed node compared to the intersection point
|
||||||
|
function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
|
||||||
|
const n = { ...node.positionAbsolute, ...node };
|
||||||
|
const nx = Math.round(n.x!);
|
||||||
|
const ny = Math.round(n.y!);
|
||||||
|
const px = Math.round(intersectionPoint.x);
|
||||||
|
const py = Math.round(intersectionPoint.y);
|
||||||
|
|
||||||
|
if (px <= nx + 1) {
|
||||||
|
return Position.Left;
|
||||||
|
}
|
||||||
|
if (px >= nx + n.width! - 1) {
|
||||||
|
return Position.Right;
|
||||||
|
}
|
||||||
|
if (py <= ny + 1) {
|
||||||
|
return Position.Top;
|
||||||
|
}
|
||||||
|
if (py >= n.y! + n.height! - 1) {
|
||||||
|
return Position.Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Position.Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
|
||||||
|
export function getEdgeParams(source: Node, target: Node) {
|
||||||
|
const sourceIntersectionPoint = getNodeIntersection(source, target);
|
||||||
|
const targetIntersectionPoint = getNodeIntersection(target, source);
|
||||||
|
|
||||||
|
const sourcePos = getEdgePosition(source, sourceIntersectionPoint);
|
||||||
|
const targetPos = getEdgePosition(target, targetIntersectionPoint);
|
||||||
|
|
||||||
|
return {
|
||||||
|
sx: sourceIntersectionPoint.x,
|
||||||
|
sy: sourceIntersectionPoint.y,
|
||||||
|
tx: targetIntersectionPoint.x,
|
||||||
|
ty: targetIntersectionPoint.y,
|
||||||
|
sourcePos,
|
||||||
|
targetPos,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNodesAndEdges() {
|
||||||
|
const nodes = [];
|
||||||
|
const edges = [];
|
||||||
|
const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
|
||||||
|
|
||||||
|
nodes.push({ id: 'target', data: { label: 'Target' }, position: center });
|
||||||
|
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
const degrees = i * (360 / 8);
|
||||||
|
const radians = degrees * (Math.PI / 180);
|
||||||
|
const x = 250 * Math.cos(radians) + center.x;
|
||||||
|
const y = 250 * Math.sin(radians) + center.y;
|
||||||
|
|
||||||
|
nodes.push({ id: `${i}`, data: { label: 'Source' }, position: { x, y } });
|
||||||
|
|
||||||
|
edges.push({
|
||||||
|
id: `edge-${i}`,
|
||||||
|
target: 'target',
|
||||||
|
source: `${i}`,
|
||||||
|
type: 'floating',
|
||||||
|
markerEnd: {
|
||||||
|
type: MarkerType.Arrow,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { nodes, edges };
|
||||||
|
}
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
import React, { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
addEdge,
|
addEdge,
|
||||||
Handle,
|
Handle,
|
||||||
Connection,
|
Connection,
|
||||||
Position,
|
Position,
|
||||||
Node,
|
Node,
|
||||||
Edge,
|
|
||||||
NodeProps,
|
NodeProps,
|
||||||
NodeTypes,
|
NodeTypes,
|
||||||
useNodesState,
|
useNodesState,
|
||||||
useEdgesState,
|
useEdgesState,
|
||||||
OnConnectStartParams,
|
OnConnectStart,
|
||||||
|
OnConnectEnd,
|
||||||
|
OnConnect,
|
||||||
} from 'reactflow';
|
} from 'reactflow';
|
||||||
|
|
||||||
import styles from './validation.module.css';
|
import styles from './validation.module.css';
|
||||||
@@ -49,24 +50,24 @@ const ValidationFlow = () => {
|
|||||||
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
|
|
||||||
const onConnectStart = useCallback(
|
const onConnectStart: OnConnectStart = useCallback(
|
||||||
(event: React.MouseEvent, params: OnConnectStartParams) => {
|
(event, params) => {
|
||||||
console.log('on connect start', params, event, value);
|
console.log('on connect start', params, event, value);
|
||||||
setValue(1);
|
setValue(1);
|
||||||
},
|
},
|
||||||
[value]
|
[value]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onConnect = useCallback(
|
const onConnect: OnConnect = useCallback(
|
||||||
(params: Connection | Edge) => {
|
(params) => {
|
||||||
console.log('on connect', params);
|
console.log('on connect', params);
|
||||||
setEdges((eds) => addEdge(params, eds));
|
setEdges((eds) => addEdge(params, eds));
|
||||||
},
|
},
|
||||||
[setEdges]
|
[setEdges]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onConnectEnd = useCallback(
|
const onConnectEnd: OnConnectEnd = useCallback(
|
||||||
(event: MouseEvent) => {
|
(event) => {
|
||||||
console.log('on connect end', event, value);
|
console.log('on connect end', event, value);
|
||||||
setValue(0);
|
setValue(0);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ export function handlePointerDown({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { connection, handleDomNode, isValid } = isValidHandle(
|
const { connection, handleDomNode, isValid } = isValidHandle(
|
||||||
|
event,
|
||||||
prevClosestHandle,
|
prevClosestHandle,
|
||||||
connectionMode,
|
connectionMode,
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -148,6 +149,7 @@ export function handlePointerDown({
|
|||||||
|
|
||||||
if (prevClosestHandle) {
|
if (prevClosestHandle) {
|
||||||
const { connection, isValid } = isValidHandle(
|
const { connection, isValid } = isValidHandle(
|
||||||
|
event,
|
||||||
prevClosestHandle,
|
prevClosestHandle,
|
||||||
connectionMode,
|
connectionMode,
|
||||||
nodeId,
|
nodeId,
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
|
|
||||||
const doc = getHostForElement(event.target as HTMLElement);
|
const doc = getHostForElement(event.target as HTMLElement);
|
||||||
const { connection, isValid } = isValidHandle(
|
const { connection, isValid } = isValidHandle(
|
||||||
|
event,
|
||||||
{
|
{
|
||||||
nodeId,
|
nodeId,
|
||||||
id: handleId,
|
id: handleId,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react';
|
||||||
|
|
||||||
import { ConnectionMode } from '../../types';
|
import { ConnectionMode } from '../../types';
|
||||||
import { internalsSymbol } from '../../utils';
|
import { getEventPosition, internalsSymbol } from '../../utils';
|
||||||
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '../../types';
|
import type { Connection, HandleType, XYPosition, Node, NodeHandleBounds } from '../../types';
|
||||||
|
|
||||||
export type ConnectionHandle = {
|
export type ConnectionHandle = {
|
||||||
@@ -61,6 +63,7 @@ type Result = {
|
|||||||
|
|
||||||
// checks if and returns connection in fom of an object { source: 123, target: 312 }
|
// checks if and returns connection in fom of an object { source: 123, target: 312 }
|
||||||
export function isValidHandle(
|
export function isValidHandle(
|
||||||
|
event: MouseEvent | TouchEvent | ReactMouseEvent | ReactTouchEvent,
|
||||||
handle: Pick<ConnectionHandle, 'nodeId' | 'id' | 'type'>,
|
handle: Pick<ConnectionHandle, 'nodeId' | 'id' | 'type'>,
|
||||||
connectionMode: ConnectionMode,
|
connectionMode: ConnectionMode,
|
||||||
fromNodeId: string,
|
fromNodeId: string,
|
||||||
@@ -73,23 +76,26 @@ export function isValidHandle(
|
|||||||
const handleDomNode = doc.querySelector(
|
const handleDomNode = doc.querySelector(
|
||||||
`.react-flow__handle[data-id="${handle?.nodeId}-${handle?.id}-${handle?.type}"]`
|
`.react-flow__handle[data-id="${handle?.nodeId}-${handle?.id}-${handle?.type}"]`
|
||||||
);
|
);
|
||||||
|
const { x, y } = getEventPosition(event);
|
||||||
|
const handleBelow = doc.elementFromPoint(x, y);
|
||||||
|
const handleToCheck = handleBelow?.classList.contains('react-flow__handle') ? handleBelow : handleDomNode;
|
||||||
|
|
||||||
const result: Result = {
|
const result: Result = {
|
||||||
handleDomNode,
|
handleDomNode: handleToCheck,
|
||||||
isValid: false,
|
isValid: false,
|
||||||
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
|
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
|
||||||
};
|
};
|
||||||
|
|
||||||
if (handleDomNode) {
|
if (handleToCheck) {
|
||||||
const handleIsTarget = handle.type === 'target';
|
const handleType = getHandleType(undefined, handleToCheck);
|
||||||
const handleIsSource = handle.type === 'source';
|
const handleNodeId = handleToCheck.getAttribute('data-nodeid');
|
||||||
const handleNodeId = handleDomNode.getAttribute('data-nodeid');
|
const handleId = handleToCheck.getAttribute('data-handleid');
|
||||||
const handleId = handleDomNode.getAttribute('data-handleid');
|
|
||||||
|
|
||||||
const connection: Connection = {
|
const connection: Connection = {
|
||||||
source: isTarget ? handle.nodeId : fromNodeId,
|
source: isTarget ? handle.nodeId : fromNodeId,
|
||||||
sourceHandle: isTarget ? handle.id : fromHandleId,
|
sourceHandle: isTarget ? handleId : fromHandleId,
|
||||||
target: isTarget ? fromNodeId : handle.nodeId,
|
target: isTarget ? fromNodeId : handle.nodeId,
|
||||||
targetHandle: isTarget ? fromHandleId : handle.id,
|
targetHandle: isTarget ? fromHandleId : handleId,
|
||||||
};
|
};
|
||||||
|
|
||||||
result.connection = connection;
|
result.connection = connection;
|
||||||
@@ -97,7 +103,7 @@ export function isValidHandle(
|
|||||||
// in strict mode we don't allow target to target or source to source connections
|
// in strict mode we don't allow target to target or source to source connections
|
||||||
const isValid =
|
const isValid =
|
||||||
connectionMode === ConnectionMode.Strict
|
connectionMode === ConnectionMode.Strict
|
||||||
? (isTarget && handleIsSource) || (!isTarget && handleIsTarget)
|
? (isTarget && handleType === 'source') || (!isTarget && handleType === 'target')
|
||||||
: handleNodeId !== fromNodeId || handleId !== fromHandleId;
|
: handleNodeId !== fromNodeId || handleId !== fromHandleId;
|
||||||
|
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
|
|||||||
Reference in New Issue
Block a user