155 lines
8.5 KiB
Markdown
155 lines
8.5 KiB
Markdown
# Frontend Guide: Developer Workflow
|
||
|
||
Этот документ отвечает на вопрос: как practically разрабатывать новый UI в этом проекте.
|
||
|
||
## 1. Порядок работы над новой UI-задачей
|
||
|
||
Рекомендуемый порядок:
|
||
|
||
1. определить жанр экрана: operational workspace, profile-like page, dashboard/feed или auth/legal flow
|
||
2. понять, это новый паттерн или существующий
|
||
3. найти ближайшие shared primitives
|
||
4. определить уровень поверхности и геометрии
|
||
5. собрать layout из существующих слоёв
|
||
6. вынести тяжёлые feature-блоки из page-level файла, если экран уже растёт за счёт list row / detail modal / compose workspace
|
||
7. только потом добавлять локальные override
|
||
|
||
Неправильный порядок:
|
||
|
||
1. сразу писать JSX с локальными utility-классами
|
||
2. потом пытаться “подогнать под проект”
|
||
|
||
## 2. Как делать новый экран
|
||
|
||
Перед началом нового экрана:
|
||
|
||
- определить, это обычная app-page или отдельный auth/legal flow
|
||
- определить, это рабочий модуль, profile-like page или обзорный dashboard/feed
|
||
- понять, нужен ли `AppShell`
|
||
- определить основной primary panel
|
||
- определить, сколько secondary blocks реально нужно
|
||
|
||
Практический шаблон:
|
||
|
||
1. взять `AppShell`, если это обычная страница приложения
|
||
2. использовать `PageHeader`, если нужен стандартный page intro
|
||
3. построить крупные контейнеры как `app-surface rounded-2xl`
|
||
4. вложенные секции делать через `app-surface-muted rounded-xl`
|
||
5. поля формы строить на `app-input` / `app-select`
|
||
|
||
## 3. Как делать новую карточку
|
||
|
||
Перед созданием карточки спросить:
|
||
|
||
- это primary container или secondary grouped block?
|
||
- карточка реально нужна, или это просто внутренний helper block?
|
||
- не создаём ли мы третью глубину вложенности без необходимости?
|
||
|
||
Норма:
|
||
|
||
- primary card: `app-surface rounded-2xl`
|
||
- secondary card: `app-surface-muted rounded-xl`
|
||
- маленький helper: `rounded-lg` или `rounded-xl`, но не новая “мини-основная карточка”
|
||
|
||
## 4. Как делать новую форму
|
||
|
||
Сначала определить тип формы:
|
||
|
||
- CRUD
|
||
- compose
|
||
- filters
|
||
- settings
|
||
- dialog action
|
||
|
||
После этого:
|
||
|
||
- выбрать подходящий form contract
|
||
- использовать общие primitives
|
||
- решить, какие поля главные, а какие вторичные
|
||
- если compose-форма превращается в workspace, вынести её из page-level JSX в feature component
|
||
|
||
Важно:
|
||
|
||
- не все формы должны выглядеть одинаково
|
||
- но все формы должны жить в одной визуальной системе
|
||
- page-level файл не должен оставаться местом для длинной compose-модалки, если она уже стала отдельным рабочим сценарием
|
||
|
||
То же правило для list/detail flow:
|
||
|
||
- если у страницы появились тяжёлые expandable rows, detail modal или participant-heavy blocks, это уже кандидат на feature components, а не на дальнейшее разрастание page-level JSX
|
||
- то же касается toolbar/filter block и attachment preview, если они уже стали самостоятельными зонами сценария
|
||
- router-sync, deep-link opening и dismissable menu state тоже можно выносить в feature hook, если это screen orchestration, а не данные домена
|
||
- если внутри одной зоны уже несколько leaf-компонентов, стоит ввести feature container вроде `*Section` или `*Panel`, а не возвращать всё обратно в page-level JSX
|
||
- если экран уже визуально удачный, первый шаг рефакторинга должен сохранять внешний контракт и чистить структуру под капотом, а не навязывать новый layout только ради декомпозиции
|
||
- если у toolbar/filter block пропсы начинают расползаться, можно группировать их локально, но это не должно само по себе заставлять перепридумывать успешный внешний вид блока
|
||
- если экран ощущается как “из другого проекта”, причина чаще всего не в одном radius или цвете, а в несовпадении screen genre; это нужно решать на уровне hierarchy, density и panel semantics, а не точечными utility-правками
|
||
|
||
## 5. Как делать dropdown-поле
|
||
|
||
Порядок решения:
|
||
|
||
1. если это простой одиночный выбор без поиска, можно использовать `app-select`
|
||
2. если нужен поиск, chips, multi-select или compose-поведение, брать `SearchableSelect*`
|
||
3. если хочется “чуть-чуть допилить стрелочку”, сначала проверить, не означает ли это, что нужен shared pattern, а не local fix
|
||
|
||
Красный флаг:
|
||
|
||
- локальная стилизация native `select`, чтобы он выглядел как shared dropdown, почти всегда означает неправильный выбор primitive
|
||
|
||
## 6. Как делать модалку
|
||
|
||
Если нужен обычный dialog:
|
||
|
||
- использовать `Modal`
|
||
|
||
Перед реализацией определить:
|
||
|
||
- это modal dialog, drawer или workspace?
|
||
- нужен ли sticky footer?
|
||
- нужна ли длинная scrollable content area?
|
||
|
||
Нормальный modal workflow:
|
||
|
||
1. взять `Modal`
|
||
2. определить размер
|
||
3. собрать shell на `app-surface-elevated`
|
||
4. выстроить content hierarchy
|
||
5. проверить mobile-скролл и footer actions
|
||
|
||
## 7. Когда выносить в shared
|
||
|
||
Выносить в shared, если:
|
||
|
||
- паттерн повторится минимум ещё раз
|
||
- это не доменная логика, а UI-механика
|
||
- локальный код начинает превращаться в новый mini-framework внутри страницы
|
||
|
||
Не выносить рано, если:
|
||
|
||
- решение ещё сырое
|
||
- паттерн пока узкоспецифичный
|
||
- компонент завязан на один домен и вряд ли станет общим
|
||
|
||
## 8. Когда можно делать локальный override
|
||
|
||
Локальный override допустим, если:
|
||
|
||
- нет подходящего primitive
|
||
- решение реально локально
|
||
- не ломается общий visual contract
|
||
- override не рождает новый незафиксированный паттерн
|
||
|
||
Если override начинает повторяться, это уже shared problem, а не local exception.
|
||
|
||
## 9. Что проверять до завершения работы
|
||
|
||
Перед тем как считать UI-задачу завершённой, проверить:
|
||
|
||
- поверхность и радиусы соответствуют guide
|
||
- нет лишнего custom radius
|
||
- поля используют `app-*` primitives
|
||
- dropdown affordance не локально придуман
|
||
- mobile layout выглядит естественно
|
||
- действия формы и карточки читаются по визуальному весу
|
||
- role-based фронтовые эвристики не подменяют API contract там, где источник истины должен приходить с backend
|