Fix canvas connection interactions
This commit is contained in:
@@ -2267,6 +2267,13 @@ export function InfiniteCanvasWorkspace() {
|
||||
}, [downloadImageFromNode, state.nodes]);
|
||||
|
||||
const handleFlowNodeSelect = useCallback((nodeId: string, additive: boolean) => {
|
||||
if (connectingFromId && connectingFromId !== nodeId) {
|
||||
addConnection(connectingFromId, nodeId);
|
||||
setConnectingFromId(null);
|
||||
setSelectedNodeId(nodeId);
|
||||
setSelectedNodeIds([nodeId]);
|
||||
return;
|
||||
}
|
||||
setAddMenu(null);
|
||||
setConnectingFromId(null);
|
||||
setSelectedNodeId(nodeId);
|
||||
@@ -2274,7 +2281,7 @@ export function InfiniteCanvasWorkspace() {
|
||||
if (!additive) return current.includes(nodeId) ? current : [nodeId];
|
||||
return current.includes(nodeId) ? current.filter(id => id !== nodeId) : [...current, nodeId];
|
||||
});
|
||||
}, []);
|
||||
}, [addConnection, connectingFromId]);
|
||||
|
||||
const handleFlowNodesCommit = useCallback((positions: { id: string; x: number; y: number }[], options?: { history?: boolean; dirty?: boolean }) => {
|
||||
if (positions.length === 0) return;
|
||||
|
||||
@@ -37,11 +37,13 @@ type CanvasFlowNodeData = {
|
||||
connecting: boolean;
|
||||
connections: CanvasConnection[];
|
||||
allNodes: CanvasNode[];
|
||||
connectingFromId: string | null;
|
||||
imageModelOptions: CanvasModelOption[];
|
||||
textModelOptions: CanvasModelOption[];
|
||||
layerCanvasSize: number;
|
||||
layerColors: Record<CanvasLayer['type'], string>;
|
||||
onSelect: (id: string, additive: boolean) => void;
|
||||
onConnect: (sourceId: string, targetId: string) => void;
|
||||
onStartConnect: (id: string) => void;
|
||||
onUpdateNode: (id: string, patch: Partial<CanvasNode>) => void;
|
||||
onRunNode: (id: string) => void;
|
||||
@@ -251,7 +253,7 @@ function NodeInlineParams({
|
||||
}
|
||||
|
||||
function CanvasNodeCard({ data }: NodeProps<CanvasFlowNode>) {
|
||||
const { node, selected, connecting, connections, allNodes, imageModelOptions, textModelOptions, layerCanvasSize, layerColors, onSelect, onStartConnect, onUpdateNode, onRunNode, onRunDownstreamNode, onCreateVariationNode, onPreviewNode, onDownloadNode } = data;
|
||||
const { node, selected, connecting, connections, allNodes, connectingFromId, imageModelOptions, textModelOptions, layerCanvasSize, layerColors, onSelect, onConnect, onStartConnect, onUpdateNode, onRunNode, onRunDownstreamNode, onCreateVariationNode, onPreviewNode, onDownloadNode } = data;
|
||||
const incomingNodes = allNodes.filter(item => connections.some(connection => connection.targetNodeId === node.id && connection.sourceNodeId === item.id));
|
||||
const incomingImage = incomingNodes.find(item => !!getNodeImageUrl(item));
|
||||
const incomingText = incomingNodes.map(getNodeTextValue).filter(Boolean).join('\n');
|
||||
@@ -277,6 +279,11 @@ function CanvasNodeCard({ data }: NodeProps<CanvasFlowNode>) {
|
||||
onPointerDown={(event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
if (target.closest('button,input,textarea,[role="combobox"],.nodrag')) return;
|
||||
if (connectingFromId && connectingFromId !== node.id) {
|
||||
event.preventDefault();
|
||||
onConnect(connectingFromId, node.id);
|
||||
return;
|
||||
}
|
||||
onSelect(node.id, event.ctrlKey || event.metaKey || event.shiftKey);
|
||||
}}
|
||||
>
|
||||
@@ -697,6 +704,7 @@ function sameFlowNodes(a: CanvasFlowNode[], b: CanvasFlowNode[]) {
|
||||
&& node.data.connecting === next.data.connecting
|
||||
&& node.data.connections === next.data.connections
|
||||
&& node.data.allNodes === next.data.allNodes
|
||||
&& node.data.connectingFromId === next.data.connectingFromId
|
||||
&& node.data.imageModelOptions === next.data.imageModelOptions
|
||||
&& node.data.textModelOptions === next.data.textModelOptions
|
||||
&& node.data.onRunNode === next.data.onRunNode
|
||||
@@ -772,11 +780,13 @@ function FlowCanvasInner({
|
||||
connecting: connectingFromId === node.id,
|
||||
connections,
|
||||
allNodes: nodes,
|
||||
connectingFromId,
|
||||
imageModelOptions,
|
||||
textModelOptions,
|
||||
layerCanvasSize,
|
||||
layerColors,
|
||||
onSelect: onSelectNode,
|
||||
onConnect,
|
||||
onStartConnect,
|
||||
onUpdateNode,
|
||||
onRunNode,
|
||||
@@ -790,9 +800,10 @@ function FlowCanvasInner({
|
||||
width: node.width,
|
||||
height: node.height,
|
||||
},
|
||||
})), [connectingFromId, connections, editable, imageModelOptions, layerCanvasSize, layerColors, nodes, onCreateVariationNode, onDownloadNode, onPreviewNode, onRemoveConnection, onRunDownstreamNode, onRunNode, onSelectNode, onStartConnect, onUpdateNode, selectedNodeIds, textModelOptions]);
|
||||
})), [connectingFromId, connections, editable, imageModelOptions, layerCanvasSize, layerColors, nodes, onConnect, onCreateVariationNode, onDownloadNode, onPreviewNode, onRemoveConnection, onRunDownstreamNode, onRunNode, onSelectNode, onStartConnect, onUpdateNode, selectedNodeIds, textModelOptions]);
|
||||
|
||||
const [flowNodes, setFlowNodes] = useState<CanvasFlowNode[]>(incomingNodes);
|
||||
const [localEdges, setLocalEdges] = useState<Edge[]>([]);
|
||||
|
||||
const flowEdges = useMemo<Edge[]>(() => connections.map((connection) => ({
|
||||
id: connection.id,
|
||||
@@ -808,6 +819,14 @@ function FlowCanvasInner({
|
||||
},
|
||||
interactionWidth: 24,
|
||||
})), [connections, edgeStroke]);
|
||||
const visibleEdges = useMemo(() => {
|
||||
const realIds = new Set(flowEdges.map(edge => edge.id));
|
||||
return [...flowEdges, ...localEdges.filter(edge => !realIds.has(edge.id))];
|
||||
}, [flowEdges, localEdges]);
|
||||
|
||||
useEffect(() => {
|
||||
setLocalEdges([]);
|
||||
}, [connections]);
|
||||
|
||||
useEffect(() => {
|
||||
setFlowNodes(current => {
|
||||
@@ -881,8 +900,25 @@ function FlowCanvasInner({
|
||||
|
||||
const handleConnect = useCallback((connection: Connection) => {
|
||||
if (!connection.source || !connection.target) return;
|
||||
const edgeId = `pending-${connection.source}-${connection.target}`;
|
||||
setLocalEdges(current => current.some(edge => edge.source === connection.source && edge.target === connection.target)
|
||||
? current
|
||||
: [
|
||||
...current,
|
||||
{
|
||||
id: edgeId,
|
||||
source: connection.source || '',
|
||||
target: connection.target || '',
|
||||
sourceHandle: connection.sourceHandle || 'output',
|
||||
targetHandle: connection.targetHandle || 'input',
|
||||
type: 'smoothstep',
|
||||
markerEnd: { type: MarkerType.ArrowClosed, color: edgeStroke },
|
||||
style: { stroke: edgeStroke, strokeWidth: 3 },
|
||||
interactionWidth: 24,
|
||||
},
|
||||
]);
|
||||
onConnect(connection.source, connection.target);
|
||||
}, [onConnect]);
|
||||
}, [edgeStroke, onConnect]);
|
||||
|
||||
const handleMoveEnd = useCallback((_event: MouseEvent | TouchEvent | null, nextViewport: FlowViewport) => {
|
||||
if (!movingRef.current) return;
|
||||
@@ -944,7 +980,7 @@ function FlowCanvasInner({
|
||||
<ReactFlow
|
||||
className="canvas-flow"
|
||||
nodes={flowNodes}
|
||||
edges={flowEdges}
|
||||
edges={visibleEdges}
|
||||
nodeTypes={CANVAS_NODE_TYPES}
|
||||
minZoom={minZoom}
|
||||
maxZoom={maxZoom}
|
||||
|
||||
Reference in New Issue
Block a user