Bin
2025-12-16 9e0b2ba2c317b1a86212f24cbae3195ad1f3dbfa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { Circle } from "react-konva";
import { useRef, useEffect, useImperativeHandle, forwardRef } from "react";
import type { GhostPoint as GhostPointType } from "../types";
 
interface GhostPointProps {
  ghostPoint: GhostPointType | null;
  transform: { zoom: number; offsetX: number; offsetY: number };
  fitScale: number;
  isShiftKeyHeld?: boolean; // Made optional - if ghostPoint is set, Shift was held
  maxPoints?: number;
  initialPointsLength: number;
  isDragging?: boolean;
}
 
export interface GhostPointRef {
  updatePosition: (x: number, y: number) => void;
}
 
export const GhostPoint = forwardRef<GhostPointRef, GhostPointProps>(
  ({ ghostPoint, transform, fitScale, isShiftKeyHeld, maxPoints, initialPointsLength, isDragging = false }, ref) => {
    if (!ghostPoint) {
      return null;
    }
 
    // Hide ghost point when maxPoints is reached
    if (maxPoints !== undefined && initialPointsLength >= maxPoints) {
      return null;
    }
 
    // Scale radius to compensate for Layer scaling
    const scale = transform.zoom * fitScale;
    const radius = 6 / scale;
 
    // Use a ref to force Konva to update position
    const circleRef = useRef<Konva.Circle>(null);
 
    // Expose updatePosition method via ref
    useImperativeHandle(ref, () => ({
      updatePosition: (x: number, y: number) => {
        if (circleRef.current) {
          circleRef.current.setPosition({ x, y });
          // Force Konva to redraw
          const stage = circleRef.current.getStage();
          if (stage) {
            stage.batchDraw();
          }
        }
      },
    }));
 
    // Update position whenever ghostPoint changes
    useEffect(() => {
      if (circleRef.current && ghostPoint) {
        circleRef.current.setPosition({ x: ghostPoint.x, y: ghostPoint.y });
        // Force Konva to redraw
        const stage = circleRef.current.getStage();
        if (stage) {
          stage.batchDraw();
        }
      }
    }, [ghostPoint?.x, ghostPoint?.y]);
 
    // Use a key that includes position to force re-render when position changes
    // Round position to avoid key changes from floating point precision
    const keyX = Math.round(ghostPoint.x * 100) / 100;
    const keyY = Math.round(ghostPoint.y * 100) / 100;
 
    return (
      <Circle
        ref={circleRef}
        key={`ghost-point-${keyX}-${keyY}-${ghostPoint.prevPointId}-${ghostPoint.nextPointId}`}
        x={ghostPoint.x}
        y={ghostPoint.y}
        radius={radius}
        fill="#87CEEB"
        stroke="white"
        strokeWidth={2}
        strokeScaleEnabled={false}
        listening={false}
      />
    );
  },
);