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 { writable } from 'svelte/store';
import { SvelteFlow, Background, type Node, type Edge } from '@xyflow/svelte';
import '@xyflow/svelte/dist/style.css';
import { initialNodes, initialEdges } from './nodes-and-edges';
const nodes = writable<Node[]>(initialNodes);
const edges = writable<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; node: Node | null }>{
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({ detail: { 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.forEach((edge) => {
if (edge.class === 'temp') {
edge.class = '';
}
});
$edges = $edges;
}
</script>
<div style="height:100vh;">
<SvelteFlow {nodes} {edges} fitView on:nodedrag={onNodeDrag} on:nodedragstop={onNodeDragStop}>
<Background />
</SvelteFlow>
</div>
<style>
:global(.svelte-flow .svelte-flow__edge-path) {
stroke: #333;
stroke-width: 2;
}
:global(.svelte-flow .temp .svelte-flow__edge-path) {
stroke: #bbb;
stroke-dasharray: 5 5;
}
:global(.svelte-flow .svelte-flow__node) {
border-radius: 100%;
background-color: #fff;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
</style>