@@ -10,6 +10,7 @@ import {
1010 BackgroundVariant ,
1111 ConnectionMode ,
1212 type Node ,
13+ Position ,
1314 NodeChange ,
1415 Edge ,
1516 EdgeChange ,
@@ -29,6 +30,8 @@ import useYDocStore from "@/store/useYDocStore";
2930import { useCollaborativeCursors } from "@/hooks/useCursor" ;
3031import { CollaborativeCursors } from "../CursorView" ;
3132
33+ import { getHandlePosition } from "@/lib/getHandlePosition" ;
34+
3235const proOptions = { hideAttribution : true } ;
3336
3437interface CanvasProps {
@@ -176,6 +179,7 @@ function Flow({ className }: CanvasProps) {
176179 ( changes : NodeChange [ ] ) => {
177180 if ( ! ydoc ) return ;
178181 const nodesMap = ydoc . getMap ( "nodes" ) ;
182+ const edgesMap = ydoc . getMap ( "edges" ) ;
179183
180184 changes . forEach ( ( change ) => {
181185 if ( change . type === "position" && change . position ) {
@@ -187,13 +191,66 @@ function Flow({ className }: CanvasProps) {
187191 selected : false ,
188192 } ;
189193 nodesMap . set ( change . id , updatedNode ) ;
194+
195+ edges . forEach ( ( edge ) => {
196+ if ( edge . source === change . id || edge . target === change . id ) {
197+ const sourceNode = nodes . find ( ( n ) => n . id === edge . source ) ;
198+ const targetNode = nodes . find ( ( n ) => n . id === edge . target ) ;
199+
200+ if ( sourceNode && targetNode ) {
201+ const handlePositions = [
202+ Position . Left ,
203+ Position . Right ,
204+ Position . Top ,
205+ Position . Bottom ,
206+ ] ;
207+ let shortestDistance = Infinity ;
208+ let bestHandles = {
209+ source : edge . sourceHandle ,
210+ target : edge . targetHandle ,
211+ } ;
212+
213+ handlePositions . forEach ( ( sourceHandle ) => {
214+ handlePositions . forEach ( ( targetHandle ) => {
215+ const sourcePosition = getHandlePosition (
216+ sourceNode ,
217+ sourceHandle ,
218+ ) ;
219+ const targetPosition = getHandlePosition (
220+ targetNode ,
221+ targetHandle ,
222+ ) ;
223+ const distance = Math . sqrt (
224+ Math . pow ( sourcePosition . x - targetPosition . x , 2 ) +
225+ Math . pow ( sourcePosition . y - targetPosition . y , 2 ) ,
226+ ) ;
227+
228+ if ( distance < shortestDistance ) {
229+ shortestDistance = distance ;
230+ bestHandles = {
231+ source : sourceHandle ,
232+ target : targetHandle ,
233+ } ;
234+ }
235+ } ) ;
236+ } ) ;
237+
238+ const updatedEdge = {
239+ ...edge ,
240+ sourceHandle : bestHandles . source ,
241+ targetHandle : bestHandles . target ,
242+ } ;
243+ edgesMap . set ( edge . id , updatedEdge ) ;
244+ }
245+ }
246+ } ) ;
190247 }
191248 }
192249 } ) ;
193250
194251 onNodesChange ( changes ) ;
195252 } ,
196- [ nodes , onNodesChange ] ,
253+ [ nodes , edges , onNodesChange ] ,
197254 ) ;
198255
199256 const handleEdgesChange = useCallback (
@@ -216,18 +273,61 @@ function Flow({ className }: CanvasProps) {
216273 ( connection : Connection ) => {
217274 if ( ! connection . source || ! connection . target || ! ydoc ) return ;
218275
219- const newEdge : Edge = {
220- id : `e${ connection . source } -${ connection . target } ` ,
221- source : connection . source ,
222- target : connection . target ,
223- sourceHandle : connection . sourceHandle || undefined ,
224- targetHandle : connection . targetHandle || undefined ,
225- } ;
226-
227- ydoc . getMap ( "edges" ) . set ( newEdge . id , newEdge ) ;
228- setEdges ( ( eds ) => addEdge ( connection , eds ) ) ;
276+ const isConnected = edges . some (
277+ ( edge ) =>
278+ ( edge . source === connection . source &&
279+ edge . target === connection . target ) ||
280+ ( edge . source === connection . target &&
281+ edge . target === connection . source ) ,
282+ ) ;
283+
284+ if ( isConnected ) return ;
285+
286+ const sourceNode = nodes . find ( ( n ) => n . id === connection . source ) ;
287+ const targetNode = nodes . find ( ( n ) => n . id === connection . target ) ;
288+
289+ if ( sourceNode && targetNode ) {
290+ const handlePositions = [
291+ Position . Left ,
292+ Position . Right ,
293+ Position . Top ,
294+ Position . Bottom ,
295+ ] ;
296+ let shortestDistance = Infinity ;
297+ let closestHandles = {
298+ source : connection . sourceHandle ,
299+ target : connection . targetHandle ,
300+ } ;
301+
302+ handlePositions . forEach ( ( sourceHandle ) => {
303+ handlePositions . forEach ( ( targetHandle ) => {
304+ const sourcePosition = getHandlePosition ( sourceNode , sourceHandle ) ;
305+ const targetPosition = getHandlePosition ( targetNode , targetHandle ) ;
306+ const distance = Math . sqrt (
307+ Math . pow ( sourcePosition . x - targetPosition . x , 2 ) +
308+ Math . pow ( sourcePosition . y - targetPosition . y , 2 ) ,
309+ ) ;
310+
311+ if ( distance < shortestDistance ) {
312+ shortestDistance = distance ;
313+ closestHandles = { source : sourceHandle , target : targetHandle } ;
314+ }
315+ } ) ;
316+ } ) ;
317+
318+ const newEdge : Edge = {
319+ id : `e${ connection . source } -${ connection . target } ` ,
320+ source : connection . source ,
321+ target : connection . target ,
322+ sourceHandle : closestHandles . source ,
323+ targetHandle : closestHandles . target ,
324+ } ;
325+
326+ ydoc . getMap ( "edges" ) . set ( newEdge . id , newEdge ) ;
327+ setEdges ( ( eds ) => addEdge ( newEdge , eds ) ) ;
328+ }
229329 } ,
230- [ setEdges ] ,
330+ [ setEdges , edges , nodes , ydoc ] ,
231331 ) ;
232332
233333 const nodeTypes = useMemo ( ( ) => ( { note : NoteNode } ) , [ ] ) ;
0 commit comments