# Toast

Imperative notification service wrapping `MatSnackBar` with shadcn styling and a Sonner-style API surface.

## Import

```ts
import { ToastService } from '@edsis/component/toast';
```

## Application Setup

The toast overlay requires animations to be configured once at application bootstrap.

```ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';

bootstrapApplication(AppComponent, {
  providers: [provideAnimationsAsync()],
});
```

Use `provideNoopAnimations()` instead when the host application intentionally disables animations.

## Basic Usage

```ts
import { inject } from '@angular/core';
import { ToastService } from '@edsis/component/toast';

export class SaveButtonComponent {
  private readonly toast = inject(ToastService);

  save(): void {
    this.toast.success({
      title: 'Saved',
      description: 'Your changes have been saved.',
      action: 'Undo',
    });
  }

  fail(): void {
    this.toast.error({ title: 'Upload failed', description: 'Check your connection.' });
  }
}
```

## Common Patterns

### Variants

```ts
this.toast.show({ title: 'Draft created' });
this.toast.success({ title: 'Invoice paid' });
this.toast.info({ title: 'Deploy starts in 10 minutes' });
this.toast.warning({ title: 'Storage is almost full' });
this.toast.error({ title: 'Payment failed' });
```

### Position

```ts
this.toast.show({
  title: 'Event has been created',
  horizontalPosition: 'start',
  verticalPosition: 'top',
});
```

### Promise State

```ts
void this.toast.promise(() => this.createEvent(), {
  loading: 'Loading...',
  success: (event) => `${event.name} has been created`,
  error: 'Error',
});
```

The loading toast uses `durationMs: null` internally so it stays visible until the promise settles.

## API Reference

### `ToastService`

| Method                             | Returns                   |
| ---------------------------------- | ------------------------- |
| `show(options: ToastOptions)`      | `MatSnackBarRef<unknown>` |
| `success(options)`                 | `MatSnackBarRef<unknown>` |
| `info(options)`                    | `MatSnackBarRef<unknown>` |
| `warning(options)`                 | `MatSnackBarRef<unknown>` |
| `error(options)`                   | `MatSnackBarRef<unknown>` |
| `promise(taskOrFactory, messages)` | `Promise<T>`              |
| `dismiss()`                        | `void`                    |

### `ToastOptions`

| Field                | Type                                                             | Default     |
| -------------------- | ---------------------------------------------------------------- | ----------- |
| `title`              | `string`                                                         | —           |
| `description`        | `string`                                                         | —           |
| `action`             | `string`                                                         | `''`        |
| `variant`            | `'default' \| 'destructive' \| 'success' \| 'info' \| 'warning'` | `'default'` |
| `durationMs`         | `number \| null`                                                 | `5000`      |
| `horizontalPosition` | `MatSnackBarConfig['horizontalPosition']`                        | `'end'`     |
| `verticalPosition`   | `MatSnackBarConfig['verticalPosition']`                          | `'bottom'`  |

Set `durationMs` to `null` when the toast should stay visible until `dismiss()` is called.

### `ToastPromiseMessages<T>`

| Field     | Type                                     | Notes                                     |
| --------- | ---------------------------------------- | ----------------------------------------- |
| `loading` | `string`                                 | Title shown while the promise is pending. |
| `success` | `string \| ((value: T) => string)`       | Title shown when the promise resolves.    |
| `error`   | `string \| ((error: unknown) => string)` | Title shown when the promise rejects.     |

## Styling And Theming

The service applies `toast-panel` plus `toast-<variant>` panel classes to the toast overlay container.

- `default` keeps the background and foreground tokens from the active theme.
- `success` uses the primary theme tokens.
- `info` uses the shared `info` tokens.
- `warning` uses the shared `warning` tokens.
- `error` maps to the destructive tokens.

If you need to override a variant, target the container class and set the Material snack-bar CSS custom properties:

```css
.mat-mdc-snack-bar-container.toast-warning {
  --mdc-snackbar-container-color: hsl(var(--warning));
  --mdc-snackbar-supporting-text-color: hsl(var(--warning-foreground));
  --mat-snack-bar-button-color: hsl(var(--warning-foreground));
}
```

## Accessibility

`MatSnackBar` manages live-region announcements automatically. Keep titles short, use `action` for a single primary follow-up, and avoid moving focus into the toast.

## Angular Notes

Inject the service where the interaction starts instead of creating a global host component. `MatSnackBar` shows one visible toast at a time, so `promise()` dismisses the loading state before showing its success or error follow-up.

## Source Parity

This entrypoint is the local Angular mapping for shadcn Sonner as well as the simpler toast examples in the demo. It intentionally preserves the imperative API mental model, variant coverage, and placement controls while staying honest about the implementation boundary: the runtime surface is still a focused `MatSnackBar` wrapper, not Sonner's stacked multi-toast viewport.

# Toast

Imperative notification service backed by `MatSnackBar`, styled for the local shadcn-inspired theme.

The upstream shadcn toast page is deprecated in favor of Sonner. This entrypoint remains useful for lightweight single-message flows in Angular apps, while richer stacked notification workflows can move to a future Sonner-style surface.

## Import

```ts
import { ToastService } from '@edsis/component/toast';
```

## App setup

Enable browser or noop animations so the underlying snackbar overlay can render correctly.

```ts
import type { ApplicationConfig } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';

export const appConfig: ApplicationConfig = {
  providers: [provideAnimationsAsync()],
};
```

Use `provideNoopAnimations()` instead when the host application deliberately disables animations.

## Structure

This entrypoint exports a service rather than a component tree. `ToastService` keeps the API at the same event-handling layer as your save, delete, archive, or publish action, while rendering is delegated to CDK overlays.

## Basic usage

```ts
import { inject } from '@angular/core';
import { ToastService } from '@edsis/component/toast';

export class SaveButton {
  private readonly toast = inject(ToastService);

  save(): void {
    this.toast.success({
      title: 'Saved',
      description: 'Your changes have been saved.',
    });
  }
}
```

## Common patterns

### Status shortcuts

```ts
this.toast.show({
  title: 'Deployment queued',
  description: 'We will notify you when the build is ready.',
});

this.toast.success({
  title: 'Saved',
  description: 'Your changes have been saved.',
});

this.toast.info({
  title: 'Heads up',
  description: 'The deployment window starts in 10 minutes.',
});

this.toast.warning({
  title: 'Storage almost full',
  description: 'Archive older uploads before the next sync.',
});

this.toast.error({
  title: 'Failed to save',
  description: 'Network error. Please retry.',
});
```

### Persistent action

For an important action, keep the toast open and handle the snackbar action from the returned reference.

```ts
const ref = this.toast.show({
  title: 'Event archived',
  description: 'Restore it from the calendar within the next minute.',
  action: 'Undo',
  durationMs: null,
});

ref.onAction().subscribe(() => {
  this.toast.info({
    title: 'Archive cancelled',
    description: 'The event is back on the calendar.',
  });
});
```

### Promise helper

Use `toast.promise()` when one async task should produce loading, success, and failure feedback in sequence.

```ts
await this.toast.promise(
  () =>
    new Promise<{ name: string }>((resolve) => {
      window.setTimeout(() => resolve({ name: 'Summer launch checklist' }), 900);
    }),
  {
    loading: 'Archiving draft...',
    success: (draft) => `${draft.name} archived`,
    error: 'Unable to archive draft',
  },
);
```

## API Reference

### `ToastService`

| Method                             | Returns                   | Notes                                                      |
| ---------------------------------- | ------------------------- | ---------------------------------------------------------- |
| `show(options)`                    | `MatSnackBarRef<unknown>` | Lowest-level method with full option control.              |
| `success(options)`                 | `MatSnackBarRef<unknown>` | Shortcut for `variant: 'success'`.                         |
| `info(options)`                    | `MatSnackBarRef<unknown>` | Angular-local semantic shortcut.                           |
| `warning(options)`                 | `MatSnackBarRef<unknown>` | Angular-local semantic shortcut.                           |
| `error(options)`                   | `MatSnackBarRef<unknown>` | Shortcut for `variant: 'destructive'`.                     |
| `promise(taskOrFactory, messages)` | `Promise<T>`              | Shows loading first, then success or destructive feedback. |
| `dismiss()`                        | `void`                    | Closes the active toast.                                   |

### `ToastOptions`

| Field                | Type                                                             | Default     | Notes                                                   |
| -------------------- | ---------------------------------------------------------------- | ----------- | ------------------------------------------------------- |
| `title`              | `string`                                                         | unset       | Primary message line.                                   |
| `description`        | `string`                                                         | unset       | Optional secondary line.                                |
| `action`             | `string`                                                         | `''`        | Single action label shown by the snackbar.              |
| `variant`            | `'default' \| 'destructive' \| 'success' \| 'info' \| 'warning'` | `'default'` | Visual variant applied through `toast-*` panel classes. |
| `durationMs`         | `number \| null`                                                 | `5000`      | Set to `null` to keep the toast open until dismissed.   |
| `horizontalPosition` | `MatSnackBarConfig['horizontalPosition']`                        | `'end'`     | Snackbar x-axis placement.                              |
| `verticalPosition`   | `MatSnackBarConfig['verticalPosition']`                          | `'bottom'`  | Snackbar y-axis placement.                              |

### `ToastPromiseMessages<T>`

| Field     | Type                                     | Notes                                               |
| --------- | ---------------------------------------- | --------------------------------------------------- |
| `loading` | `string`                                 | Required loading message while the task is pending. |
| `success` | `string \| ((value: T) => string)`       | Static or data-derived success copy.                |
| `error`   | `string \| ((error: unknown) => string)` | Static or error-derived destructive copy.           |

## Styling and theming

The service applies `toast-panel` and `toast-<variant>` classes on the snackbar container. The shared Material integration maps those classes to the local semantic tokens for default, destructive, success, info, and warning states, and preserves line breaks between `title` and `description`.

## Accessibility

`MatSnackBar` manages live-region announcements automatically. Keep toast copy short, reserve it for transient status updates, and prefer `durationMs: null` when the action button must stay available long enough for keyboard and assistive-technology users.

## Keyboard interactions

The toast body itself is not a roving-focus widget. Keyboard interaction is limited to the optional action button exposed by the snackbar when `action` is present.

## Angular notes

This API is intentionally Angular-first rather than a one-to-one port of Radix toast composition. You trigger notifications from event handlers or async workflows, not by managing a dedicated toast viewport in every template.

## Source parity

Default and destructive map most closely to the historical shadcn toast examples. `success`, `info`, and `warning` are Angular-local semantic additions that fit the service-style API better. The upstream shadcn page now points users to Sonner, so this entrypoint deliberately focuses on simple, single-message feedback instead of stacked queue management.
