ERP-node/frontend/components/dataflow/connection/redesigned/RightPanel/VisualMapping/ConnectionLine.tsx

153 lines
4.2 KiB
TypeScript

"use client";
import React, { useState } from "react";
import { X } from "lucide-react";
interface ConnectionLineProps {
id: string;
fromX: number;
fromY: number;
toX: number;
toY: number;
isValid: boolean;
mapping: any;
onDelete: () => void;
}
/**
* 🔗 SVG 연결선 컴포넌트
* - 베지어 곡선으로 부드러운 연결선 표시
* - 유효성에 따른 색상 변경
* - 호버 시 삭제 버튼 표시
*/
const ConnectionLine: React.FC<ConnectionLineProps> = ({ id, fromX, fromY, toX, toY, isValid, mapping, onDelete }) => {
const [isHovered, setIsHovered] = useState(false);
// 베지어 곡선 제어점 계산
const controlPointOffset = Math.abs(toX - fromX) * 0.5;
const controlPoint1X = fromX + controlPointOffset;
const controlPoint1Y = fromY;
const controlPoint2X = toX - controlPointOffset;
const controlPoint2Y = toY;
// 패스 생성
const pathData = `M ${fromX} ${fromY} C ${controlPoint1X} ${controlPoint1Y}, ${controlPoint2X} ${controlPoint2Y}, ${toX} ${toY}`;
// 색상 결정
const strokeColor = isValid
? isHovered
? "#10b981" // green-500 hover
: "#22c55e" // green-500
: isHovered
? "#f97316" // orange-500 hover
: "#fb923c"; // orange-400
// 중간점 계산 (삭제 버튼 위치)
const midX = (fromX + toX) / 2;
const midY = (fromY + toY) / 2;
return (
<g>
{/* 연결선 - 더 부드럽고 덜 방해되는 스타일 */}
<path
d={pathData}
stroke={strokeColor}
strokeWidth={isHovered ? "2.5" : "1.5"}
fill="none"
opacity={isHovered ? "0.9" : "0.6"}
strokeDasharray="0"
className="cursor-pointer transition-all duration-300"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={{ pointerEvents: "stroke" }}
/>
{/* 연결선 위의 투명한 넓은 영역 (호버 감지용) */}
<path
d={pathData}
stroke="transparent"
strokeWidth="12"
fill="none"
className="cursor-pointer"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={{ pointerEvents: "stroke" }}
/>
{/* 시작점 원 */}
<circle
cx={fromX}
cy={fromY}
r={isHovered ? "3.5" : "2.5"}
fill={strokeColor}
opacity={isHovered ? "0.9" : "0.7"}
className="transition-all duration-300"
/>
{/* 끝점 원 */}
<circle
cx={toX}
cy={toY}
r={isHovered ? "3.5" : "2.5"}
fill={strokeColor}
opacity={isHovered ? "0.9" : "0.7"}
className="transition-all duration-300"
/>
{/* 호버 시 삭제 버튼 */}
{isHovered && (
<g>
{/* 삭제 버튼 배경 */}
<circle
cx={midX}
cy={midY}
r="12"
fill="white"
stroke={strokeColor}
strokeWidth="2"
className="cursor-pointer"
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
style={{ pointerEvents: "all" }}
/>
{/* X 아이콘 */}
<g
transform={`translate(${midX - 4}, ${midY - 4})`}
className="cursor-pointer"
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
style={{ pointerEvents: "all" }}
>
<path d="M1 1L7 7M7 1L1 7" stroke={strokeColor} strokeWidth="1.5" strokeLinecap="round" />
</g>
</g>
)}
{/* 매핑 정보 툴팁 (호버 시) */}
{isHovered && (
<g>
<rect
x={midX - 60}
y={midY - 35}
width="120"
height="20"
rx="4"
fill="rgba(0, 0, 0, 0.8)"
style={{ pointerEvents: "none" }}
/>
<text x={midX} y={midY - 22} textAnchor="middle" fill="white" fontSize="10" style={{ pointerEvents: "none" }}>
{mapping.fromField.webType} {mapping.toField.webType}
</text>
</g>
)}
</g>
);
};
export default ConnectionLine;