Тост — это немодальное сообщение поверх всех элементов на экране. Отличается от всплывающего окна тем, что выводится в правом верхнем углу экрана и не привязан ни к какому конкретному элементу интерфейса. Его используют, когда не требуется обязательная реакция пользователя (то есть не нужно модальное окно).

Рекомендованная длина сообщения — 144 символа, заголовка — 80 символов. Длинный текст не обрезается, тост увеличивается по высоте.

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

Успех. Сообщение о выполненной операции. Может содержать кнопку «Отмена». Полезно предложить пользователю просмотреть результат операции, если это возможно.

Предупреждение. Помогает пользователю избежать ошибки. Этот вид тостов используется редко.

Ошибка. Сообщает о неуспешной операции, когда ситуация не требует немедленного вмешательства пользователя, и можно продолжить работу.

Тост появляется в правом верхнем углу. Несколько сообщений образуют стопку на экране. Новые сообщения появляются внизу списка. Если тостов много, то новые уходят даже за нижнюю границу видимой области. Чаще всего тост открывается в качестве реакции на команду пользователя, но система может показывать уведомления и самостоятельно.

По умолчанию тост скрывается автоматически. Сообщение должно находиться в списке тостов минимум 5 секунд перед скрытием. Тосты пропадают с интервалом в 2 секунды после последнего скрытия, автоматического или ручного, чтобы пользователь успел прочитать текст в них, а исчезновение не воспринималось внезапным.

Тост скрывается автоматически. Сообщение должно показываться как минимум 5 секунд.

Сообщения не исчезают со времени. Пользователь может скрыть тост по кнопке «Закрыть».

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

По фокусу или ховеру на сообщении все тосты перестают пропадать. После ухода фокуса или потере ховера сообщения с автоматическим скрытием начинают пропадают друг за другом с интервалом от 2–5 секунд. Должно быть выполнено условие, чтобы сообщение было показано минимум 5 секунд и между закрытием тостов прошло 2 секунды.

Возможность скрыть тост по кнопке-иконке в углу доступна по умолчанию. Это действие не дублируется элементом в панели действий.

Иногда полезно сделать тост без кнопки-иконки «Закрыть». Например, когда надо показать ход фоновой операции. В этом случае следует расположить в панели действий кнопку с текстом «Отмена», чтобы прервать операцию.

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

В панели действий тоста можно расположить два элемента. Как правило, запуск действия закрывает тост.

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

Тост-уведомление может содержать кнопку отмены операции. Кнопка-иконка «Закрыть» не может служить для этого, она только скрывает тост.

В текст сообщения можно вставлять ссылку (рекомендация: не больше одной).

Пуш-сообщения Центра уведомлений следует показывать в виде тостов в едином списке с системными сообщениями.

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

Клавиша
Действие
Tab Shift+Tab Переход к следующему или предыдущему focusable-элементу
Esc Закрыть тост
  1. Инлайн-ссылка внутри текста тоста
  2. Кнопка «Закрыть»
  3. Действие 1
  4. Действие 2
  5. Инлайн-ссылка внутри текста следующего тоста
  6. Ссылка «Skip to content» (опционально)
  7. Первый элемент в Главном меню

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

Положение можно изменить, используя kbqToastConfigurationProvider:

import { kbqToastConfigurationProvider, KbqToastPosition } from '@koobiq/components/toast';

@NgModule({
    providers: [
        kbqToastConfigurationProvider({ position: KbqToastPosition.BOTTOM_RIGHT })
    ]
})

Ширина тоста фиксированная, а высота зависит от содержания.

Тост появляется слева по горизонтали. По мере сдвига новый элемент становится полностью непрозрачным. Высота панели тоста не меняется.

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

Тосты отображаются через CDK overlay, который по умолчанию добавляет свой контейнер в document.body. Когда приложение монтируется внутри Shadow DOM — типичный сценарий для микрофронтендов Module Federation, изолирующих свои стили, — тост выходит из shadow root в light DOM. Там он теряет стили и токены темы, ограниченные областью shadow root (токены темы Koobiq определены на предке .kbq-light / .kbq-dark), поэтому тост отображается без оформления.

Используйте kbqShadowDomOverlayProvider, чтобы направить все CDK overlay (тост, модальное окно, выпадающее меню, тултип и т. д.) внутрь shadow root:

import { bootstrapApplication } from '@angular/platform-browser';
import { kbqShadowDomOverlayProvider } from '@koobiq/components/core';

bootstrapApplication(AppComponent, {
    providers: [
        // Передайте корневой элемент микрофронтенда (или любой элемент внутри его shadow-дерева).
        ...kbqShadowDomOverlayProvider(() => document.querySelector('my-mfe-root')!)
    ]
});

При вызове без аргументов для поиска shadow root используется элемент корневого компонента приложения. Если хост не находится внутри открытого shadow root, контейнер остаётся в document.body, поэтому провайдер можно добавлять безусловно.

Провайдер заменяет глобальный OverlayContainer, поэтому его нельзя комбинировать с другим кастомным OverlayContainer (например, с FullscreenOverlayContainer из CDK) — побеждает последний провайдер.

Помимо перемещения контейнера, провайдер автоматически доставляет структурные стили overlay из CDK (позиционирование, z-index, подложку) в shadow root. Микрофронтенд по-прежнему отвечает за доставку стилей компонентов и темы Koobiq (токенов .kbq-light / .kbq-dark и CSS компонентов) в свой shadow root, поскольку глобальные таблицы стилей из document.head не пересекают границу shadow DOM.

По умолчанию каждый микрофронтенд, добавивший kbqShadowDomOverlayProvider, получает свой OverlayContainer, поэтому его overlay рендерятся в его собственном shadow root и теме. Это правильное поведение по умолчанию, когда каждый микрофронтенд должен сохранять свою тему.

Если же нужно, чтобы все микрофронтенды использовали один overlay-контейнер — единый stacking-контекст, поэтому overlay, открытые в разных микрофронтендах, накладываются в порядке открытия, — пусть host создаёт контейнер и передаёт этот единственный экземпляр каждому remote:

import { OverlayContainer } from '@angular/cdk/overlay';
import { createApplication } from '@angular/platform-browser';
import { kbqShadowDomOverlayProvider } from '@koobiq/components/core';

// Host: создаёт единый общий контейнер в своём shadow root.
const hostApp = await createApplication({
    providers: [...kbqShadowDomOverlayProvider(() => hostElement)]
});
const sharedOverlayContainer = hostApp.injector.get(OverlayContainer);

// Каждый remote-микрофронтенд переиспользует этот же экземпляр вместо создания своего.
const remoteApp = await createApplication({
    providers: [{ provide: OverlayContainer, useValue: sharedOverlayContainer }]
});

Компромиссы:

  • Все overlay (modal, sidepanel, select, dropdown, tooltip, toast, …) рендерятся в теме host, а не в собственной теме каждого remote.
  • Host должен жить дольше любого remote — он владеет элементом контейнера, и его ngOnDestroy удаляет этот элемент.
  • Так объединяется только контейнер overlay. Единый стек тостов дополнительно требует единого KbqToastService (каждый сервис ведёт свой стек), поэтому направляйте тосты через один общий сервис.
Предложения по улучшению
Если вы нашли ошибку или хотите доработать статью, создайте запрос на GitHub.