166 lines
6.2 KiB
Markdown
166 lines
6.2 KiB
Markdown
# Архитектура системы элементов
|
||
|
||
## Обзор
|
||
|
||
Система была переработана для обеспечения универсальности и расширяемости. Теперь все элементы регистрируются в едином реестре, что позволяет легко добавлять новые типы элементов без изменения основного кода.
|
||
|
||
## Структура
|
||
|
||
### 1. Реестр элементов (`src/lib/element-registry.ts`)
|
||
|
||
Центральный реестр, который содержит все определения элементов:
|
||
|
||
```typescript
|
||
export interface ElementDefinition<Config = any, Value = any> {
|
||
type: ElementType; // Уникальный тип элемента
|
||
label: string; // Человекочитаемое имя
|
||
icon: ReactNode; // Иконка для UI
|
||
|
||
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[]; // Маппинг на ячейки Excel
|
||
}
|
||
```
|
||
|
||
### 2. Базовые элементы (`src/components/BasicElements/`)
|
||
|
||
Стандартные элементы формы:
|
||
- `TextElement.tsx` - текстовое поле
|
||
- `SelectElement.tsx` - выпадающий список
|
||
- `NumberElement.tsx` - числовое поле
|
||
- `DateElement.tsx` - поле даты
|
||
- `TextareaElement.tsx` - многострочный текст
|
||
- `CheckboxElement.tsx` - чекбокс
|
||
- `RadioElement.tsx` - радиокнопки
|
||
|
||
### 3. Специальные элементы (`src/components/StandardsElement/`)
|
||
|
||
Сложные элементы с собственной логикой:
|
||
- `StandardsDialog.tsx` - диалог выбора эталонов
|
||
- `StandardsEditor.tsx` - редактор настроек
|
||
- `StandardsPreview.tsx` - превью элемента
|
||
- `definition.tsx` - определение элемента
|
||
|
||
## Как добавить новый элемент
|
||
|
||
### 1. Создать компоненты
|
||
|
||
```typescript
|
||
// MyElement/MyElementEditor.tsx
|
||
export const MyElementEditor: React.FC<{ config: MyConfig; onChange: (config: MyConfig) => void }> = ({ config, onChange }) => {
|
||
// Логика редактирования
|
||
};
|
||
|
||
// MyElement/MyElementPreview.tsx
|
||
export const MyElementPreview: React.FC<{ config: MyConfig }> = ({ config }) => {
|
||
// Логика превью
|
||
};
|
||
|
||
// MyElement/MyElementRender.tsx
|
||
export const MyElementRender: React.FC<{ config: MyConfig; value: string; onChange?: (value: string) => void }> = ({ config, value, onChange }) => {
|
||
// Логика рендеринга
|
||
};
|
||
```
|
||
|
||
### 2. Создать определение
|
||
|
||
```typescript
|
||
// MyElement/definition.tsx
|
||
export const myElementDefinition: ElementDefinition<MyConfig, string> = {
|
||
type: "my_element",
|
||
label: "Мой элемент",
|
||
icon: <MyIcon className="h-4 w-4" />,
|
||
defaultConfig: {
|
||
// конфигурация по умолчанию
|
||
},
|
||
Editor: MyElementEditor,
|
||
Preview: MyElementPreview,
|
||
Render: MyElementRender,
|
||
mapToCells: (config) => {
|
||
// логика маппинга на ячейки Excel
|
||
return [];
|
||
},
|
||
};
|
||
```
|
||
|
||
### 3. Зарегистрировать в реестре
|
||
|
||
```typescript
|
||
// src/lib/element-registry-init.ts
|
||
import { myElementDefinition } from "@/components/MyElement/definition";
|
||
|
||
export function initializeElementRegistry() {
|
||
// ... существующие элементы
|
||
registerElement("my_element", myElementDefinition);
|
||
}
|
||
```
|
||
|
||
### 4. Добавить тип в types/template.ts
|
||
|
||
```typescript
|
||
export type ElementType =
|
||
| 'text' | 'select' | 'radio' | 'checkbox' | 'number'
|
||
| 'textarea' | 'date' | 'standards'
|
||
| 'my_element'; // <-- новый тип
|
||
```
|
||
|
||
## Особенности элемента "Измерительные эталоны"
|
||
|
||
### Конфигурация
|
||
|
||
```typescript
|
||
interface StandardsConfig {
|
||
registryNumber: string; // ГРСИ
|
||
sheet: string; // Лист Excel
|
||
startCell: string; // Первая ячейка (например "A10")
|
||
maxItems: number; // Обычно 7
|
||
targetCells: CellTarget[];
|
||
}
|
||
```
|
||
|
||
### Логика маппинга
|
||
|
||
Элемент автоматически генерирует ячейки для записи выбранных эталонов:
|
||
|
||
```typescript
|
||
mapToCells: (cfg) => {
|
||
const res: CellTarget[] = [];
|
||
const parsed = parseCellAddress(cfg.startCell);
|
||
|
||
if (!parsed) return res;
|
||
|
||
const { column, row } = parsed;
|
||
|
||
// Генерируем ячейки вертикально (A1, A2, A3...)
|
||
for (let i = 0; i < cfg.maxItems; i++) {
|
||
res.push({
|
||
sheet: cfg.sheet,
|
||
cell: `${column}${row + i}`,
|
||
displayName: `Эталон ${i + 1}`,
|
||
});
|
||
}
|
||
|
||
return res;
|
||
}
|
||
```
|
||
|
||
### Ограничения
|
||
|
||
- Максимум 7 эталонов (настраивается)
|
||
- Автоматическое заполнение только выбранных позиций
|
||
- Оставшиеся ячейки остаются пустыми
|
||
|
||
## Преимущества новой архитектуры
|
||
|
||
1. **Расширяемость**: Добавление новых элементов требует минимальных изменений
|
||
2. **Универсальность**: Единый интерфейс для всех элементов
|
||
3. **Типобезопасность**: TypeScript обеспечивает корректность типов
|
||
4. **Модульность**: Каждый элемент самодостаточен
|
||
5. **Переиспользование**: Компоненты можно использовать в разных контекстах
|
||
|
||
## Миграция
|
||
|
||
Старые элементы продолжают работать через fallback-логику в `ElementConstructor.tsx`. Для полной миграции рекомендуется перевести все элементы на новую архитектуру. |