# Toast

- Entry point: `@koobiq/components/toast`
- NgModule: `KbqToastModule`
- Page: https://koobiq.io/en/components/toast/overview

## Overview

A toast is a non-modal message displayed on top of all elements on the screen. It differs from a popup in that it appears in the top right corner of the screen and is not tied to any specific interface element. It is used when no mandatory user response is required (i.e., a modal window is not needed).

**Example: Basic Toast** (`toast-overview`)

```ts toast-overview-example.ts
import { ChangeDetectionStrategy, Component, inject, TemplateRef } from '@angular/core';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqLink } from '@koobiq/components/link';
import { KbqToastService, KbqToastStyle } from '@koobiq/components/toast';

@Component({
    selector: 'toast-overview-example',
    imports: [
        KbqLink,
        KbqButtonModule
    ],
    template: `
        <ng-template #toastAction let-toast>
            <a kbqLink pseudo (click)="toast.close()">Обновить</a>
        </ng-template>

        <div class="demo-block">
            <button class="example-button" kbq-button (click)="showToast(toastAction)">Показать тост</button>
        </div>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastOverviewExample {
    readonly toastService = inject(KbqToastService);

    showToast(actions: TemplateRef<any>) {
        this.toastService.show({
            style: KbqToastStyle.Success,
            title: 'Доступно обновление агента',
            caption: 'Версия 2.03.15 от 15 мая 2022',
            actions,
            closeButton: true
        });
    }
}
```

### How it works

#### Message length

The recommended message length is 144 characters, and the title length is 80 characters. Long text is not truncated — the toast grows in height.

#### Toast types

**Information.** Any system message that does not require an immediate response, or for which there was no suitable place to embed in the on-screen content. For example, it can inform the user that a background operation has been launched by their command.

**Success.** A message about a completed operation. May include a "Cancel" button. It is useful to offer the user the option to view the result of the operation, if possible.

**Warning.** Helps the user avoid an error. This type of toast is used rarely.

**Error.** Reports an unsuccessful operation when the situation does not require immediate user intervention and work can continue.

**Example: Toast types** (`toast-types-overview`)

```ts toast-types-overview-example.ts
import { NgTemplateOutlet } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, TemplateRef } from '@angular/core';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqLink } from '@koobiq/components/link';
import { KbqToastService, KbqToastStyle } from '@koobiq/components/toast';

@Component({
    selector: 'toast-types-overview-example',
    imports: [
        KbqLink,
        NgTemplateOutlet,
        KbqButtonModule
    ],
    templateUrl: 'toast-types-overview-example.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastTypesOverviewExample {
    readonly toastService = inject(KbqToastService);

    showContrastToast(actions: TemplateRef<any>) {
        this.toastService.show({
            title: 'Доступно обновление агента',
            caption: 'Версия 2.03.15 от 15 мая 2022',
            actions
        });
    }

    showSuccessToast() {
        this.toastService.show({ style: KbqToastStyle.Success, caption: 'Удалено 13 записей из журнала событий' });
    }

    showWarningToast(actions: TemplateRef<any>) {
        this.toastService.show({ style: KbqToastStyle.Warning, caption: 'Ваш пароль истекает через 5 дней', actions });
    }

    showErrorToast(actions: TemplateRef<any>) {
        this.toastService.show({
            style: KbqToastStyle.Error,
            caption: 'Не удалось авторизовать 15 агентов',
            actions
        });
    }
}
```

```html toast-types-overview-example.html
<ng-template #action let-actionText="text" let-toast>
    <a kbqLink pseudo (click)="toast.close()">
        {{ actionText }}
    </a>
</ng-template>

<ng-template #toastActionInfo let-toast>
    <ng-container *ngTemplateOutlet="action; context: { $implicit: toast, text: 'Обновить' }" />
</ng-template>
<ng-template #toastActionWarning let-toast>
    <ng-container *ngTemplateOutlet="action; context: { $implicit: toast, text: 'Сменить пароль' }" />
</ng-template>
<ng-template #toastActionError let-toast>
    <ng-container *ngTemplateOutlet="action; context: { $implicit: toast, text: 'Повторить' }" />
</ng-template>

<div style="gap: 16px" class="layout-row">
    <button kbq-button (click)="showContrastToast(toastActionInfo)">Информация</button>
    <button kbq-button (click)="showSuccessToast()">Успех</button>
    <button kbq-button (click)="showWarningToast(toastActionWarning)">Предупреждение</button>
    <button kbq-button (click)="showErrorToast(toastActionError)">Ошибка</button>
</div>
```

#### Message appearance

Toasts appear in the top right corner. Multiple messages form a stack on the screen. New messages appear at the bottom of the list. If there are many toasts, new ones may extend beyond the bottom boundary of the visible area. Most often, a toast opens in response to a user command, but the system can also show notifications on its own.

#### Disappearance

By default, toasts are hidden automatically. A message must remain in the toast list for at least 5 seconds before being hidden. Toasts disappear with a 2-second interval after the last dismissal (automatic or manual), so the user has time to read the text and the disappearance does not feel sudden.

##### By timer (default behavior)

The toast is hidden automatically. The message must be shown for at least 5 seconds.

##### On command

Messages do not disappear over time. The user can hide a toast using the "Close" button.

At the designer's request, a toast may close via a button in the action panel, by following a link in the message text, or after any other event. For example, after a file upload or another process that the toast was reporting on is complete.

##### On hover or focus

When the mouse hovers over or focus is on a message, all toasts stop disappearing. After the mouse leaves or focus is lost, messages with auto-hide begin disappearing one after another with an interval of 2–5 seconds. The condition must be met that the message was shown for at least 5 seconds and 2 seconds have passed between toast closings.

#### Close button

The option to hide a toast using the icon button in the corner is available by default. This action is not duplicated by an element in the action panel.

Sometimes it is useful to make a toast without a close × icon button. For example, when you need to show the progress of a background operation. In this case, a "Cancel" button with text should be placed in the action panel to allow interrupting the operation.

The default close button is a native `<button>` whose accessible name comes from the locale, so it is reachable with Tab and activated with Enter or Space. A template passed through `closeButton` replaces it entirely — keep it a real button and give it an accessible name.

#### Actions

Do not use toasts [to confirm commands](https://koobiq.io/en/components/modal.md#simple-dialog-without-a-header).

The toast action panel can contain two elements. As a rule, triggering an action closes the toast.

If there are more actions, the primary one should be shown first, and the rest hidden in a "More" menu. The menu name may differ if another name is more appropriate.

**Example: Toast actions** (`toast-actions-overview`)

```ts toast-actions-overview-example.ts
import { ChangeDetectionStrategy, Component, inject, TemplateRef, viewChild } from '@angular/core';
import { outputToObservable } from '@angular/core/rxjs-interop';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqDropdown, KbqDropdownModule } from '@koobiq/components/dropdown';
import { KbqLink, KbqLinkSuffix } from '@koobiq/components/link';
import { KbqToastService, KbqToastStyle } from '@koobiq/components/toast';
import { take } from 'rxjs/operators';

@Component({
    selector: 'toast-actions-overview-example',
    imports: [
        KbqLink,
        KbqLinkSuffix,
        KbqDropdownModule,
        KbqButtonModule
    ],
    templateUrl: 'toast-actions-overview-example.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastActionsOverviewExample {
    readonly dropdown = viewChild.required<KbqDropdown>('dropdown');

    readonly toastService = inject(KbqToastService);

    showSingleActionToast(title: TemplateRef<any>, actions: TemplateRef<any>) {
        this.toastService.show({ style: KbqToastStyle.Success, title, actions });
    }

    showTwoActionToast(actions: TemplateRef<any>) {
        this.toastService.show({ title: 'Доступно обновление компонентов', actions });
    }

    showManyActionToast(actions: TemplateRef<any>) {
        const { ref } = this.toastService.show(
            {
                title: 'Заголовок',
                caption: 'Подзаголовок, подробности',
                actions
            },
            0
        );

        outputToObservable(this.dropdown().closed)
            .pipe(take(1))
            .subscribe(() => ref.instance.close());
    }
}
```

```html toast-actions-overview-example.html
<ng-template #toastSingleActonContentTemplate>
    Политика
    <span class="kbq-text-normal-strong">Почтовые серверы Windows</span>
    удалена
</ng-template>

<ng-template #toastSingleActonActionsTemplate let-toast>
    <a kbqLink pseudo (click)="toast.close()">Восстановить</a>
</ng-template>

<ng-template #toastTwoActonActionsTemplate let-toast>
    <a kbqLink pseudo class="layout-margin-right-m" (click)="toast.close()">Посмотреть</a>
    <a kbqLink pseudo (click)="toast.close()">Отклонить</a>
</ng-template>

<ng-template #toastManyActionActionsTemplate let-toast>
    <a kbqLink pseudo class="layout-margin-right-m" (click)="toast.close()">Действие 1</a>
    <a kbqLink pseudo class="layout-margin-right-m" (click)="toast.close()">Действие 2</a>
    <a kbqLink pseudo role="button" [kbqDropdownTriggerFor]="dropdown">
        Еще
        <i kbqLinkSuffix kbq-icon="kbq-chevron-down-s_16"></i>
    </a>
</ng-template>
<kbq-dropdown #dropdown="kbqDropdown">
    <button kbq-dropdown-item>Второстепенное действие 1</button>
    <button kbq-dropdown-item>Второстепенное действие 2</button>
    <button kbq-dropdown-item>Второстепенное действие 3</button>
    <button kbq-dropdown-item>Второстепенное действие 4</button>
</kbq-dropdown>

<div style="gap: 16px" class="layout-row">
    <button
        kbq-button
        (click)="showSingleActionToast(toastSingleActonContentTemplate, toastSingleActonActionsTemplate)"
    >
        Одно действие
    </button>
    <button kbq-button (click)="showTwoActionToast(toastTwoActonActionsTemplate)">Два действия</button>
    <button kbq-button (click)="showManyActionToast(toastManyActionActionsTemplate)">Много действий</button>
</div>
```

A toast notification may contain a button to cancel an operation. The close × icon button cannot serve this purpose — it only hides the toast.

**Example: Toast report** (`toast-report-overview`)

```ts toast-report-overview-example.ts
import { ChangeDetectionStrategy, Component, inject, TemplateRef } from '@angular/core';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqLink } from '@koobiq/components/link';
import { KbqToastService, KbqToastStyle } from '@koobiq/components/toast';

@Component({
    selector: 'toast-report-overview-example',
    imports: [
        KbqLink,
        KbqButtonModule
    ],
    template: `
        <ng-template #toastContentTemplate>
            Отчет&nbsp;
            <span class="kbq-text-normal-strong">Защита периметра</span>
            &nbsp;удален
        </ng-template>

        <ng-template #toastActionsTemplate let-toast>
            <a kbqLink pseudo (click)="toast.close()">Восстановить</a>
        </ng-template>

        <button kbq-button (click)="showToast(toastContentTemplate, toastActionsTemplate)">Отчет</button>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastReportOverviewExample {
    readonly toastService = inject(KbqToastService);

    showToast(caption: TemplateRef<any>, actions: TemplateRef<any>) {
        this.toastService.show({ style: KbqToastStyle.Success, caption, actions }, 0);
    }
}
```

#### Links in message text

A link can be inserted into the message text (recommendation: no more than one).

**Example: Toast link** (`toast-link-overview`)

```ts toast-link-overview-example.ts
import { ChangeDetectionStrategy, Component, inject, TemplateRef } from '@angular/core';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqLink } from '@koobiq/components/link';
import { KbqToastService } from '@koobiq/components/toast';

@Component({
    selector: 'toast-link-overview-example',
    imports: [
        KbqLink,
        KbqButtonModule
    ],
    templateUrl: 'toast-link-overview-example.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastLinkOverviewExample {
    readonly toastService = inject(KbqToastService);

    showToastWithInlineLink(caption: TemplateRef<any>, actions: TemplateRef<any>) {
        this.toastService.show({ caption, actions });
    }
}
```

```html toast-link-overview-example.html
<ng-template #toastWithInlineLinkTemplate let-toast>
    <p>
        Запрос
        <a kbqLink pseudo>REQ-20180101-02</a>
        назначен на вас
    </p>
</ng-template>

<ng-template #toastActionsTemplate let-toast>
    <a kbqLink pseudo (click)="toast.close()">Восстановить</a>
</ng-template>

<button kbq-button (click)="showToastWithInlineLink(toastWithInlineLinkTemplate, toastActionsTemplate)">
    Тост с гиперссылкой в заголовке
</button>
```

#### Notification center and toast messages

Push messages from the Notification Center should be shown as toasts in a unified list with system messages.

At the designer's request, some toast messages may be recorded in the Notification Center. When a toast closes, it is useful to be able to review what happened and read error details. At the same time, there is no requirement to log every Notification Center message.

### Focus and keyboard navigation

| Key           | Action                                         |
| ------------- | ---------------------------------------------- |
| Tab Shift+Tab | Move to the next or previous focusable element |
| Esc           | Close the toast                                |

#### Tab sequence

The sequence follows the reading order of the toast: the message first, its actions next, and the close button last.

1. Inline link inside the toast text
2. Action 1
3. Action 2
4. Close button
5. Inline link inside the next toast text
6. …
7. "Skip to content" link (optional)
8. First element in the [Main menu](https://koobiq.io/en/components/navbar.md)
9. …

#### Screen readers

A toast announces itself the moment it appears: the **error** and **warning** styles are inserted with `role="alert"` and interrupt the current announcement, the other styles use `role="status"` and wait for a pause. Both carry `aria-atomic="true"`, so the message is read as a whole. A toast shown through `showTemplate` gets `role="status"` on the first element of the template unless that element already declares a role of its own.

Auto-dismissal is paused by hovering and by focus only, and only for a toast shown through `show`: a template toast shown through `showTemplate` carries no such listeners and keeps counting down under the pointer. A screen reader user reading with a virtual cursor moves neither, so a toast that carries an action or reports an error should be shown with `duration: 0` and closed explicitly.

### Design and animation

#### Position on screen

The toast appears in the top right corner of the screen. Multiple messages form a stack on the screen; new toasts are added at the bottom of the stack.

The position can be changed using `kbqToastConfigurationProvider`:

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

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

#### Sizes

The width of the toast is fixed; the height depends on the content.

#### Animation

##### Appearance

The toast appears from the left horizontally. As it slides in, the new element becomes fully opaque. The height of the toast panel does not change.

##### Disappearance

The toast slides horizontally to the right beyond the screen edge. The toast window height does not change. As it slides, the element becomes transparent. Messages below it smoothly rise to fill the freed space in the stack.

Both animations are skipped when the operating system asks for reduced motion (`prefers-reduced-motion: reduce`); the toast then appears and disappears instantly.

### Module Federation and Shadow DOM

Toasts are rendered through the CDK overlay, which by default appends its container to `document.body`. When the application is mounted inside a **Shadow DOM** — a common setup for Module Federation micro-frontends that isolate their styles — the toast escapes the shadow root into the light DOM. There it loses the styles and theme tokens scoped to the shadow root (Koobiq theme tokens are defined on the `.kbq-light` / `.kbq-dark` ancestor), so the toast appears unstyled.

Use `kbqShadowDomOverlayProvider` to route all CDK overlays (toast, modal, dropdown, tooltip, etc.) into the shadow root:

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

bootstrapApplication(AppComponent, {
    providers: [
        // Pass the micro-frontend root element (or any element inside its shadow tree).
        ...kbqShadowDomOverlayProvider(() => document.querySelector('my-mfe-root')!)
    ]
});
```

When called without arguments, the application root component element is used to resolve the shadow root. If the host is not inside an **open** shadow root, the container stays on `document.body`, so the provider is safe to add unconditionally.

> The provider replaces the global `OverlayContainer`, so it cannot be combined with another custom `OverlayContainer` (such as CDK's `FullscreenOverlayContainer`) — the last provider wins.
>
> Besides relocating the container, the provider automatically delivers the CDK **structural** overlay styles (position, `z-index`, backdrop) into the shadow root. The micro-frontend is still responsible for delivering the **Koobiq component/theme styles** (the `.kbq-light` / `.kbq-dark` tokens and component CSS) into its shadow root, because global `document.head` stylesheets do not cross a shadow boundary.

#### One shared overlay container across micro-frontends

By default each micro-frontend that adds `kbqShadowDomOverlayProvider` gets **its own** `OverlayContainer`, so its overlays render inside **its own** shadow root and theme. That is the right default when every micro-frontend should keep its own theme.

If instead you want **all** micro-frontends to share a **single** overlay container — one stacking context, so overlays opened in different micro-frontends stack by open order — let the host create the container and pass that one instance to every remote:

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

// Host: create the single shared container in the host's shadow root.
const hostApp = await createApplication({
    providers: [...kbqShadowDomOverlayProvider(() => hostElement)]
});
const sharedOverlayContainer = hostApp.injector.get(OverlayContainer);

// Each remote micro-frontend reuses that same instance instead of creating its own.
const remoteApp = await createApplication({
    providers: [{ provide: OverlayContainer, useValue: sharedOverlayContainer }]
});
```

Trade-offs:

- All overlays (modal, sidepanel, select, dropdown, tooltip, toast, …) render in the **host's theme**, not each remote's own theme.
- The **host must outlive every remote** — it owns the container element, and its `ngOnDestroy` removes it.
- This unifies only the overlay **container**. A single toast **stack** additionally needs a single `KbqToastService` (each service keeps its own stack), so route toasts through one shared service.

## API

### Components

#### KbqToastComponent (component)

```ts
@Component({ selector: 'kbq-toast' })
class KbqToastComponent {
    readonly data: KbqToastData;
    readonly hovered: Signal<boolean>;
    readonly focused: Signal<boolean>;
    id: number;
    readonly $implicit: this;
    get isFocusedOrHovered(): boolean;
    close(): void;
}
```

- `hovered`: `Signal<boolean>` — Whether the pointer is over the toast.
- `focused`: `Signal<boolean>` — Whether the toast or an element inside it holds the focus.
- `$implicit`: `this` — Context of the templates a consumer passes through `KbqToastData`.

#### KbqToastContainerComponent (component)

```ts
@Component({ selector: 'kbq-toast-container' })
class KbqToastContainerComponent extends CdkScrollable {
    readonly service: KbqToastService<any>;
    readonly viewContainer: Signal<ViewContainerRef>;
    createToast<C>(
        data: KbqToastData,
        componentType: Type<C>,
        onTop: boolean
    ): ComponentRef<C>;
    createTemplate(
        data: KbqToastData,
        template: TemplateRef<any>,
        onTop: boolean
    ): EmbeddedViewRef<KbqToastTemplateContext>;
    remove(viewRef: ViewRef): void;
    getInjector(data: KbqToastData): Injector;
    elementScrolled(): Observable<Event>;
    getElementRef(): ElementRef<HTMLElement>;
    scrollTo(options: ExtendedScrollToOptions): void;
    measureScrollOffset(
        from: "top" | "right" | "left" | "bottom" | "start" | "end"
    ): number;
}
```

- `elementScrolled()`: `Observable<Event>` — Returns observable that emits when a scroll event is fired on the host element.
- `getElementRef()`: `ElementRef<HTMLElement>` — Gets the ElementRef for the viewport.
- `scrollTo()`: `void` — Scrolls to the specified offsets. This is a normalized version of the browser's native scrollTo
  method, since browsers are not consistent about what scrollLeft means in RTL. For this method
  left and right always refer to the left and right side of the scrolling container irrespective
  of the layout direction. start and end refer to left and right in an LTR context and vice-versa
  in an RTL context.
  - `options`: `ExtendedScrollToOptions` — specified the offsets to scroll to.
- `measureScrollOffset()`: `number` — Measures the scroll offset relative to the specified edge of the viewport. This method can be
  used instead of directly checking scrollLeft or scrollTop, since browsers are not consistent
  about what scrollLeft means in RTL. The values returned by this method are normalized such that
  left and right always refer to the left and right side of the scrolling container irrespective
  of the layout direction. start and end refer to left and right in an LTR context and vice-versa
  in an RTL context.
  - `from`: `"top" | "right" | "left" | "bottom" | "start" | "end"` — The edge to measure from.

### Directives

#### KbqToastCloseButton (directive)

```ts
@Directive({ selector: '[kbq-toast-close-button]' })
class KbqToastCloseButton {}
```

### Services

#### KbqToastService (service)

Generic `T` is a type hint only; the runtime component comes from `KBQ_TOAST_FACTORY`.

```ts
@Injectable({ providedIn: 'root' })
class KbqToastService<T extends KbqToastComponent = KbqToastComponent> implements KbqToastStack {
    get toasts(): ComponentRef<T>[];
    get templates(): EmbeddedViewRef<KbqToastTemplateContext>[];
    readonly read: BehaviorSubject<KbqToastData | null>;
    readonly hovered: Signal<boolean>;
    readonly focused: Signal<boolean>;
    readonly timer: Observable<number>;
    show(
        data: KbqToastData,
        duration?: number,
        onTop?: boolean
    ): { ref: ComponentRef<T>; id: number };
    showTemplate(
        data: KbqToastData,
        template: TemplateRef<any>,
        duration?: number,
        onTop?: boolean
    ): { ref: EmbeddedViewRef<KbqToastTemplateContext>; id: number };
    hide(id: number): void;
    hideTemplate(id: number): void;
}
```

- `hovered`: `Signal<boolean>` — Whether at least one toast is hovered.
- `focused`: `Signal<boolean>` — Whether at least one toast holds the focus.
- `timer`: `Observable<number>` — Countdown heartbeat: emits every 500 ms while the stack holds something and is not paused by the pointer
  or the keyboard focus. No interval runs while the stack is empty.

### Classes

#### KbqToastData (class)

```ts
class KbqToastData {
    id?: string;
    title?: string | TemplateRef<any>;
    style?: KbqToastStyle | (string & {});
    icon?: TemplateRef<any> | boolean;
    iconClass?: string;
    caption?: string | TemplateRef<any>;
    content?: string | TemplateRef<any>;
    actions?: TemplateRef<any>;
    closeButton?: TemplateRef<any> | boolean;
}
```

- `style?`: `KbqToastStyle | (string & {})` — Visual style of the toast. Styles outside `KbqToastStyle` render without a default icon.

### Types

#### KbqToastConfig (interface)

```ts
interface KbqToastConfig {
    position: KbqToastPosition;
    duration: number;
    delay: number;
    onTop: boolean;
    indent: {
        vertical: number;
        horizontal: number;
    };
}
```

- `indent`: `{ vertical: number; horizontal: number; }` — Custom indentation for positioning the toast stack overlay when using `GlobalPositionStrategy`
- `indent.vertical`: `number` — Vertical spacing from the top or bottom of the screen.
- `indent.horizontal`: `number` — Horizontal spacing from the left or right of the screen.

#### KbqToastStack (interface)

Narrow contract that a toast consumes from the service owning its stack.

`KbqToastService` implements it and provides itself under `KBQ_TOAST_STACK` for every toast it creates, so
that a toast never has to depend on the concrete service that instantiates it.

```ts
interface KbqToastStack {
    readonly read: Subject<KbqToastData | null>;
    hide(id: number): void;
    setHovered(id: number, hovered: boolean): void;
    setFocused(id: number, origin: FocusOrigin): void;
}
```

- `read`: `Subject<KbqToastData | null>` — Emits the data of a toast once the user has read it.
- `hide()`: `void` — Removes the toast with the given id from the stack.
- `setHovered()`: `void` — Reports that the pointer entered or left the toast with the given id.
- `setFocused()`: `void` — Reports the origin the toast with the given id is focused with, or `null` once it loses focus.

#### KbqToastTemplateContext (type)

Context of a template rendered by `KbqToastService.showTemplate`.

Note that it differs from the context of the slot templates passed through `KbqToastData` (title, caption,
content, actions, icon and close button), whose `$implicit` is the `KbqToastComponent` instance.

```ts
type KbqToastTemplateContext = {
    $implicit: KbqToastData;
};
```

### Enums

#### KbqToastPosition (enum)

```ts
enum KbqToastPosition {
    TOP_RIGHT = 'top-right',
    TOP_LEFT = 'top-left',
    TOP_CENTER = 'top-center',
    BOTTOM_RIGHT = 'bottom-right',
    BOTTOM_LEFT = 'bottom-left',
    BOTTOM_CENTER = 'bottom-center',
    CENTER = 'center'
}
```

#### KbqToastStyle (enum)

```ts
enum KbqToastStyle {
    Contrast = 'contrast',
    Success = 'success',
    Warning = 'warning',
    Error = 'error'
}
```

### Constants

#### defaultToastConfig (const)

Frozen so that the fallback every application shares cannot be reconfigured from a single consumer.

```ts
const defaultToastConfig: KbqToastConfig;
```

#### KBQ_TOAST_CONFIG (const)

```ts
const KBQ_TOAST_CONFIG: InjectionToken<KbqToastConfig>;
```

#### KBQ_TOAST_FACTORY (const)

```ts
const KBQ_TOAST_FACTORY: InjectionToken<typeof KbqToastComponent>;
```

#### KBQ_TOAST_STACK (const)

Stack a toast belongs to. Provided by `KbqToastService` for every toast it creates.

```ts
const KBQ_TOAST_STACK: InjectionToken<KbqToastStack>;
```

#### kbqToastConfigurationProvider (const)

Utility provider for `KBQ_TOAST_CONFIG`.

```ts
const kbqToastConfigurationProvider: (
    configuration: Partial<KbqToastConfig>
) => Provider;
```

## Examples

### Disappearance

**Example: Toast hide** (`toast-hide-overview`)

```ts toast-hide-overview-example.ts
import { NgTemplateOutlet } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, TemplateRef } from '@angular/core';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqLink } from '@koobiq/components/link';
import { KbqProgressBarModule } from '@koobiq/components/progress-bar';
import { KbqToastService, KbqToastStyle } from '@koobiq/components/toast';
import { switchMap, timer } from 'rxjs';

@Component({
    selector: 'toast-hide-overview-example',
    imports: [
        KbqButtonModule,
        KbqLink,
        KbqProgressBarModule,
        NgTemplateOutlet
    ],
    templateUrl: 'toast-hide-overview-example.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastHideOverviewExample {
    progress: boolean;

    readonly toastService = inject(KbqToastService);

    showDefaultToast(actions: TemplateRef<any>) {
        this.toastService.show({
            title: 'Доступно обновление агента',
            caption: 'Версия 2.03.15 от 15 мая 2022',
            actions
        });
    }

    showPermanentToast(content: TemplateRef<any>, actions: TemplateRef<any>) {
        this.progress = true;
        const { ref } = this.toastService.show(
            {
                title: 'Импорт файлов',
                caption: '12,1 МБ из 85 МБ — осталось 15 мин  ',
                closeButton: false,
                content,
                actions
            },
            0
        );

        timer(2000)
            .pipe(
                switchMap(() => {
                    this.progress = false;

                    return timer(550);
                })
            )
            .subscribe(() => ref.instance.close());
    }

    showWarningToast(actions: TemplateRef<any>) {
        this.toastService.show({ style: KbqToastStyle.Warning, caption: 'Ваш пароль истекает через 5 дней', actions });
    }
}
```

```html toast-hide-overview-example.html
<ng-template #action let-actionText="text" let-toast>
    <a kbqLink pseudo (click)="toast.close()">
        {{ actionText }}
    </a>
</ng-template>

<ng-template #toastActionInfo let-toast>
    <ng-container [ngTemplateOutlet]="action" [ngTemplateOutletContext]="{ $implicit: toast, text: 'Обновить' }" />
</ng-template>
<ng-template #toastActionWarning let-toast>
    <ng-container
        [ngTemplateOutlet]="action"
        [ngTemplateOutletContext]="{ $implicit: toast, text: 'Сменить пароль' }"
    />
</ng-template>

<ng-template #toastPermanentContentTemplate>
    @if (progress) {
        <kbq-progress-bar class="layout-margin-top-m layout-margin-bottom-m" [mode]="'indeterminate'" />
    } @else {
        <div>
            <span>Импорт файлов завершен</span>
            <i kbq-icon="kbq-circle-check_16" class="kbq-success"></i>
        </div>
    }
</ng-template>

<ng-template #toastPermanentActionsTemplate let-toast>
    <a kbqLink pseudo (click)="toast.close()">Отмена</a>
</ng-template>

<div style="gap: 16px" class="layout-row">
    <button kbq-button (click)="showDefaultToast(toastActionInfo)">По таймеру</button>
    <button kbq-button (click)="showPermanentToast(toastPermanentContentTemplate, toastPermanentActionsTemplate)">
        По команде
    </button>
    <button kbq-button (click)="showWarningToast(toastActionWarning)">Ховер или фокус</button>
</div>
```

### The "Close" button

**Example: Toast progress bar** (`toast-progress-bar-overview`)

```ts toast-progress-bar-overview-example.ts
import { ChangeDetectionStrategy, Component, inject, TemplateRef } from '@angular/core';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqLink } from '@koobiq/components/link';
import { KbqProgressBarModule } from '@koobiq/components/progress-bar';
import { KbqToastService } from '@koobiq/components/toast';

@Component({
    selector: 'toast-progress-bar-overview-example',
    imports: [
        KbqProgressBarModule,
        KbqLink,
        KbqButtonModule
    ],
    template: `
        <ng-template #toastStickyContentTemplate let-toast>
            <kbq-progress-bar class="layout-margin-top-m layout-margin-bottom-m" [mode]="'indeterminate'" />
        </ng-template>

        <ng-template #toastStickyActionsTemplate let-toast>
            <a kbqLink pseudo (click)="toast.close()">Отмена</a>
        </ng-template>

        <button kbq-button (click)="showStickyToast(toastStickyContentTemplate, toastStickyActionsTemplate)">
            Тост с прогресс-баром, кнопкой Отмена и без крестика
        </button>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastProgressBarOverviewExample {
    readonly toastService = inject(KbqToastService);

    showStickyToast(content: TemplateRef<any>, actions: TemplateRef<any>) {
        this.toastService.show(
            {
                title: 'Импорт файлов',
                caption: '12,1 МБ из 85 МБ — осталось 15 мин  ',
                closeButton: false,
                content,
                actions
            },
            0
        );
    }
}
```

### Access to user data

**Example: Toast user data** (`toast-user-data`)

```ts toast-user-data-example.ts
import { ChangeDetectionStrategy, Component, inject, TemplateRef } from '@angular/core';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqLink } from '@koobiq/components/link';
import { KbqToastData, KbqToastService, KbqToastStyle } from '@koobiq/components/toast';

@Component({
    selector: 'toast-user-data-example',
    imports: [
        KbqLink,
        KbqButtonModule
    ],
    templateUrl: 'toast-user-data-example.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastUserDataExample {
    readonly toastService = inject(KbqToastService);

    showToast(actions: TemplateRef<any>) {
        interface ExtendedUserData extends KbqToastData {
            userData: string;
            userButtonName: string;
        }

        this.toastService.show(<ExtendedUserData>{
            style: KbqToastStyle.Success,
            title: 'Agent update available',
            caption: 'Version 2.03.15 from May 15, 2022',
            actions,
            closeButton: true,
            userData: 'AnyUserData',
            userButtonName: 'Update'
        });
    }
}
```

```html toast-user-data-example.html
<ng-template #toastAction let-toast>
    <a kbqLink pseudo (click)="toast.close()">
        {{ toast.data.userButtonName }}
    </a>
    <br />
    {{ toast.data.userData }}
</ng-template>

<div class="demo-block">
    <button kbq-button class="example-button" (click)="showToast(toastAction)">Show toast</button>
</div>
```

### Line breaks

**Example: toast-multiline** (`toast-multiline`)

```ts toast-multiline-example.ts
import { ChangeDetectionStrategy, Component, inject, TemplateRef } from '@angular/core';
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqLink } from '@koobiq/components/link';
import { KbqToastService, KbqToastStyle } from '@koobiq/components/toast';

@Component({
    selector: 'toast-multiline-example',
    imports: [
        KbqLink,
        KbqButtonModule
    ],
    template: `
        <ng-template #toastAction let-toast>
            <a kbqLink pseudo (click)="toast.close()">Обновить</a>
        </ng-template>

        <div class="demo-block">
            <button class="example-button" kbq-button (click)="showToast(toastAction)">Показать тост</button>
        </div>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToastMultilineExample {
    readonly toastService = inject(KbqToastService);

    showToast(actions: TemplateRef<any>) {
        this.toastService.show({
            style: KbqToastStyle.Success,
            title: 'Доступно \nобновление \nагента',
            caption: 'Версия: \n2.03.15 от 15 мая 2022',
            content:
                'Содержит: \n№1 - Очень важные изменения\n№2 - Менее важные изменения\n№3 - Совсем неважные изменения',
            actions,
            closeButton: true
        });
    }
}
```
