fix: use explicit source+hidden-target handles for reliable bidirectional snapping

source handles: visible, can initiate drag from top or bottom
target handles: invisible (opacity:0, 12x12px), overlap the source at same
position — React Flow detects them for snapping and shows visual feedback
without the user seeing a second dot. Ensures bottom→top connections work.
This commit is contained in:
Pouzor
2026-03-08 13:10:25 +01:00
parent be18bcc6d9
commit d829a4821e
2 changed files with 8 additions and 8 deletions
@@ -38,8 +38,8 @@ export function BaseNode({ data, selected, icon: typeIcon }: BaseNodeProps) {
minWidth: 140,
}}
>
<Handle type="source" position={Position.Top} className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="target" position={Position.Top} className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="source" position={Position.Top} id="top" className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="target" position={Position.Top} id="top-t" style={{ opacity: 0, width: 12, height: 12 }} />
{/* Icon */}
<div
@@ -68,8 +68,8 @@ export function BaseNode({ data, selected, icon: typeIcon }: BaseNodeProps) {
title={data.status}
/>
<Handle type="source" position={Position.Bottom} className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="target" position={Position.Bottom} className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="source" position={Position.Bottom} id="bottom" className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="target" position={Position.Bottom} id="bottom-t" style={{ opacity: 0, width: 12, height: 12 }} />
</div>
)
}
@@ -80,10 +80,10 @@ export function ProxmoxGroupNode(props: NodeProps<Node<NodeData>>) {
<div className="flex-1 relative" />
</div>
<Handle type="source" position={Position.Top} className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="target" position={Position.Top} className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="source" position={Position.Bottom} className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="target" position={Position.Bottom} className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="source" position={Position.Top} id="top" className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="target" position={Position.Top} id="top-t" style={{ opacity: 0, width: 12, height: 12 }} />
<Handle type="source" position={Position.Bottom} id="bottom" className="!bg-[#30363d] !border-[#8b949e]" />
<Handle type="target" position={Position.Bottom} id="bottom-t" style={{ opacity: 0, width: 12, height: 12 }} />
{/* Cluster handles — left/right for same-cluster links */}
<Handle type="source" position={Position.Left} id="cluster-left" title="Same cluster" style={{ background: '#ff6e00', borderColor: '#ff6e0088', width: 6, height: 6 }} />