"use client"; import { ConnectionLineType, getBezierPath, getSimpleBezierPath, getSmoothStepPath, getStraightPath, type ConnectionLineComponentProps, useReactFlow, } from "@xyflow/react"; import { useEffect, useMemo } from "react"; import { HANDLE_SNAP_RADIUS_PX, resolveCanvasMagnetTarget, } from "@/components/canvas/canvas-connection-magnetism"; import { useCanvasConnectionMagnetism } from "@/components/canvas/canvas-connection-magnetism-context"; import { connectionLineAccentRgb } from "@/lib/canvas-utils"; function hasSameMagnetTarget( a: Parameters["setActiveTarget"]>[0], b: Parameters["setActiveTarget"]>[0], ): boolean { if (a === b) { return true; } if (!a || !b) { return false; } return ( a.nodeId === b.nodeId && a.handleId === b.handleId && a.handleType === b.handleType && a.centerX === b.centerX && a.centerY === b.centerY && a.distancePx === b.distancePx ); } export default function CustomConnectionLine({ connectionLineType, fromNode, fromHandle, fromX, fromY, toX, toY, fromPosition, toPosition, connectionStatus, }: ConnectionLineComponentProps) { const { getNodes, getEdges } = useReactFlow(); const { activeTarget, setActiveTarget } = useCanvasConnectionMagnetism(); const fromHandleId = fromHandle?.id; const fromNodeId = fromNode?.id; const fromHandleType = fromHandle?.type === "source" || fromHandle?.type === "target" ? fromHandle.type : null; const resolvedMagnetTarget = useMemo(() => { if (!fromHandleType || !fromNodeId) { return null; } return resolveCanvasMagnetTarget({ point: { x: toX, y: toY }, fromNodeId, fromHandleId: fromHandleId ?? undefined, fromHandleType, nodes: getNodes(), edges: getEdges(), }); }, [fromHandleId, fromHandleType, fromNodeId, getEdges, getNodes, toX, toY]); useEffect(() => { if (hasSameMagnetTarget(activeTarget, resolvedMagnetTarget)) { return; } setActiveTarget(resolvedMagnetTarget); }, [activeTarget, resolvedMagnetTarget, setActiveTarget]); const magnetTarget = activeTarget ?? resolvedMagnetTarget; const snappedTarget = magnetTarget && magnetTarget.distancePx <= HANDLE_SNAP_RADIUS_PX ? magnetTarget : null; const targetX = snappedTarget?.centerX ?? toX; const targetY = snappedTarget?.centerY ?? toY; const pathParams = { sourceX: fromX, sourceY: fromY, sourcePosition: fromPosition, targetX, targetY, targetPosition: toPosition, }; let path = ""; switch (connectionLineType) { case ConnectionLineType.Bezier: [path] = getBezierPath(pathParams); break; case ConnectionLineType.SimpleBezier: [path] = getSimpleBezierPath(pathParams); break; case ConnectionLineType.Step: [path] = getSmoothStepPath({ ...pathParams, borderRadius: 0, }); break; case ConnectionLineType.SmoothStep: [path] = getSmoothStepPath(pathParams); break; default: [path] = getStraightPath(pathParams); } const [r, g, b] = connectionLineAccentRgb(fromNode.type, fromHandleId); const opacity = connectionStatus === "invalid" ? 0.45 : 1; const strokeWidth = snappedTarget ? 3.25 : 2.5; const filter = snappedTarget ? `drop-shadow(0 0 3px rgba(${r}, ${g}, ${b}, 0.7)) drop-shadow(0 0 8px rgba(${r}, ${g}, ${b}, 0.48))` : undefined; return ( ); }