холст фигуры меняю

This commit is contained in:
2025-07-16 19:07:20 +03:00
parent 6933fab3bb
commit dfb4d377d0

View File

@@ -15,6 +15,8 @@ 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[];
@@ -65,7 +67,6 @@ const DraggableElement: React.FC<DraggableElementProps> = React.memo(({
gridSize,
}) => {
const layout = element.layout || { x: 50, y: 50, width: 300, height: 80, zIndex: 1 };
const elementColor = ELEMENT_COLORS[element.type];
const handleDragStop = useCallback((_e: any, d: { x: number; y: number }) => {
// Вызываем onUpdate только если позиция действительно изменилась,
@@ -95,6 +96,57 @@ const DraggableElement: React.FC<DraggableElementProps> = React.memo(({
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 <Input readOnly placeholder={element.placeholder || 'Введите текст'} className="pointer-events-none w-full bg-white" />;
case 'textarea':
return <Textarea readOnly rows={3} placeholder={element.placeholder || 'Введите текст'} className="pointer-events-none w-full resize-none bg-white" />;
case 'number':
return <Input type="number" readOnly placeholder={element.placeholder || '0'} className="pointer-events-none w-full bg-white" />;
case 'date':
return <Input type="date" readOnly className="pointer-events-none w-full bg-white" />;
case 'select':
return (
<div className="pointer-events-none w-full">
<div className="flex h-10 w-full items-center justify-between rounded-md border border-input bg-white px-3 py-2 text-sm">
<span className="text-gray-500">
{element.placeholder || 'Выберите значение'}
</span>
<ChevronDown className="h-4 w-4 opacity-50" />
</div>
</div>
);
case 'radio':
return (
<div className="space-y-2 pointer-events-none">
{(element.options?.length ? element.options : [{ label: 'Вариант 1' }]).map((opt, idx) => (
<div key={idx} className="flex items-center gap-2">
<div className="w-4 h-4 border-2 border-gray-300 rounded-full bg-white" />
<span className="text-sm text-gray-900">{opt.label || opt.value || `Вариант ${idx + 1}`}</span>
</div>
))}
</div>
);
case 'checkbox':
return (
<div className="flex items-center gap-2 pointer-events-none">
<div className="w-4 h-4 border-2 border-gray-300 rounded bg-white" />
<span className="text-sm text-gray-900">{element.placeholder || element.label || 'Отметить'}</span>
</div>
);
default:
return null;
}
}, [element]);
return (
<Rnd
dragGrid={[gridSize, gridSize]}
@@ -107,47 +159,73 @@ const DraggableElement: React.FC<DraggableElementProps> = React.memo(({
}}
onDragStop={handleDragStop}
onResizeStop={handleResizeStop}
minWidth={150}
minHeight={40}
minWidth={120}
minHeight={32}
bounds="parent"
className={`transition-shadow duration-200 group ${isSelected ? 'ring-2 ring-blue-500 ring-offset-2 z-20' : 'z-10'}`}
className={`group ${isSelected ? 'ring-2 ring-blue-500 ring-offset-2 z-20' : 'z-10'}`}
onClick={onSelect}
>
<div className={`h-full rounded-lg border-2 p-3 cursor-move ${elementColor} ${isSelected ? 'shadow-xl' : 'shadow-md'} transition-all duration-200`}>
<div className="flex items-center justify-between mb-2">
<div className={`h-full cursor-move ${
isSelected
? 'bg-blue-50/20 border border-blue-200/50 rounded-md p-2'
: 'bg-transparent border border-transparent rounded-md hover:bg-gray-50/10 p-1'
}`}>
{/* Label and controls (only visible when selected or hovered) */}
<div className={`flex items-center justify-between mb-1 ${
isSelected ? 'opacity-100' : 'opacity-0 group-hover:opacity-60'
}`}>
<div className="flex items-center gap-2">
{ELEMENT_ICONS[element.type]}
<span className="font-medium text-sm truncate">{element.label || `${element.type} элемент`}</span>
<span className="font-medium text-xs text-gray-600 truncate">
{element.label || element.name || `${element.type} элемент`}
</span>
</div>
<div className="flex items-center gap-1">
{onEdit && (
<Button
variant="ghost"
size="icon"
className="h-6 w-6 cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity hover:bg-blue-100"
className="h-4 w-4 cursor-pointer hover:bg-blue-100"
onClick={handleEditClick}
>
<Edit className="h-3 w-3 text-blue-600" />
<Edit className="h-2.5 w-2.5 text-blue-600" />
</Button>
)}
<Button
variant="ghost"
size="icon"
className="h-6 w-6 cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity hover:bg-red-100"
className="h-4 w-4 cursor-pointer hover:bg-red-100"
onClick={handleDeleteClick}
>
<Trash2 className="h-3 w-3 text-red-600" />
<Trash2 className="h-2.5 w-2.5 text-red-600" />
</Button>
</div>
</div>
<div className="text-xs opacity-75 space-y-1">
{element.required && <div className="text-red-700 font-medium"> Обязательное</div>}
{element.targetCells && element.targetCells.length > 0 && (
<div className="font-mono truncate">
{element.targetCells.map((cell, i) => <span key={i} className="mr-1.5 p-1 bg-black/5 rounded-sm">{cell.sheet}!{cell.cell}</span>)}
</div>
)}
</div>
{/* Real element preview */}
{previewControl && (
<div className="space-y-1 flex-1">
{element.label && (
<label className="block text-sm font-medium text-gray-900 pointer-events-none">
{element.label}
{element.required && <span className="text-red-500 ml-1">*</span>}
</label>
)}
{previewControl}
</div>
)}
{/* Additional info (only visible on selection to preserve visual clarity & perf) */}
{isSelected && (
<div className="text-xs opacity-75 space-y-1 mt-1">
{element.targetCells && element.targetCells.length > 0 && (
<div className="font-mono truncate">
{element.targetCells.map((cell, i) => (
<span key={i} className="mr-1 px-1 py-0.5 bg-black/5 rounded text-xs">{cell.sheet}!{cell.cell}</span>
))}
</div>
)}
</div>
)}
</div>
</Rnd>
);