From 30e83b7dd119483e7cf4aae51f8ecb304a8c8e6f Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 26 Aug 2024 14:31:39 +0200 Subject: [PATCH 1/2] fixed reconnect with connectionMode=loose --- .../EdgeWrapper/EdgeUpdateAnchors.tsx | 35 ++++++++----------- .../src/components/EdgeWrapper/index.tsx | 2 -- packages/system/src/xyhandle/XYHandle.ts | 4 +-- packages/system/src/xyhandle/utils.ts | 8 +++-- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/react/src/components/EdgeWrapper/EdgeUpdateAnchors.tsx b/packages/react/src/components/EdgeWrapper/EdgeUpdateAnchors.tsx index e10da1bc..4aed1c21 100644 --- a/packages/react/src/components/EdgeWrapper/EdgeUpdateAnchors.tsx +++ b/packages/react/src/components/EdgeWrapper/EdgeUpdateAnchors.tsx @@ -1,5 +1,5 @@ // 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 type { EdgeWrapperProps, Edge } from '../../types/edges'; @@ -9,8 +9,6 @@ type EdgeUpdateAnchorsProps = { edge: EdgeType; isReconnectable: boolean | 'source' | 'target'; reconnectRadius: EdgeWrapperProps['reconnectRadius']; - sourceHandleId: Edge['sourceHandle']; - targetHandleId: Edge['targetHandle']; onReconnect: EdgeWrapperProps['onReconnect']; onReconnectStart: EdgeWrapperProps['onReconnectStart']; onReconnectEnd: EdgeWrapperProps['onReconnectEnd']; @@ -22,8 +20,6 @@ export function EdgeUpdateAnchors({ isReconnectable, reconnectRadius, edge, - targetHandleId, - sourceHandleId, sourceX, sourceY, targetX, @@ -38,7 +34,10 @@ export function EdgeUpdateAnchors({ }: EdgeUpdateAnchorsProps) { const store = useStoreApi(); - const handleEdgeUpdater = (event: React.MouseEvent, isSourceHandle: boolean) => { + const handleEdgeUpdater = ( + event: React.MouseEvent, + oppositeHandle: { nodeId: string; id: string | null; type: HandleType } + ) => { // avoid triggering edge updater if mouse btn is not left if (event.button !== 0) { return; @@ -59,18 +58,14 @@ export function EdgeUpdateAnchors({ panBy, updateConnection, } = store.getState(); - const nodeId = isSourceHandle ? edge.target : edge.source; - const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null; - const handleType = isSourceHandle ? 'target' : 'source'; - - const isTarget = isSourceHandle; + const isTarget = oppositeHandle.type === 'target'; setReconnecting(true); - onReconnectStart?.(event, edge, handleType); + onReconnectStart?.(event, edge, oppositeHandle.type); const _onReconnectEnd = (evt: MouseEvent | TouchEvent, connectionState: FinalConnectionState) => { setReconnecting(false); - onReconnectEnd?.(evt, edge, handleType, connectionState); + onReconnectEnd?.(evt, edge, oppositeHandle.type, connectionState); }; const onConnectEdge = (connection: Connection) => onReconnect?.(edge, connection); @@ -80,11 +75,11 @@ export function EdgeUpdateAnchors({ connectionMode, connectionRadius, domNode, - handleId, - nodeId, + handleId: oppositeHandle.id, + nodeId: oppositeHandle.nodeId, nodeLookup, isTarget, - edgeUpdaterType: handleType, + edgeUpdaterType: oppositeHandle.type, lib, flowId, cancelConnection, @@ -101,15 +96,15 @@ export function EdgeUpdateAnchors({ }; const onReconnectSourceMouseDown = (event: React.MouseEvent): void => - handleEdgeUpdater(event, true); + handleEdgeUpdater(event, { nodeId: edge.target, id: edge.targetHandle ?? null, type: 'target' }); const onReconnectTargetMouseDown = (event: React.MouseEvent): void => - handleEdgeUpdater(event, false); + handleEdgeUpdater(event, { nodeId: edge.source, id: edge.sourceHandle ?? null, type: 'source' }); const onReconnectMouseEnter = () => setUpdateHover(true); const onReconnectMouseOut = () => setUpdateHover(false); return ( <> - {(isReconnectable === 'source' || isReconnectable === true) && ( + {(isReconnectable === true || isReconnectable === 'source') && ( ({ type="source" /> )} - {(isReconnectable === 'target' || isReconnectable === true) && ( + {(isReconnectable === true || isReconnectable === 'target') && ( ({ targetPosition={targetPosition} setUpdateHover={setUpdateHover} setReconnecting={setReconnecting} - sourceHandleId={edge.sourceHandle} - targetHandleId={edge.targetHandle} /> )} diff --git a/packages/system/src/xyhandle/XYHandle.ts b/packages/system/src/xyhandle/XYHandle.ts index cc4c5d1f..b15befc9 100644 --- a/packages/system/src/xyhandle/XYHandle.ts +++ b/packages/system/src/xyhandle/XYHandle.ts @@ -61,7 +61,7 @@ function onPointerDown( return; } - const fromHandleInternal = getHandle(nodeId, handleType, handleId, nodeLookup); + const fromHandleInternal = getHandle(nodeId, handleType, handleId, nodeLookup, connectionMode); if (!fromHandleInternal) { return; } @@ -289,7 +289,7 @@ function isValidHandle( result.isValid = isValid && isValidConnection(connection); - result.toHandle = getHandle(handleNodeId, handleType, handleId, nodeLookup, false); + result.toHandle = getHandle(handleNodeId, handleType, handleId, nodeLookup, connectionMode, false); } return result; diff --git a/packages/system/src/xyhandle/utils.ts b/packages/system/src/xyhandle/utils.ts index 2a0ff911..ecc9b1cc 100644 --- a/packages/system/src/xyhandle/utils.ts +++ b/packages/system/src/xyhandle/utils.ts @@ -1,5 +1,5 @@ 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[] { const nodes: InternalNodeBase[] = []; @@ -78,6 +78,7 @@ export function getHandle( handleType: HandleType, handleId: string | null, nodeLookup: NodeLookup, + connectionMode: ConnectionMode, withAbsolutePosition = false ): Handle | null { const node = nodeLookup.get(nodeId); @@ -85,7 +86,10 @@ export function getHandle( 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; return handle && withAbsolutePosition From 5138d90bdb91ff5d8dbeb8c8d29bdfd31c5b59d6 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 26 Aug 2024 14:33:26 +0200 Subject: [PATCH 2/2] added changeset --- .changeset/lovely-glasses-perform.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/lovely-glasses-perform.md diff --git a/.changeset/lovely-glasses-perform.md b/.changeset/lovely-glasses-perform.md new file mode 100644 index 00000000..8e1e4f4d --- /dev/null +++ b/.changeset/lovely-glasses-perform.md @@ -0,0 +1,6 @@ +--- +'@xyflow/react': patch +'@xyflow/system': patch +--- + +Fixed reconnecting edges with loose connectionMode