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

107 lines
2.8 KiB
TypeScript
Raw Normal View History

/**
* WireInProgressRenderer — live preview while drawing a wire.
* Shows the fixed waypoints + a dynamic elbow segment to the mouse cursor.
* Includes a full-canvas crosshair at the cursor position (CircuitJS-style)
* to help with alignment and precision on touch devices.
*/
import React from 'react';
import type { WireInProgress } from '../../types/wire';
import { generatePreviewPath, generateOrthogonalPath } from '../../utils/wireUtils';
/** Crosshair arm length in world pixels — large enough to span any visible area */
const CROSSHAIR_EXTENT = 8000;
interface Props {
wireInProgress: WireInProgress;
/** When true, show the full-canvas crosshair guides (aiming mode on touch) */
isAiming?: boolean;
}
export const WireInProgressRenderer: React.FC<Props> = ({ wireInProgress, isAiming = false }) => {
const { startEndpoint, waypoints, color, currentX, currentY } = wireInProgress;
const path = generatePreviewPath(
{ x: startEndpoint.x, y: startEndpoint.y },
waypoints,
currentX,
currentY,
);
if (!path) return null;
return (
<g className="wire-in-progress" style={{ pointerEvents: 'none' }}>
{/* ── Crosshair: full-canvas alignment guides (only during aiming) ── */}
{isAiming && (
<>
<line
x1={currentX - CROSSHAIR_EXTENT} y1={currentY}
x2={currentX + CROSSHAIR_EXTENT} y2={currentY}
stroke="rgba(255,255,255,0.25)"
strokeWidth="0.5"
strokeDasharray="8,6"
/>
<line
x1={currentX} y1={currentY - CROSSHAIR_EXTENT}
x2={currentX} y2={currentY + CROSSHAIR_EXTENT}
stroke="rgba(255,255,255,0.25)"
strokeWidth="0.5"
strokeDasharray="8,6"
/>
</>
)}
{/* Dark outline */}
<path d={path} stroke="#1a1a1a" strokeWidth="5" fill="none" />
{/* Colored wire */}
<path d={path} stroke={color} strokeWidth="2" fill="none" />
{/* Dashed overlay to indicate "in progress" */}
<path
d={path}
stroke="#ffffff"
strokeWidth="1.5"
fill="none"
strokeDasharray="6,4"
opacity="0.5"
/>
{/* Start pin marker */}
<circle
cx={startEndpoint.x}
cy={startEndpoint.y}
r="4"
fill={color}
stroke="white"
strokeWidth="1.5"
/>
{/* Waypoint markers (locked-in corners) */}
{waypoints.map((wp, i) => (
<circle
key={i}
cx={wp.x}
cy={wp.y}
r="3"
fill={color}
stroke="white"
strokeWidth="1"
/>
))}
{/* Cursor marker */}
<circle
cx={currentX}
cy={currentY}
r="4"
fill={color}
stroke="white"
strokeWidth="1.5"
opacity="0.7"
/>
</g>
);
};