Corp/docs/guides/frontend/04-PATTERNS.md

9.2 KiB
Raw Permalink Blame History

Frontend Guide: Patterns

Формы

Общие правила

Для базовых form elements использовать:

  • app-input
  • app-select
  • shared select-компоненты из frontend/src/components/shared/SearchableSelect.tsx

Не допускается:

  • смешивать на одном экране несколько разных focus/hover contracts без причины
  • строить поле через случайные border/background utility-классы, если это уже покрыто foundation layer

Формы оцениваются по UX-паттерну

Формы нельзя насильно привести к одному layout-паттерну. Они должны оцениваться по типу:

  • CRUD form
  • compose form
  • filter form
  • settings form
  • dialog action form

Следствие:

  • карточки можно нормализовать массово
  • формы требуют ручного review по сценарию использования

Для filter form дополнительно:

  • toolbar и фильтрующий блок лучше держать отдельным feature-компонентом, если page-level файл уже управляет списком, detail modal и compose flow одновременно
  • если рядом уже есть удачный плотный list shell, сначала сохранять его внешний контракт и выносить логику/leaf-блоки без агрессивного редизайна

Dropdown-like UX

Если поле:

  • требует поиска
  • поддерживает multi-select
  • отображает выбранные элементы как chips
  • ведёт себя как адресная строка compose-интерфейса

то приоритет у shared select-компонентов, а не у локально стилизованного native select.

Причина:

  • dropdown affordance должен быть единым по приложению
  • локальные стрелки, паддинги и раскрытия быстро создают вторую дизайн-систему
  • filter-toolbar с сортировкой и раскрытием фильтров тоже не должен бесконечно разрастаться внутри page-level JSX

Person chips

Если secondary/detail-блок показывает конкретных людей:

  • получателей
  • участников
  • согласующих
  • исполнителей
  • авторов

то приоритет у person-chip паттерна: avatar + name внутри capsule, а не у сырого текста или локально собранного badge.

Причина:

  • человек считывается быстрее по аватару и имени, чем по строке текста
  • однотипные participant-блоки не должны выглядеть по-разному в соседних feature-модулях
  • это снижает визуальный дрейф между requests, equipment, procurement, documents, calendar

Референс по духу:

  • frontend/src/components/requests/RequestUserBadge.tsx

Object detail screens

Если один detail-route обслуживает и обычный просмотр, и управление объектом:

  • базовый режим должен быть showcase-first, а не выглядеть как обрубленная админка
  • management controls встраиваются в ту же anatomy, а не создают второй отдельный layout
  • главный акцент у сильного header и одного основного рабочего блока
  • secondary-настройки должны быть заметно спокойнее главного контента

Референс по духу:

  • frontend/src/app/departments/[id]/page.tsx

Compose forms

Compose-паттерн должен восприниматься как рабочее пространство, а не как список независимых инпутов.

Особенно это важно для:

  • заявлений
  • сообщений
  • писем
  • согласований

Референс по духу:

  • frontend/src/components/requests/RequestComposeModal.tsx

Базовый контракт:

  • адресные строки сверху
  • основное содержание формы в центре
  • служебные поля ниже и визуально спокойнее
  • действия формы собраны в одном предсказуемом footer
  • page-level файл держит orchestration, а длинный compose workspace лучше выносить в feature component

Модалки

Базовый shell

Для обычных модалок использовать:

  • frontend/src/components/ui/Modal.tsx

Новый modal pattern нельзя создавать без отдельной причины.

Геометрия модалок

Target state:

  • modal shell: rounded-2xl

Текущее переходное состояние:

  • mobile может быть компактнее по геометрии, но без отдельного visual contract

Responsive-логика

Разница между mobile и desktop должна достигаться через:

  • responsive spacing
  • responsive sizing
  • изменение плотности
  • sticky header/footer при необходимости

Не должно быть:

  • отдельной mobile-версии той же модалки
  • дублирования структуры ради платформы

App Shell

frontend/src/components/AppShell.tsx задаёт верхнеуровневый контракт приложения:

  • shell background
  • header
  • global search
  • mobile drawers
  • навигацию

Следствия:

  • feature-страницы не должны переизобретать глобальный shell
  • глобальные системные элементы должны оставаться в одном контракте
  • capsule-геометрия допустима на уровне shell controls, например у глобального поиска

Target-pattern registry

Элемент Рекомендуемый primitive / радиус Допустимые исключения Статус
Основная секция страницы app-surface rounded-2xl hero/media pattern по отдельному решению ok
Внутренний grouped block app-surface-muted rounded-xl rounded-lg, если блок очень компактный needs refactor
Modal shell app-surface-elevated rounded-2xl переходное mobile state допустимо временно ok
Обычный input / textarea / select app-input / app-select + rounded-lg нет ok
Searchable dropdown trigger shared SearchableSelect* + rounded-lg compose inline-row может быть крупнее по shell ok
Compose address row shared select pattern на базе app-input rounded-2xl допустим для compose-shell строки ok
Inline comment composer shared CommentControls + icon-only send action feed/footer variant может добавлять attachment actions перед input ok
Person chip / participant capsule local feature wrapper вокруг avatar + name capsule простой текст допустим только если аватар/ссылка не нужны и список не является заметной частью блока ok
Helper / alert / selected block app-selected, app-feedback-*, app-surface-muted + rounded-lg/rounded-xl зависит от веса блока needs refactor
Pills / status chips / avatar rounded-full нет ok
rounded-md у новых карточек и полей не использовать только legacy до отдельной переработки legacy
rounded-[...] без обоснования не использовать только согласованные special cases legacy

Do / Don’t

Делать

  • использовать globals.css как foundation layer
  • опираться на app-surface, app-input, app-select, app-action-*
  • выбирать радиус из базовой шкалы
  • расширять существующий shared primitive, если паттерн уже есть

Не делать

  • не вводить новый радиус “под один экран”
  • не стилизовать native select локальными хаками, если нужен shared dropdown pattern
  • не смешивать old-gray-contract и app-* contract в одном новом блоке
  • не дублировать shared interaction pattern в page-level JSX