дублирование в списке и отключить копирование при открытом диалговом окне
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/component/ui/select'
|
} from '@/component/ui/select'
|
||||||
import { ElementDefinition } from '@/entitiy/element/model/interface'
|
import { ElementDefinition } from '@/entitiy/element/model/interface'
|
||||||
|
import { useToast } from '@/lib/hooks/useToast'
|
||||||
import { ElementOption } from '@/type/template'
|
import { ElementOption } from '@/type/template'
|
||||||
import { ChevronDown, Plus, Trash2 } from 'lucide-react'
|
import { ChevronDown, Plus, Trash2 } from 'lucide-react'
|
||||||
|
|
||||||
@@ -36,6 +37,8 @@ export const selectDefinition: ElementDefinition<SelectConfig, string> = {
|
|||||||
return cells.map(target => ({ target, value: value || '' }))
|
return cells.map(target => ({ target, value: value || '' }))
|
||||||
},
|
},
|
||||||
Editor: ({ config, onChange }) => {
|
Editor: ({ config, onChange }) => {
|
||||||
|
const { warning } = useToast()
|
||||||
|
|
||||||
const handleAddOption = () => {
|
const handleAddOption = () => {
|
||||||
onChange({
|
onChange({
|
||||||
...config,
|
...config,
|
||||||
@@ -48,6 +51,19 @@ export const selectDefinition: ElementDefinition<SelectConfig, string> = {
|
|||||||
field: 'value' | 'label',
|
field: 'value' | 'label',
|
||||||
value: string
|
value: string
|
||||||
) => {
|
) => {
|
||||||
|
// Проверяем дублирование только для подписей (label)
|
||||||
|
if (field === 'label' && value.trim() !== '') {
|
||||||
|
const isDuplicate = config.options.some(
|
||||||
|
(option, i) =>
|
||||||
|
i !== index &&
|
||||||
|
option.label.trim().toLowerCase() === value.trim().toLowerCase()
|
||||||
|
)
|
||||||
|
if (isDuplicate) {
|
||||||
|
warning(`Такая подпись уже существует: "${value.trim()}"`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onChange({
|
onChange({
|
||||||
...config,
|
...config,
|
||||||
options: config.options.map((option, i) =>
|
options: config.options.map((option, i) =>
|
||||||
@@ -74,31 +90,59 @@ export const selectDefinition: ElementDefinition<SelectConfig, string> = {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-h-32 space-y-2 overflow-y-auto">
|
<div className="max-h-32 space-y-2 overflow-y-auto">
|
||||||
{config.options.map((option, index) => (
|
{config.options.map((option, index) => {
|
||||||
<div key={index} className="flex gap-2">
|
const isDuplicateLabel = config.options.some(
|
||||||
<Input
|
(otherOption, i) =>
|
||||||
placeholder="Значение"
|
i !== index &&
|
||||||
value={option.value}
|
option.label.trim() !== '' &&
|
||||||
onChange={e =>
|
otherOption.label.trim().toLowerCase() ===
|
||||||
handleUpdateOption(index, 'value', e.target.value)
|
option.label.trim().toLowerCase()
|
||||||
}
|
)
|
||||||
/>
|
// Убираем проверку дублирования для значений
|
||||||
<Input
|
const isDuplicateValue = false
|
||||||
placeholder="Подпись"
|
|
||||||
value={option.label}
|
return (
|
||||||
onChange={e =>
|
<div key={index} className="flex gap-2">
|
||||||
handleUpdateOption(index, 'label', e.target.value)
|
<div className="flex-1">
|
||||||
}
|
<Input
|
||||||
/>
|
placeholder="Значение"
|
||||||
<Button
|
value={option.value}
|
||||||
variant="outline"
|
onChange={e =>
|
||||||
size="icon"
|
handleUpdateOption(index, 'value', e.target.value)
|
||||||
onClick={() => handleRemoveOption(index)}
|
}
|
||||||
>
|
className={isDuplicateValue ? 'border-red-500' : ''}
|
||||||
<Trash2 className="h-4 w-4" />
|
/>
|
||||||
</Button>
|
{isDuplicateValue && (
|
||||||
</div>
|
<p className="mt-1 text-xs text-red-500">
|
||||||
))}
|
Это значение уже используется
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<Input
|
||||||
|
placeholder="Подпись"
|
||||||
|
value={option.label}
|
||||||
|
onChange={e =>
|
||||||
|
handleUpdateOption(index, 'label', e.target.value)
|
||||||
|
}
|
||||||
|
className={isDuplicateLabel ? 'border-red-500' : ''}
|
||||||
|
/>
|
||||||
|
{isDuplicateLabel && (
|
||||||
|
<p className="mt-1 text-xs text-red-500">
|
||||||
|
Такая подпись уже существует
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => handleRemoveOption(index)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -621,6 +621,7 @@ export const ElementConstructor: React.FC<ElementConstructorProps> = ({
|
|||||||
onLayoutSettingsChange={onLayoutSettingsChange || (() => {})}
|
onLayoutSettingsChange={onLayoutSettingsChange || (() => {})}
|
||||||
onElementEdit={handleEditElement}
|
onElementEdit={handleEditElement}
|
||||||
onElementAdd={handleElementCopy}
|
onElementAdd={handleElementCopy}
|
||||||
|
isDialogOpen={isAddDialogOpen || !!editingElement}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -25,19 +25,6 @@ const ELEMENT_MIN_SIZE: Record<string, { width: number; height: number }> = {
|
|||||||
button_group: { width: 180, height: 80 },
|
button_group: { width: 180, height: 80 },
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VisualLayoutEditorProps {
|
|
||||||
elements: TemplateElement[]
|
|
||||||
layoutSettings: FormLayoutSettings
|
|
||||||
onElementUpdate: (
|
|
||||||
elementId: string,
|
|
||||||
updates: Partial<TemplateElement>
|
|
||||||
) => void
|
|
||||||
onElementDelete: (elementId: string) => void
|
|
||||||
onLayoutSettingsChange: (settings: FormLayoutSettings) => void
|
|
||||||
onElementEdit?: (element: TemplateElement) => void
|
|
||||||
onElementAdd?: (element: Omit<TemplateElement, 'id'>) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DraggableElementProps {
|
interface DraggableElementProps {
|
||||||
element: TemplateElement
|
element: TemplateElement
|
||||||
isSelected: boolean
|
isSelected: boolean
|
||||||
@@ -213,6 +200,20 @@ const DraggableElement: React.FC<DraggableElementProps> = React.memo(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
interface VisualLayoutEditorProps {
|
||||||
|
elements: TemplateElement[]
|
||||||
|
layoutSettings: FormLayoutSettings
|
||||||
|
onElementUpdate: (
|
||||||
|
elementId: string,
|
||||||
|
updates: Partial<TemplateElement>
|
||||||
|
) => void
|
||||||
|
onElementDelete: (elementId: string) => void
|
||||||
|
onLayoutSettingsChange: (settings: FormLayoutSettings) => void
|
||||||
|
onElementEdit?: (element: TemplateElement) => void
|
||||||
|
onElementAdd?: (element: Omit<TemplateElement, 'id'>) => void
|
||||||
|
isDialogOpen?: boolean // Добавляем новый проп
|
||||||
|
}
|
||||||
|
|
||||||
export const VisualLayoutEditor: React.FC<VisualLayoutEditorProps> = ({
|
export const VisualLayoutEditor: React.FC<VisualLayoutEditorProps> = ({
|
||||||
elements,
|
elements,
|
||||||
layoutSettings,
|
layoutSettings,
|
||||||
@@ -221,6 +222,7 @@ export const VisualLayoutEditor: React.FC<VisualLayoutEditorProps> = ({
|
|||||||
onLayoutSettingsChange,
|
onLayoutSettingsChange,
|
||||||
onElementEdit,
|
onElementEdit,
|
||||||
onElementAdd,
|
onElementAdd,
|
||||||
|
isDialogOpen = false, // Значение по умолчанию
|
||||||
}) => {
|
}) => {
|
||||||
// console.log('VisualLayoutEditor render with elements:', elements)
|
// console.log('VisualLayoutEditor render with elements:', elements)
|
||||||
const [selectedElementId, setSelectedElementId] = useState<string | null>(
|
const [selectedElementId, setSelectedElementId] = useState<string | null>(
|
||||||
@@ -232,6 +234,11 @@ export const VisualLayoutEditor: React.FC<VisualLayoutEditorProps> = ({
|
|||||||
|
|
||||||
// Обработчик клавиш для копирования/вставки
|
// Обработчик клавиш для копирования/вставки
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Отключаем обработчики если открыт диалог
|
||||||
|
if (isDialogOpen) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if (e.ctrlKey || e.metaKey) {
|
if (e.ctrlKey || e.metaKey) {
|
||||||
if (e.key === 'c' && selectedElementId) {
|
if (e.key === 'c' && selectedElementId) {
|
||||||
@@ -248,6 +255,8 @@ export const VisualLayoutEditor: React.FC<VisualLayoutEditorProps> = ({
|
|||||||
...copiedElement.layout,
|
...copiedElement.layout,
|
||||||
x: (copiedElement.layout?.x || 0) + 20,
|
x: (copiedElement.layout?.x || 0) + 20,
|
||||||
y: (copiedElement.layout?.y || 0) + 20,
|
y: (copiedElement.layout?.y || 0) + 20,
|
||||||
|
width: copiedElement.layout?.width || 300,
|
||||||
|
height: copiedElement.layout?.height || 80,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
delete (newElement as any).id // Удаляем id чтобы создался новый
|
delete (newElement as any).id // Удаляем id чтобы создался новый
|
||||||
@@ -258,7 +267,7 @@ export const VisualLayoutEditor: React.FC<VisualLayoutEditorProps> = ({
|
|||||||
|
|
||||||
document.addEventListener('keydown', handleKeyDown)
|
document.addEventListener('keydown', handleKeyDown)
|
||||||
return () => document.removeEventListener('keydown', handleKeyDown)
|
return () => document.removeEventListener('keydown', handleKeyDown)
|
||||||
}, [selectedElementId, elements, copiedElement, onElementAdd])
|
}, [selectedElementId, elements, copiedElement, onElementAdd, isDialogOpen])
|
||||||
|
|
||||||
const canvasSize = useMemo(() => {
|
const canvasSize = useMemo(() => {
|
||||||
const PADDING_Y = 200
|
const PADDING_Y = 200
|
||||||
|
|||||||
Reference in New Issue
Block a user