velxio/frontend/src/components/simulator/WireRenderer.tsx

88 lines
2.3 KiB
TypeScript

/**
* WireRenderer — purely visual renderer for a single wire.
* All interaction (click/hover/drag) is handled by SimulatorCanvas.
*/
import React from 'react';
import type { Wire } from '../../types/wire';
import { generateOrthogonalPath } from '../../utils/wireUtils';
interface WireRendererProps {
wire: Wire;
isSelected: boolean;
isHovered: boolean;
/** Temporary waypoints used during drag preview */
previewWaypoints?: { x: number; y: number }[];
/** Override the full SVG path string (used during segment drag preview) */
overridePath?: string;
}
export const WireRenderer: React.FC<WireRendererProps> = ({
wire,
isSelected,
isHovered,
previewWaypoints,
overridePath,
}) => {
// Guard against null/undefined wire
if (!wire || !wire.start || !wire.end) return null;
const waypoints = previewWaypoints ?? wire.waypoints;
const path = overridePath ?? generateOrthogonalPath(wire.start, waypoints, wire.end);
if (!path) return null;
const color = wire.color;
const strokeW = isSelected ? 3 : 2;
const outlineW = isSelected ? 6 : 5;
const opacity = isSelected || isHovered ? 1 : 0.85;
return (
<g style={{ pointerEvents: 'none' }}>
{/* Dark outline for wire crossing effect */}
<path d={path} stroke="#1a1a1a" strokeWidth={outlineW} fill="none" />
{/* Hover highlight (below wire) */}
{isHovered && !isSelected && (
<path
d={path}
stroke="#ffffff"
strokeWidth="6"
fill="none"
opacity="0.2"
/>
)}
{/* Visible wire */}
<path
d={path}
stroke={color}
strokeWidth={strokeW}
fill="none"
opacity={opacity}
/>
{/* Selection dashed highlight */}
{isSelected && (
<path
d={path}
stroke="#ffffff"
strokeWidth="1.5"
fill="none"
strokeDasharray="6,4"
opacity="0.6"
/>
)}
{/* Endpoint dots */}
<circle cx={wire.start.x} cy={wire.start.y} r="3" fill={color} stroke="#1a1a1a" strokeWidth="1" />
<circle cx={wire.end.x} cy={wire.end.y} r="3" fill={color} stroke="#1a1a1a" strokeWidth="1" />
{/* Waypoint dots */}
{waypoints.map((wp, i) => (
<circle key={i} cx={wp.x} cy={wp.y} r="2" fill={color} />
))}
</g>
);
};