Files
protoc-frontend/ARCHITECTURE.md

6.2 KiB
Raw Blame History

Архитектура системы элементов

Обзор

Система была переработана для обеспечения универсальности и расширяемости. Теперь все элементы регистрируются в едином реестре, что позволяет легко добавлять новые типы элементов без изменения основного кода.

Структура

1. Реестр элементов (src/lib/element-registry.ts)

Центральный реестр, который содержит все определения элементов:

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. Создать компоненты

// 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. Создать определение

// 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. Зарегистрировать в реестре

// src/lib/element-registry-init.ts
import { myElementDefinition } from "@/components/MyElement/definition";

export function initializeElementRegistry() {
  // ... существующие элементы
  registerElement("my_element", myElementDefinition);
}

4. Добавить тип в types/template.ts

export type ElementType =
  | 'text' | 'select' | 'radio' | 'checkbox' | 'number'
  | 'textarea' | 'date' | 'standards'
  | 'my_element'; // <-- новый тип

Особенности элемента "Измерительные эталоны"

Конфигурация

interface StandardsConfig {
  registryNumber: string;      // ГРСИ
  sheet: string;               // Лист Excel
  startCell: string;           // Первая ячейка (например "A10")
  maxItems: number;            // Обычно 7
  targetCells: CellTarget[];
}

Логика маппинга

Элемент автоматически генерирует ячейки для записи выбранных эталонов:

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. Для полной миграции рекомендуется перевести все элементы на новую архитектуру.