Skip to content

Commit a6ce459

Browse files
committed
feat: 가장 가까운 핸들에 연결, 노드 간 1개의 엣지만 가능하게 수정
1 parent 428419b commit a6ce459

File tree

2 files changed

+140
-12
lines changed

2 files changed

+140
-12
lines changed

apps/frontend/src/components/canvas/index.tsx

Lines changed: 112 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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";
2930
import { useCollaborativeCursors } from "@/hooks/useCursor";
3031
import { CollaborativeCursors } from "../CursorView";
3132

33+
import { getHandlePosition } from "@/lib/getHandlePosition";
34+
3235
const proOptions = { hideAttribution: true };
3336

3437
interface 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 }), []);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Position, type Node } from "@xyflow/react";
2+
export function getHandlePosition(node: Node, handleId: Position) {
3+
const nodeElement = document.querySelector(`[data-id="${node.id}"]`);
4+
const nodeRect = nodeElement!.getBoundingClientRect();
5+
const nodeWidth = nodeRect.width;
6+
const nodeHeight = nodeRect.height;
7+
8+
const positions = {
9+
[Position.Left]: {
10+
x: node.position.x,
11+
y: node.position.y + nodeHeight / 2,
12+
},
13+
[Position.Right]: {
14+
x: node.position.x + nodeWidth,
15+
y: node.position.y + nodeHeight / 2,
16+
},
17+
[Position.Top]: {
18+
x: node.position.x + nodeWidth / 2,
19+
y: node.position.y,
20+
},
21+
[Position.Bottom]: {
22+
x: node.position.x + nodeWidth / 2,
23+
y: node.position.y + nodeHeight,
24+
},
25+
};
26+
27+
return positions[handleId];
28+
}

0 commit comments

Comments
 (0)