fix(simulator): prevent crosshair from activating during wire segment editing on mobile
- Add data-segment-handle attribute to segment handle circles in WireLayer - Skip long-press aiming timer when touch targets segment handle - Add safety checks to cancel pending timer when segment drag starts - Ensure segment drag always takes priority over crosshair activation This fixes the bug where accidentally holding still while dragging a wire segment would activate the crosshair overlay and interrupt the editing workflow.master
parent
52321dffe8
commit
1973b0adbc
|
|
@ -402,6 +402,9 @@ export const SimulatorCanvas = () => {
|
||||||
// Any phase that uses aiming: cancel previous timer first
|
// Any phase that uses aiming: cancel previous timer first
|
||||||
if (wireAimingTimerRef.current) { clearTimeout(wireAimingTimerRef.current); wireAimingTimerRef.current = null; }
|
if (wireAimingTimerRef.current) { clearTimeout(wireAimingTimerRef.current); wireAimingTimerRef.current = null; }
|
||||||
|
|
||||||
|
// Check if touch is on a segment handle - if so, skip aiming timer (handle drag takes priority)
|
||||||
|
const isOnSegmentHandle = target?.closest('[data-segment-handle]');
|
||||||
|
|
||||||
const aimPhase = wireAimingPhaseRef.current;
|
const aimPhase = wireAimingPhaseRef.current;
|
||||||
|
|
||||||
// Already in an aiming phase (aiming_start or aiming_end) → touch updates crosshair immediately
|
// Already in an aiming phase (aiming_start or aiming_end) → touch updates crosshair immediately
|
||||||
|
|
@ -415,16 +418,21 @@ export const SimulatorCanvas = () => {
|
||||||
|
|
||||||
// wire_started → long press to aim for endpoint, short drag = pan
|
// wire_started → long press to aim for endpoint, short drag = pan
|
||||||
if (aimPhase === 'wire_started') {
|
if (aimPhase === 'wire_started') {
|
||||||
wireAimingTimerRef.current = setTimeout(() => {
|
// Only start aiming timer if NOT on a segment handle
|
||||||
wireAimingPhaseRef.current = 'aiming_end';
|
if (!isOnSegmentHandle) {
|
||||||
setWireAiming(true);
|
wireAimingTimerRef.current = setTimeout(() => {
|
||||||
if (navigator.vibrate) navigator.vibrate(30);
|
// Double-check: only activate if segment drag hasn't started
|
||||||
const world = toWorld(currentTouchRef.current.x, currentTouchRef.current.y);
|
if (segmentDragRef.current) return;
|
||||||
const aimX = world.x;
|
wireAimingPhaseRef.current = 'aiming_end';
|
||||||
const aimY = world.y + AIMING_OFFSET_Y;
|
setWireAiming(true);
|
||||||
setAimPosition({ x: aimX, y: aimY });
|
if (navigator.vibrate) navigator.vibrate(30);
|
||||||
useSimulatorStore.getState().updateWireInProgress(aimX, aimY);
|
const world = toWorld(currentTouchRef.current.x, currentTouchRef.current.y);
|
||||||
}, AIMING_LONG_PRESS_MS);
|
const aimX = world.x;
|
||||||
|
const aimY = world.y + AIMING_OFFSET_Y;
|
||||||
|
setAimPosition({ x: aimX, y: aimY });
|
||||||
|
useSimulatorStore.getState().updateWireInProgress(aimX, aimY);
|
||||||
|
}, AIMING_LONG_PRESS_MS);
|
||||||
|
}
|
||||||
|
|
||||||
isPanningRef.current = true;
|
isPanningRef.current = true;
|
||||||
panStartRef.current = {
|
panStartRef.current = {
|
||||||
|
|
@ -479,8 +487,10 @@ export const SimulatorCanvas = () => {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ── 6. Empty canvas → start pan + long press starts aiming (idle phase only) ──
|
// ── 6. Empty canvas → start pan + long press starts aiming (idle phase only) ──
|
||||||
if (aimPhase === 'idle') {
|
if (aimPhase === 'idle' && !isOnSegmentHandle) {
|
||||||
wireAimingTimerRef.current = setTimeout(() => {
|
wireAimingTimerRef.current = setTimeout(() => {
|
||||||
|
// Double-check: only activate if segment drag hasn't started
|
||||||
|
if (segmentDragRef.current) return;
|
||||||
wireAimingPhaseRef.current = 'aiming_start';
|
wireAimingPhaseRef.current = 'aiming_start';
|
||||||
setWireAiming(true);
|
setWireAiming(true);
|
||||||
if (navigator.vibrate) navigator.vibrate(30);
|
if (navigator.vibrate) navigator.vibrate(30);
|
||||||
|
|
@ -538,6 +548,11 @@ export const SimulatorCanvas = () => {
|
||||||
|
|
||||||
// ── Segment drag (wire editing) via touch ──
|
// ── Segment drag (wire editing) via touch ──
|
||||||
if (segmentDragRef.current) {
|
if (segmentDragRef.current) {
|
||||||
|
// Cancel any pending aiming timer - segment drag takes priority
|
||||||
|
if (wireAimingTimerRef.current) {
|
||||||
|
clearTimeout(wireAimingTimerRef.current);
|
||||||
|
wireAimingTimerRef.current = null;
|
||||||
|
}
|
||||||
const world = toWorld(touch.clientX, touch.clientY);
|
const world = toWorld(touch.clientX, touch.clientY);
|
||||||
const sd = segmentDragRef.current;
|
const sd = segmentDragRef.current;
|
||||||
sd.isDragging = true;
|
sd.isDragging = true;
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ export const WireLayer: React.FC<WireLayerProps> = ({
|
||||||
stroke="#007acc"
|
stroke="#007acc"
|
||||||
strokeWidth={sw}
|
strokeWidth={sw}
|
||||||
style={{ pointerEvents: 'all', cursor: handle.axis === 'horizontal' ? 'ns-resize' : 'ew-resize', touchAction: 'none' }}
|
style={{ pointerEvents: 'all', cursor: handle.axis === 'horizontal' ? 'ns-resize' : 'ew-resize', touchAction: 'none' }}
|
||||||
|
data-segment-handle={handle.segIndex}
|
||||||
onMouseDown={(e) => onHandleMouseDown(e, handle.segIndex)}
|
onMouseDown={(e) => onHandleMouseDown(e, handle.segIndex)}
|
||||||
onTouchStart={(e) => onHandleTouchStart?.(e, handle.segIndex)}
|
onTouchStart={(e) => onHandleTouchStart?.(e, handle.segIndex)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue