import { Calendar, CheckSquare, ChevronDown, Edit, Eye, EyeOff, FileText, Hash, Move, Trash2, Type } from 'lucide-react'; import React, { useCallback, useMemo, useState } from 'react'; import { Rnd } from 'react-rnd'; import { ElementLayout, ElementType, FormLayoutSettings, TemplateElement } from '../../types/template'; import { Button } from '../ui/button'; import { Input } from '../ui/input'; import { Textarea } from '../ui/textarea'; interface VisualLayoutEditorProps { elements: TemplateElement[]; layoutSettings: FormLayoutSettings; onElementUpdate: (elementId: string, updates: Partial) => void; onElementDelete: (elementId: string) => void; onLayoutSettingsChange: (settings: FormLayoutSettings) => void; onElementEdit?: (element: TemplateElement) => void; } interface DraggableElementProps { element: TemplateElement; isSelected: boolean; onSelect: () => void; onUpdate: (layout: ElementLayout) => void; onDelete: () => void; onEdit?: () => void; gridSize: number; } const ELEMENT_ICONS: Record = { text: , textarea: , number: , date: , select: , radio: , checkbox: , }; const ELEMENT_COLORS: Record = { text: 'bg-blue-100 border-blue-300 text-blue-800', textarea: 'bg-green-100 border-green-300 text-green-800', number: 'bg-purple-100 border-purple-300 text-purple-800', date: 'bg-orange-100 border-orange-300 text-orange-800', select: 'bg-indigo-100 border-indigo-300 text-indigo-800', radio: 'bg-pink-100 border-pink-300 text-pink-800', checkbox: 'bg-teal-100 border-teal-300 text-teal-800', }; const DraggableElement: React.FC = React.memo(({ element, isSelected, onSelect, onUpdate, onDelete, onEdit, gridSize, }) => { const layout = element.layout || { x: 50, y: 50, width: 300, height: 80, zIndex: 1 }; const handleDragStop = useCallback((_e: any, d: { x: number; y: number }) => { // Вызываем onUpdate только если позиция действительно изменилась, // чтобы избежать ложных срабатываний при клике. if (d.x !== layout.x || d.y !== layout.y) { onUpdate({ ...layout, x: d.x, y: d.y }); } }, [layout, onUpdate]); const handleResizeStop = useCallback((_e: any, _dir: any, ref: HTMLElement, _delta: any, pos: { x: number; y: number }) => { onUpdate({ ...layout, width: parseInt(ref.style.width, 10), height: parseInt(ref.style.height, 10), x: pos.x, y: pos.y, }); }, [layout, onUpdate]); const handleDeleteClick = useCallback((e: React.MouseEvent) => { e.stopPropagation(); onDelete(); }, [onDelete]); const handleEditClick = useCallback((e: React.MouseEvent) => { e.stopPropagation(); onEdit?.(); }, [onEdit]); /* * Render a lightweight, disabled preview of the element so that the user can * see how the real control looks directly on the canvas instead of a plain * colored square. Keeping the elements disabled and wrapped with * `pointer-events-none` guarantees that they do not interfere with the drag * behaviour of Rnd and keeps rendering costs minimal. */ const previewControl = useMemo(() => { switch (element.type) { case 'text': return ; case 'textarea': return