Merge pull request #4594 from xyflow/fix-reconnect-loose
Fix reconnect with loose connectionMode
This commit is contained in:
@@ -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.
|
||||
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<EdgeType extends Edge = Edge> = {
|
||||
edge: EdgeType;
|
||||
isReconnectable: boolean | 'source' | 'target';
|
||||
reconnectRadius: EdgeWrapperProps['reconnectRadius'];
|
||||
sourceHandleId: Edge['sourceHandle'];
|
||||
targetHandleId: Edge['targetHandle'];
|
||||
onReconnect: EdgeWrapperProps<EdgeType>['onReconnect'];
|
||||
onReconnectStart: EdgeWrapperProps<EdgeType>['onReconnectStart'];
|
||||
onReconnectEnd: EdgeWrapperProps<EdgeType>['onReconnectEnd'];
|
||||
@@ -22,8 +20,6 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
|
||||
isReconnectable,
|
||||
reconnectRadius,
|
||||
edge,
|
||||
targetHandleId,
|
||||
sourceHandleId,
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
@@ -38,7 +34,10 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
|
||||
}: EdgeUpdateAnchorsProps<EdgeType>) {
|
||||
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
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
@@ -59,18 +58,14 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
|
||||
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<EdgeType extends Edge = Edge>({
|
||||
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<EdgeType extends Edge = Edge>({
|
||||
};
|
||||
|
||||
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 =>
|
||||
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') && (
|
||||
<EdgeAnchor
|
||||
position={sourcePosition}
|
||||
centerX={sourceX}
|
||||
@@ -121,7 +116,7 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
|
||||
type="source"
|
||||
/>
|
||||
)}
|
||||
{(isReconnectable === 'target' || isReconnectable === true) && (
|
||||
{(isReconnectable === true || isReconnectable === 'target') && (
|
||||
<EdgeAnchor
|
||||
position={targetPosition}
|
||||
centerX={targetX}
|
||||
|
||||
@@ -255,8 +255,6 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
targetPosition={targetPosition}
|
||||
setUpdateHover={setUpdateHover}
|
||||
setReconnecting={setReconnecting}
|
||||
sourceHandleId={edge.sourceHandle}
|
||||
targetHandleId={edge.targetHandle}
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user