From 0d25446a7ecd056f6cd026e6aa1247bd224536c3 Mon Sep 17 00:00:00 2001 From: tlartem Date: Fri, 25 Jul 2025 14:11:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BA=D0=BE=D0=BF=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=20=D0=BF=D0=BE=20ctrl+c=20=D0=B8=20ctrl+v?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TemplateManager/ElementConstructor.tsx | 10 ++++ .../TemplateManager/VisualLayoutEditor.tsx | 52 ++++++++++++++++--- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/component/TemplateManager/ElementConstructor.tsx b/src/component/TemplateManager/ElementConstructor.tsx index b75e5a1..db14f98 100644 --- a/src/component/TemplateManager/ElementConstructor.tsx +++ b/src/component/TemplateManager/ElementConstructor.tsx @@ -578,6 +578,15 @@ export const ElementConstructor: React.FC = ({ resetForm() } + const handleElementCopy = (element: Omit) => { + const newElement: TemplateElement = { + ...element, + id: Date.now().toString(), + order: elements.length, + } + onElementAdd(newElement) + } + return (
= ({ onElementDelete={handleDeleteAndSelect} onLayoutSettingsChange={onLayoutSettingsChange || (() => {})} onElementEdit={handleEditElement} + onElementAdd={handleElementCopy} />
diff --git a/src/component/TemplateManager/VisualLayoutEditor.tsx b/src/component/TemplateManager/VisualLayoutEditor.tsx index aeedbd7..9e21d8e 100644 --- a/src/component/TemplateManager/VisualLayoutEditor.tsx +++ b/src/component/TemplateManager/VisualLayoutEditor.tsx @@ -1,5 +1,5 @@ import { Edit, Grid, Move, Trash2 } from 'lucide-react' -import React, { useCallback, useMemo, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { Rnd } from 'react-rnd' import { getElementDefinition } from '../../entitiy/element/model/interface' import { @@ -35,6 +35,7 @@ interface VisualLayoutEditorProps { onElementDelete: (elementId: string) => void onLayoutSettingsChange: (settings: FormLayoutSettings) => void onElementEdit?: (element: TemplateElement) => void + onElementAdd?: (element: Omit) => void } interface DraggableElementProps { @@ -219,11 +220,45 @@ export const VisualLayoutEditor: React.FC = ({ onElementDelete, onLayoutSettingsChange, onElementEdit, + onElementAdd, }) => { // console.log('VisualLayoutEditor render with elements:', elements) const [selectedElementId, setSelectedElementId] = useState( null ) + const [copiedElement, setCopiedElement] = useState( + null + ) + + // Обработчик клавиш для копирования/вставки + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.ctrlKey || e.metaKey) { + if (e.key === 'c' && selectedElementId) { + e.preventDefault() + const elementToCopy = elements.find(el => el.id === selectedElementId) + if (elementToCopy) { + setCopiedElement(elementToCopy) + } + } else if (e.key === 'v' && copiedElement && onElementAdd) { + e.preventDefault() + const newElement = { + ...copiedElement, + layout: { + ...copiedElement.layout, + x: (copiedElement.layout?.x || 0) + 20, + y: (copiedElement.layout?.y || 0) + 20, + }, + } + delete (newElement as any).id // Удаляем id чтобы создался новый + onElementAdd(newElement) + } + } + } + + document.addEventListener('keydown', handleKeyDown) + return () => document.removeEventListener('keydown', handleKeyDown) + }, [selectedElementId, elements, copiedElement, onElementAdd]) const canvasSize = useMemo(() => { const PADDING_Y = 200 @@ -267,12 +302,15 @@ export const VisualLayoutEditor: React.FC = ({ [onElementUpdate] ) - const handleCanvasClick = useCallback((e: React.MouseEvent) => { - // Снимаем выделение при клике по пустому месту - if (e.target === e.currentTarget) { - setSelectedElementId(null) - } - }, []) + const handleCanvasClick = useCallback( + (e: React.MouseEvent) => { + // Снимаем выделение при клике по пустому месту + if (e.target === e.currentTarget) { + setSelectedElementId(null) + } + }, + [] + ) const toggleGrid = useCallback(() => { onLayoutSettingsChange({