Перейти к содержимому

Машины состояний и реакции

Форма может выглядеть законченной, но что происходит после отправки: ожидание, ошибка ввода, сохранение значений после сбоя или повторный запрос? Кликабельный прототип обычно показывает только один путь. Контракт реакций DFC Bridge описывает все важные ветви, включая обратную связь, фокус, историю и восстановление.

  • Реакция — это событие + текущее состояние + условие → эффекты + следующее состояние.
  • Состояние — стабильное условие элемента, формы, сценария или страницы.
  • Представление ([view=...]) — пример состояния страницы или данных: default, loading, empty, error.
  • Цель — стабильный элемент, состояние, модальное окно, форма, маршрут или сервисная операция, на которую ссылается реакция.

Не считайте экран с именем «успех» полным описанием успешной реакции. Нужны также отправка, обработка ответа, следующий фокус, объявление, повторное действие и результат для маршрута/истории.

Существующие теги обозначают опорные элементы:

email [field=email] [name=email]
submit [action=submit:lead-form]
lead-form [state=lead-form-idle]
success [state=lead-form-success]

Связи прототипа могут быть дополнительным свидетельством. Не добавляйте отдельный тег для каждого события, условия, таймаута, фокуса, объявления и кода ответа. Храните эти связи в структурированных метаданных DFC Bridge, ссылающихся на стабильные идентификаторы.

Реакция отвечает на все применимые вопросы:

Поле Смысл
id Стабильный идентификатор реакции.
scope Компонент, форма, страница, приложение или межстраничный сценарий.
from / event Текущее состояние и событие пользователя или системы.
guard Условие, разрешающее или отклоняющее переход.
effects Проверка, запрос, хранилище, аналитика, буфер обмена и другие эффекты.
to Успешное состояние и ветви ошибки, отмены и таймаута.
feedback Видимый статус и объявление для вспомогательных технологий.
focus Следующий фокус и правило восстановления.
history URL, query, hash, переход, replace/push и результат Back/Forward.
concurrency Повторный ввод, отмена, устаревший ответ и порядок применения.
persistence Что сохраняется после перезагрузки, перехода, закрытия или на другом устройстве.

Неполный фрагмент модуля. Здесь показан только bridge.interaction, а обязательные поля оболочки намеренно опущены. Перед обменом или проверкой полного контракта вставьте его в обязательную оболочку bridge из контракта переноса.

{
"bridge": {
"interaction": {
"stateMachines": [{
"id": "lead-form",
"initial": "idle",
"states": ["idle", "invalid", "submitting", "success", "failure"],
"transitions": [{
"id": "submit-lead",
"from": ["idle", "invalid", "failure"],
"event": { "type": "submit", "source": "lead-form" },
"guard": "all-required-fields-valid",
"pending": { "to": "submitting", "duplicateEvent": "ignore", "cancel": "on-route-leave" },
"outcomes": {
"success": { "to": "success", "focus": "success-heading", "announce": "Lead sent" },
"validation": { "to": "invalid", "focus": "first-invalid-field", "announce": "Form has errors" },
"failure": { "to": "failure", "focus": "error-summary", "retry": "preserve-values" }
},
"history": "none"
}]
}]
}
}
}

Читайте пример как сценарий: отправка из допустимого состояния, проверка условия, показ submitting, затем ветвь по результату ответа. После сбоя значения сохраняются для повтора; после успеха фокус переходит на заголовок результата. Идентификаторы предназначены адаптеру, а не для удлинения имён слоёв Figma.

Область Примеры Источник правды
Элемент фокус, нажатие, выбор, ошибка Нативная семантика или компонент UI Kit
Компонент раскрытый аккордеон, занятый диалог, месяц календаря Машина компонента и содержимое экземпляра
Форма не изменена, изменена, проверяется, отправляется, успех Машина реакций формы
Страница/данные загрузка, пусто, частично, ошибка Корневые [view] и контракт данных
Приложение выход из учётной записи, нет сети, изменились права Межстраничный контракт

Не создавайте корень страницы для каждого наведения. Не прячьте ошибку всей страницы в варианте кнопки. Родительская и дочерняя машины могут взаимодействовать, но владение и передача событий должны быть явными.

Записывайте смысловые события — activate, submit, change, dismiss, select, drag, timeout, response, — а не только жест конкретного устройства. Затем определите способы ввода:

  • действие мышью или касанием работает и с нативной клавиатурной активацией;
  • содержимое по наведению появляется при фокусе и может быть закрыто;
  • у перетаскивания есть альтернатива одним указателем, клавиатурой или элементами управления;
  • долгому нажатию, смахиванию и мультитачу соответствует заметная альтернатива;
  • повторное действие во время ожидания подчиняется явному правилу конкуренции.

Сверяйте модели взаимодействия с официальным WAI-ARIA Authoring Practices Guide, отдавая предпочтение нативной HTML-семантике.

Для каждого поля задайте:

  • имя данных, тип значения, обязательность и назначение автозаполнения, если применимо;
  • постоянную видимую метку, инструкцию, допустимый формат и ограничения;
  • обычное, сфокусированное, заполненное, ошибочное, недоступное и только для чтения состояния;
  • момент проверки: ввод, потеря фокуса, смена шага или отправка;
  • канонический код/текст ошибки и связь с полем;
  • нормализацию, не меняющую смысл незаметно;
  • зависимые поля, условную видимость и судьбу скрытого значения.

Для формы задайте:

  • полномочия клиентской и серверной проверки;
  • сводку ошибок, фокус первой ошибки и объявление;
  • ожидание ответа и доступные в этот момент действия;
  • сохранение значений при ошибке, окончании сессии, переходе и повторе;
  • результат успеха, повторную отправку и ожидание идемпотентности;
  • подтверждение сброса/отмены и предупреждение о несохранённых данных.

Подсказка внутри поля не заменяет метку. Недоступная кнопка не может объяснить причину своей недоступности: покажите статус или инструкцию рядом. «Только чтение» и «недоступно» — разные состояния.

Для запроса предусмотрите применимые ветви idle → pending → success | empty | partial | failure | cancelled | timed-out. Опишите:

  • сохраняются ли предыдущие данные и становятся ли они устаревшими;
  • прогресс и время до смены сообщения;
  • повтор, задержку, очередь без сети и ручное восстановление;
  • отмену при смене запроса, маршрута, окна или экземпляра;
  • гонки: последний ответ, строгий порядок, объединение или отклонение;
  • удаление дублей и идемпотентность повторного действия;
  • оптимистическое изменение, откат, конфликт и окно отмены;
  • объявление статуса без навязчивых повторов.

Спиннер не является контрактом асинхронности. Запоздалый ответ не должен перезаписать новый запрос или неожиданно переместить фокус.

Для диалога, панели, меню, всплывающего элемента и раскрытия задайте:

  • событие открытия и допустимые состояния;
  • начальный фокус или причину оставить его на триггере;
  • модальность;
  • область Tab/Shift+Tab и клавиши-стрелки, где они нужны;
  • Escape, кнопку закрытия, нажатие снаружи и закрытие при разрушительном действии;
  • возврат фокуса на триггер или заданный следующий элемент, если триггер исчез;
  • вложенные наложения и смену маршрута;
  • блокировку прокрутки с восстановлением позиции.

Визуально скрытое содержимое не должно случайно оставаться управляемым. Свёрнутое представление не должно терять обязательное состояние. Используйте подходящий шаблон APG как руководство реализации, но не как замену продуктовым решениям.

Полная реакция определяет фокус, когда:

  • проверка не прошла;
  • модальное окно открыто или закрыто;
  • содержимое добавлено, удалено, переставлено или отфильтровано;
  • шаг завершён;
  • сфокусированный элемент исчез;
  • маршрут изменён или история восстановлена;
  • операция завершилась ошибкой, таймаутом или отменой.

По умолчанию сохраняйте фокус, если работу можно продолжить на месте. Перемещайте его только ради следующей задачи или важной обратной связи. Не сбрасывайте фокус в начало документа из-за случайного повторного рендера.

Решите, можно ли поделиться состоянием, добавить его в закладки и восстановить. Для фильтров, вкладок, пагинации, выбранных записей, панелей и многошаговых сценариев задайте:

  • путь, query, hash, память или постоянное хранилище;
  • добавление или замену записи истории;
  • результат Back/Forward;
  • прямую загрузку и инициализацию после обновления;
  • недействительное или просроченное состояние;
  • восстановление прокрутки и фокуса;
  • отменяет ли закрытие наложения запись истории.

Глубокая ссылка должна создать то же продуктовое состояние без повторения предыдущих нажатий. URL не содержит секретов и чувствительных значений формы.

Перед передачей перечислите как минимум:

Путь Обязательное свидетельство
Успех Начало, промежуточная обратная связь, результат, фокус и история
Ошибочный ввод Ошибки поля и сводки, путь исправления
Пустой результат Объяснение и действие для продолжения
Нет прав или сессии Сохранение работы и путь входа/запроса доступа
Ошибка сети/сервера Возможность повтора или окончательный результат
Таймаут/нет сети Кэш, очередь и восстановление соединения
Отмена/закрытие Очистка побочных эффектов и возврат фокуса
Частый/повторный ввод Детерминированный результат конкуренции
Back/Forward/перезагрузка Восстановление состояния, фокуса и прокрутки

Перед передачей сценария проверьте следующее. Это ревью контракта реакций, а не автоматический статус «Готово» для страницы или приложения:

  • у каждого интерактивного элемента есть действие или настоящий адрес;
  • каждое действие разрешается в цель и полную запись реакции;
  • для всех достижимых состояний, включая ожидание и ошибку, есть макет или наследуемый контракт компонента;
  • поля имеют метки, ограничения, ошибки и правила сохранения значений;
  • решены гонки, повторы, отмена, повтор запроса и откат;
  • явно описаны фокус, объявления, URL, история и прокрутка;
  • одну задачу можно завершить с клавиатуры, указателя, сенсорного ввода и вспомогательных технологий.

Приближайте и листайте. Закрыть — Esc.