import { useRef } from "react"; import { Rnd } from "react-rnd"; import { cn } from "@/lib/utils"; import { getArrowComponent } from "./ArrowSvgs"; import type { AnnotationRegion } from "./types"; interface AnnotationOverlayProps { annotation: AnnotationRegion; isSelected: boolean; containerWidth: number; containerHeight: number; onPositionChange: (id: string, position: { x: number; y: number }) => void; onSizeChange: (id: string, size: { width: number; height: number }) => void; onClick: (id: string) => void; zIndex: number; isSelectedBoost: boolean; // Boost z-index when selected for easy editing } export function AnnotationOverlay({ annotation, isSelected, containerWidth, containerHeight, onPositionChange, onSizeChange, onClick, zIndex, isSelectedBoost, }: AnnotationOverlayProps) { const x = (annotation.position.x / 100) * containerWidth; const y = (annotation.position.y / 100) * containerHeight; const width = (annotation.size.width / 100) * containerWidth; const height = (annotation.size.height / 100) * containerHeight; const isDraggingRef = useRef(false); const renderArrow = () => { const direction = annotation.figureData?.arrowDirection || "right"; const color = annotation.figureData?.color || "#34B27B"; const strokeWidth = annotation.figureData?.strokeWidth || 4; const ArrowComponent = getArrowComponent(direction); return ; }; const renderContent = () => { switch (annotation.type) { case "text": return (
{annotation.content}
); case "image": if (annotation.content && annotation.content.startsWith("data:image")) { return ( Annotation ); } return (
No image
); case "figure": if (!annotation.figureData) { return (
No arrow data
); } return (
{renderArrow()}
); default: return null; } }; return ( { isDraggingRef.current = true; }} onDragStop={(_e, d) => { const xPercent = (d.x / containerWidth) * 100; const yPercent = (d.y / containerHeight) * 100; onPositionChange(annotation.id, { x: xPercent, y: yPercent }); // Reset dragging flag after a short delay to prevent click event setTimeout(() => { isDraggingRef.current = false; }, 100); }} onResizeStop={(_e, _direction, ref, _delta, position) => { const xPercent = (position.x / containerWidth) * 100; const yPercent = (position.y / containerHeight) * 100; const widthPercent = (ref.offsetWidth / containerWidth) * 100; const heightPercent = (ref.offsetHeight / containerHeight) * 100; onPositionChange(annotation.id, { x: xPercent, y: yPercent }); onSizeChange(annotation.id, { width: widthPercent, height: heightPercent }); }} onClick={() => { if (isDraggingRef.current) return; onClick(annotation.id); }} bounds="parent" className={cn( "cursor-move transition-all", isSelected && "ring-2 ring-[#34B27B] ring-offset-2 ring-offset-transparent", )} style={{ zIndex: isSelectedBoost ? zIndex + 1000 : zIndex, // Boost selected annotation to ensure it's on top pointerEvents: isSelected ? "auto" : "none", border: isSelected ? "2px solid rgba(52, 178, 123, 0.8)" : "none", backgroundColor: isSelected ? "rgba(52, 178, 123, 0.1)" : "transparent", boxShadow: isSelected ? "0 0 0 1px rgba(52, 178, 123, 0.35)" : "none", }} enableResizing={isSelected} disableDragging={!isSelected} resizeHandleStyles={{ topLeft: { width: "12px", height: "12px", backgroundColor: isSelected ? "white" : "transparent", border: isSelected ? "2px solid #34B27B" : "none", borderRadius: "50%", left: "-6px", top: "-6px", cursor: "nwse-resize", }, topRight: { width: "12px", height: "12px", backgroundColor: isSelected ? "white" : "transparent", border: isSelected ? "2px solid #34B27B" : "none", borderRadius: "50%", right: "-6px", top: "-6px", cursor: "nesw-resize", }, bottomLeft: { width: "12px", height: "12px", backgroundColor: isSelected ? "white" : "transparent", border: isSelected ? "2px solid #34B27B" : "none", borderRadius: "50%", left: "-6px", bottom: "-6px", cursor: "nesw-resize", }, bottomRight: { width: "12px", height: "12px", backgroundColor: isSelected ? "white" : "transparent", border: isSelected ? "2px solid #34B27B" : "none", borderRadius: "50%", right: "-6px", bottom: "-6px", cursor: "nwse-resize", }, }} >
{renderContent()}
); }