import { type ClassValue, clsx } from 'clsx' import { twMerge } from 'tailwind-merge' import { CellTarget, ElementLayout, FormLayoutSettings, TemplateElement, } from '../type/template' export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } // Миграция элементов из старого формата в новый export function migrateTemplateElement(element: any): TemplateElement { // Если элемент уже в новом формате if (element.targetCells && Array.isArray(element.targetCells)) { return element as TemplateElement } // Миграция из старого формата const targetCells: CellTarget[] = [] if (element.targetCell) { // Парсим старый формат targetCell (например "A1", "B5") const cellMatch = element.targetCell.match(/^([A-Z]+)(\d+)$/) if (cellMatch) { targetCells.push({ sheet: 'Report', // По умолчанию используем Report cell: element.targetCell, displayName: `Ячейка ${element.targetCell}`, }) } } return { ...element, targetCells, order: element.order || 0, layout: element.layout || generateDefaultLayout(element.order || 0), } as TemplateElement } // Получение настроек отображения по умолчанию export function getDefaultLayoutSettings(): FormLayoutSettings { return { gridSize: 20, showGrid: true, } } // Генерация позиции по умолчанию для нового элемента export function generateDefaultLayout(order: number = 0): ElementLayout { const cols = 4 // Количество колонок для автоматического размещения const elementWidth = 300 const elementHeight = 80 const padding = 50 const col = order % cols const row = Math.floor(order / cols) return { x: col * (elementWidth + padding) + padding, y: row * (elementHeight + padding) + padding, width: elementWidth, height: elementHeight, zIndex: 1, } } // Автоматическое размещение элементов на холсте export function autoArrangeElements( elements: TemplateElement[], gridSize: number = 20 ): TemplateElement[] { const elementWidth = 300 const elementHeight = 80 const padding = 50 const cols = 4 // Фиксированное количество колонок return elements.map((element, index) => { const col = index % cols const row = Math.floor(index / cols) const x = col * (elementWidth + padding) + padding const y = row * (elementHeight + padding) + padding // Привязка к сетке const snappedX = Math.round(x / gridSize) * gridSize const snappedY = Math.round(y / gridSize) * gridSize return { ...element, layout: { x: snappedX, y: snappedY, width: elementWidth, height: elementHeight, zIndex: 1, }, } }) } // Проверка пересечения элементов export function checkElementOverlap( element1: ElementLayout, element2: ElementLayout, threshold: number = 5 ): boolean { return !( element1.x + element1.width + threshold < element2.x || element2.x + element2.width + threshold < element1.x || element1.y + element1.height + threshold < element2.y || element2.y + element2.height + threshold < element1.y ) } // Поиск свободного места для элемента (поиск в разумных пределах) export function findFreePosition( newElement: ElementLayout, existingElements: ElementLayout[], gridSize: number = 20 ): ElementLayout { const step = gridSize const maxWidth = 1600 // Максимальная ширина поиска const maxHeight = 1200 // Максимальная высота поиска const maxAttempts = 500 let attempts = 0 for (let y = 50; y <= maxHeight - newElement.height; y += step) { for (let x = 50; x <= maxWidth - newElement.width; x += step) { attempts++ if (attempts > maxAttempts) break const testPosition = { ...newElement, x, y } const hasOverlap = existingElements.some(existing => checkElementOverlap(testPosition, existing) ) if (!hasOverlap) { return testPosition } } if (attempts > maxAttempts) break } // Если не нашли свободное место, размещаем в правом нижнем углу return { ...newElement, x: 50, y: existingElements.length * 100 + 50, } } // Валидация адреса ячейки export function validateCellAddress(cell: string): boolean { return /^[A-Z]+\d+$/.test(cell) } // Парсинг адреса ячейки в компоненты export function parseCellAddress( cell: string ): { column: string; row: number } | null { const match = cell.match(/^([A-Z]+)(\d+)$/) if (!match) return null return { column: match[1], row: parseInt(match[2], 10), } } // Генерация уникального ID для элемента export function generateElementId(): string { return `element_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` }