Merge pull request #4594 from xyflow/fix-reconnect-loose

Fix reconnect with loose connectionMode
This commit is contained in:
Moritz Klack
2024-08-26 15:06:09 +02:00
committed by GitHub
5 changed files with 29 additions and 26 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@xyflow/react': patch
'@xyflow/system': patch
---
Fixed reconnecting edges with loose connectionMode
@@ -1,5 +1,5 @@
// Reconnectable edges have a anchors around their handles to reconnect the edge. // Reconnectable edges have a anchors around their handles to reconnect the edge.
import { XYHandle, type Connection, EdgePosition, FinalConnectionState } from '@xyflow/system'; import { XYHandle, type Connection, EdgePosition, FinalConnectionState, HandleType } from '@xyflow/system';
import { EdgeAnchor } from '../Edges/EdgeAnchor'; import { EdgeAnchor } from '../Edges/EdgeAnchor';
import type { EdgeWrapperProps, Edge } from '../../types/edges'; import type { EdgeWrapperProps, Edge } from '../../types/edges';
@@ -9,8 +9,6 @@ type EdgeUpdateAnchorsProps<EdgeType extends Edge = Edge> = {
edge: EdgeType; edge: EdgeType;
isReconnectable: boolean | 'source' | 'target'; isReconnectable: boolean | 'source' | 'target';
reconnectRadius: EdgeWrapperProps['reconnectRadius']; reconnectRadius: EdgeWrapperProps['reconnectRadius'];
sourceHandleId: Edge['sourceHandle'];
targetHandleId: Edge['targetHandle'];
onReconnect: EdgeWrapperProps<EdgeType>['onReconnect']; onReconnect: EdgeWrapperProps<EdgeType>['onReconnect'];
onReconnectStart: EdgeWrapperProps<EdgeType>['onReconnectStart']; onReconnectStart: EdgeWrapperProps<EdgeType>['onReconnectStart'];
onReconnectEnd: EdgeWrapperProps<EdgeType>['onReconnectEnd']; onReconnectEnd: EdgeWrapperProps<EdgeType>['onReconnectEnd'];
@@ -22,8 +20,6 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
isReconnectable, isReconnectable,
reconnectRadius, reconnectRadius,
edge, edge,
targetHandleId,
sourceHandleId,
sourceX, sourceX,
sourceY, sourceY,
targetX, targetX,
@@ -38,7 +34,10 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
}: EdgeUpdateAnchorsProps<EdgeType>) { }: EdgeUpdateAnchorsProps<EdgeType>) {
const store = useStoreApi(); const store = useStoreApi();
const handleEdgeUpdater = (event: React.MouseEvent<SVGGElement, MouseEvent>, isSourceHandle: boolean) => { const handleEdgeUpdater = (
event: React.MouseEvent<SVGGElement, MouseEvent>,
oppositeHandle: { nodeId: string; id: string | null; type: HandleType }
) => {
// avoid triggering edge updater if mouse btn is not left // avoid triggering edge updater if mouse btn is not left
if (event.button !== 0) { if (event.button !== 0) {
return; return;
@@ -59,18 +58,14 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
panBy, panBy,
updateConnection, updateConnection,
} = store.getState(); } = store.getState();
const nodeId = isSourceHandle ? edge.target : edge.source; const isTarget = oppositeHandle.type === 'target';
const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null;
const handleType = isSourceHandle ? 'target' : 'source';
const isTarget = isSourceHandle;
setReconnecting(true); setReconnecting(true);
onReconnectStart?.(event, edge, handleType); onReconnectStart?.(event, edge, oppositeHandle.type);
const _onReconnectEnd = (evt: MouseEvent | TouchEvent, connectionState: FinalConnectionState) => { const _onReconnectEnd = (evt: MouseEvent | TouchEvent, connectionState: FinalConnectionState) => {
setReconnecting(false); setReconnecting(false);
onReconnectEnd?.(evt, edge, handleType, connectionState); onReconnectEnd?.(evt, edge, oppositeHandle.type, connectionState);
}; };
const onConnectEdge = (connection: Connection) => onReconnect?.(edge, connection); const onConnectEdge = (connection: Connection) => onReconnect?.(edge, connection);
@@ -80,11 +75,11 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
connectionMode, connectionMode,
connectionRadius, connectionRadius,
domNode, domNode,
handleId, handleId: oppositeHandle.id,
nodeId, nodeId: oppositeHandle.nodeId,
nodeLookup, nodeLookup,
isTarget, isTarget,
edgeUpdaterType: handleType, edgeUpdaterType: oppositeHandle.type,
lib, lib,
flowId, flowId,
cancelConnection, cancelConnection,
@@ -101,15 +96,15 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
}; };
const onReconnectSourceMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void => const onReconnectSourceMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
handleEdgeUpdater(event, true); handleEdgeUpdater(event, { nodeId: edge.target, id: edge.targetHandle ?? null, type: 'target' });
const onReconnectTargetMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void => const onReconnectTargetMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
handleEdgeUpdater(event, false); handleEdgeUpdater(event, { nodeId: edge.source, id: edge.sourceHandle ?? null, type: 'source' });
const onReconnectMouseEnter = () => setUpdateHover(true); const onReconnectMouseEnter = () => setUpdateHover(true);
const onReconnectMouseOut = () => setUpdateHover(false); const onReconnectMouseOut = () => setUpdateHover(false);
return ( return (
<> <>
{(isReconnectable === 'source' || isReconnectable === true) && ( {(isReconnectable === true || isReconnectable === 'source') && (
<EdgeAnchor <EdgeAnchor
position={sourcePosition} position={sourcePosition}
centerX={sourceX} centerX={sourceX}
@@ -121,7 +116,7 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
type="source" type="source"
/> />
)} )}
{(isReconnectable === 'target' || isReconnectable === true) && ( {(isReconnectable === true || isReconnectable === 'target') && (
<EdgeAnchor <EdgeAnchor
position={targetPosition} position={targetPosition}
centerX={targetX} centerX={targetX}
@@ -255,8 +255,6 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
targetPosition={targetPosition} targetPosition={targetPosition}
setUpdateHover={setUpdateHover} setUpdateHover={setUpdateHover}
setReconnecting={setReconnecting} setReconnecting={setReconnecting}
sourceHandleId={edge.sourceHandle}
targetHandleId={edge.targetHandle}
/> />
)} )}
</g> </g>
+2 -2
View File
@@ -61,7 +61,7 @@ function onPointerDown(
return; return;
} }
const fromHandleInternal = getHandle(nodeId, handleType, handleId, nodeLookup); const fromHandleInternal = getHandle(nodeId, handleType, handleId, nodeLookup, connectionMode);
if (!fromHandleInternal) { if (!fromHandleInternal) {
return; return;
} }
@@ -289,7 +289,7 @@ function isValidHandle(
result.isValid = isValid && isValidConnection(connection); result.isValid = isValid && isValidConnection(connection);
result.toHandle = getHandle(handleNodeId, handleType, handleId, nodeLookup, false); result.toHandle = getHandle(handleNodeId, handleType, handleId, nodeLookup, connectionMode, false);
} }
return result; return result;
+6 -2
View File
@@ -1,5 +1,5 @@
import { getHandlePosition, getOverlappingArea, nodeToRect } from '../utils'; import { getHandlePosition, getOverlappingArea, nodeToRect } from '../utils';
import type { HandleType, XYPosition, Handle, InternalNodeBase, NodeLookup } from '../types'; import type { HandleType, XYPosition, Handle, InternalNodeBase, NodeLookup, ConnectionMode } from '../types';
function getNodesWithinDistance(position: XYPosition, nodeLookup: NodeLookup, distance: number): InternalNodeBase[] { function getNodesWithinDistance(position: XYPosition, nodeLookup: NodeLookup, distance: number): InternalNodeBase[] {
const nodes: InternalNodeBase[] = []; const nodes: InternalNodeBase[] = [];
@@ -78,6 +78,7 @@ export function getHandle(
handleType: HandleType, handleType: HandleType,
handleId: string | null, handleId: string | null,
nodeLookup: NodeLookup, nodeLookup: NodeLookup,
connectionMode: ConnectionMode,
withAbsolutePosition = false withAbsolutePosition = false
): Handle | null { ): Handle | null {
const node = nodeLookup.get(nodeId); const node = nodeLookup.get(nodeId);
@@ -85,7 +86,10 @@ export function getHandle(
return null; return null;
} }
const handles = node.internals.handleBounds?.[handleType]; const handles =
connectionMode === 'strict'
? node.internals.handleBounds?.[handleType]
: [...(node.internals.handleBounds?.source ?? []), ...(node.internals.handleBounds?.target ?? [])];
const handle = (handleId ? handles?.find((h) => h.id === handleId) : handles?.[0]) ?? null; const handle = (handleId ? handles?.find((h) => h.id === handleId) : handles?.[0]) ?? null;
return handle && withAbsolutePosition return handle && withAbsolutePosition