переименования папок

This commit is contained in:
2025-07-21 15:02:10 +03:00
parent d0c79bd334
commit 84f0c9c6aa
72 changed files with 245 additions and 249 deletions

View File

@@ -1,42 +1,48 @@
import { ReactNode } from "react";
import { CellTarget, ElementType } from "../types/template";
import { ReactNode } from 'react'
import { CellTarget, ElementType } from '../type/template'
export interface ElementDefinition<Config = any, Value = any> {
/* служебные */
type: ElementType; // "text" | "select" | ...
label: string; // Человекочитаемое имя
icon: ReactNode;
type: ElementType // "text" | "select" | ...
label: string // Человекочитаемое имя
icon: ReactNode
/* React-компоненты */
Editor: React.FC<{ config: Config; onChange(c: Config): void }>;
Preview: React.FC<{ config: Config }>;
Render: React.FC<{ config: Config; value: Value; onChange?(v: Value): void }>;
Editor: React.FC<{ config: Config; onChange(c: Config): void }>
Preview: React.FC<{ config: Config }>
Render: React.FC<{ config: Config; value: Value; onChange?(v: Value): void }>
/* бизнес-логика */
defaultConfig: Config;
mapToCells?(config: Config): CellTarget[]; // куда пишем данные
defaultConfig: Config
mapToCells?(config: Config): CellTarget[] // куда пишем данные
}
export const elementRegistry: Record<ElementType, ElementDefinition> = {} as any;
export const elementRegistry: Record<ElementType, ElementDefinition> = {} as any
// Функция для регистрации элемента
export function registerElement<T extends ElementType>(
type: T,
definition: ElementDefinition
) {
elementRegistry[type] = definition;
elementRegistry[type] = definition
}
// Получить все доступные типы элементов
export function getAvailableElementTypes(): Array<{ type: ElementType; label: string; icon: ReactNode }> {
export function getAvailableElementTypes(): Array<{
type: ElementType
label: string
icon: ReactNode
}> {
return Object.values(elementRegistry).map(({ type, label, icon }) => ({
type,
label,
icon,
}));
}))
}
// Получить определение элемента по типу
export function getElementDefinition(type: ElementType): ElementDefinition | undefined {
return elementRegistry[type];
}
export function getElementDefinition(
type: ElementType
): ElementDefinition | undefined {
return elementRegistry[type]
}

View File

@@ -1,6 +1,11 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { CellTarget, ElementLayout, FormLayoutSettings, TemplateElement } from "../types/template";
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))
@@ -10,21 +15,21 @@ export function cn(...inputs: ClassValue[]) {
export function migrateTemplateElement(element: any): TemplateElement {
// Если элемент уже в новом формате
if (element.targetCells && Array.isArray(element.targetCells)) {
return element as TemplateElement;
return element as TemplateElement
}
// Миграция из старого формата
const targetCells: CellTarget[] = [];
const targetCells: CellTarget[] = []
if (element.targetCell) {
// Парсим старый формат targetCell (например "A1", "B5")
const cellMatch = element.targetCell.match(/^([A-Z]+)(\d+)$/);
const cellMatch = element.targetCell.match(/^([A-Z]+)(\d+)$/)
if (cellMatch) {
targetCells.push({
sheet: 'Report', // По умолчанию используем Report
cell: element.targetCell,
displayName: `Ячейка ${element.targetCell}`,
});
})
}
}
@@ -33,7 +38,7 @@ export function migrateTemplateElement(element: any): TemplateElement {
targetCells,
order: element.order || 0,
layout: element.layout || generateDefaultLayout(element.order || 0),
} as TemplateElement;
} as TemplateElement
}
// Получение настроек отображения по умолчанию
@@ -41,49 +46,49 @@ 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);
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[],
elements: TemplateElement[],
gridSize: number = 20
): TemplateElement[] {
const elementWidth = 300;
const elementHeight = 80;
const padding = 50;
const cols = 4; // Фиксированное количество колонок
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 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;
const snappedX = Math.round(x / gridSize) * gridSize
const snappedY = Math.round(y / gridSize) * gridSize
return {
...element,
layout: {
@@ -93,8 +98,8 @@ export function autoArrangeElements(
height: elementHeight,
zIndex: 1,
},
};
});
}
})
}
// Проверка пересечения элементов
@@ -108,7 +113,7 @@ export function checkElementOverlap(
element2.x + element2.width + threshold < element1.x ||
element1.y + element1.height + threshold < element2.y ||
element2.y + element2.height + threshold < element1.y
);
)
}
// Поиск свободного места для элемента (поиск в разумных пределах)
@@ -117,54 +122,56 @@ export function findFreePosition(
existingElements: ElementLayout[],
gridSize: number = 20
): ElementLayout {
const step = gridSize;
const maxWidth = 1600; // Максимальная ширина поиска
const maxHeight = 1200; // Максимальная высота поиска
const maxAttempts = 500;
let attempts = 0;
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 =>
attempts++
if (attempts > maxAttempts) break
const testPosition = { ...newElement, x, y }
const hasOverlap = existingElements.some(existing =>
checkElementOverlap(testPosition, existing)
);
)
if (!hasOverlap) {
return testPosition;
return testPosition
}
}
if (attempts > maxAttempts) break;
if (attempts > maxAttempts) break
}
// Если не нашли свободное место, размещаем в правом нижнем углу
return {
...newElement,
x: 50,
y: existingElements.length * 100 + 50
};
y: existingElements.length * 100 + 50,
}
}
// Валидация адреса ячейки
export function validateCellAddress(cell: string): boolean {
return /^[A-Z]+\d+$/.test(cell);
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;
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)}`;
}
return `element_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
}