Proximity Connect
This example shows how to automatically create edges when a node is dropped in close proximity to another one. While dragging, a dotted connection line is displayed to show which edge will be created if you drop the node.
<script lang="ts">
import { SvelteFlow, Background, type Node, type Edge } from '@xyflow/svelte';
import '@xyflow/svelte/dist/style.css';
import { initialNodes, initialEdges } from './nodes-and-edges';
let nodes = $state.raw<Node[]>(initialNodes);
let edges = $state.raw<Edge[]>(initialEdges);
const MIN_DISTANCE = 150;
function getClosestEdge(node: Node, nodes: Node[]) {
const closestNode = nodes.reduce(
(res, n) => {
if (n.id !== node.id) {
const dx = n.position.x - node.position.x;
const dy = n.position.y - node.position.y;
const d = Math.sqrt(dx * dx + dy * dy);
if (d < res.distance && d < MIN_DISTANCE) {
res.distance = d;
res.node = n;
}
}
return res;
},
{
distance: Number.MAX_VALUE,
node: null,
},
);
if (!closestNode.node) {
return null;
}
const closeNodeIsSource = closestNode.node.position.x < node.position.x;
return {
id: closeNodeIsSource
? `${node.id}-${closestNode.node.id}`
: `${closestNode.node.id}-${node.id}`,
source: closeNodeIsSource ? closestNode.node.id : node.id,
target: closeNodeIsSource ? node.id : closestNode.node.id,
class: 'temp',
};
}
function onNodeDrag({ targetNode: node }) {
const closestEdge = getClosestEdge(node, nodes);
let edgeAlreadyExists = false;
edges.forEach((edge, i) => {
if (edgeAlreadyExists) {
return;
}
if (closestEdge) {
// non-temporary edge already exists
if (
edge.source === closestEdge.source &&
edge.target === closestEdge.target
) {
edgeAlreadyExists = true;
return;
}
if (edge.class !== 'temp') {
return;
}
if (
edge.source !== closestEdge.source ||
edge.target !== closestEdge.target
) {
edges[i] = closestEdge; // replace the edge
edgeAlreadyExists = true;
}
} else if (edge.class === 'temp') {
edges.splice(i, 1); // remove edge
}
});
if (closestEdge && !edgeAlreadyExists) {
edges.push(closestEdge);
}
edges = edges;
}
function onNodeDragStop() {
edges = edges.map((edge) => {
if (edge.class === 'temp') {
return { ...edge, class: '' };
}
return edge;
});
}
</script>
<SvelteFlow
bind:nodes
bind:edges
fitView
onnodedrag={onNodeDrag}
onnodedragstop={onNodeDragStop}
>
<Background />
</SvelteFlow>