import { useRef } from "react"; import { Rnd } from "react-rnd"; import type { AnnotationRegion } from "./types"; import { cn } from "@/lib/utils"; import { getArrowComponent } from "./ArrowSvgs"; 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()}
); }