# Toast

> Imperative notification API (`toast.success`, `toast.error`, `toast.promise`). Differentiates `role="alert"` from `role="status"`.

- Available in: `@sisyphos-ui/react`, `@sisyphos-ui/vue`, `@sisyphos-ui/angular`
- Docs: https://www.sisyphosui.com/docs/components/toast

## Installation

Pick the framework binding that matches your stack:

```bash
pnpm add @sisyphos-ui/react   # React 18+
pnpm add @sisyphos-ui/vue     # Vue 3+
pnpm add @sisyphos-ui/angular # Angular 17+
```

## Import

```tsx
import "@sisyphos-ui/react/styles.css";
import { Toast } from "@sisyphos-ui/react";
```

## Framework usage

### React 18+

```tsx
import { Toaster, toast, Button } from "@sisyphos-ui/react";

export const App = () => (
  <>
    <Toaster position="bottom-right" />
    <Button onClick={() => toast.success("Saved")}>Save</Button>
  </>
);
```

### Vue 3+

```vue
<script setup lang="ts">
import { Toaster, toast, Button } from "@sisyphos-ui/vue";
</script>

<template>
  <Toaster position="bottom-right" />
  <Button @click="toast.success('Saved')">Save</Button>
</template>
```

### Angular 17+

```ts
import { Component } from "@angular/core";
import { Toaster, Button, toast } from "@sisyphos-ui/angular";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [Toaster, Button],
  template: `
    <sui-toaster position="bottom-right" />
    <sui-button (buttonClick)="save()">Save</sui-button>
  `,
})
export class AppComponent {
  save() { toast.success("Saved"); }
}
```

## Examples

### Imperative API

`toast.success`, `.error`, `.warning`, `.info`. Mount `<Toaster />` once per app; fire toasts from anywhere.

```tsx
import { Button, Toaster, toast } from "@sisyphos-ui/react";

export function Example() {
  return (
    <>
      {/* Mount the <Toaster /> once — in your root layout or App shell. */}
      <Toaster position="bottom-right" />

      <div className="flex flex-wrap items-center gap-3">
        <Button onClick={() => toast.success("Saved successfully")}>Success</Button>
        <Button color="error"   onClick={() => toast.error("Something went wrong")}>Error</Button>
        <Button color="warning" onClick={() => toast.warning("Heads up — session expiring")}>Warning</Button>
        <Button color="info" variant="outlined" onClick={() => toast.info("New update available")}>Info</Button>
      </div>
    </>
  );
}
```

### Title + description

Second argument accepts `{ description, duration, dismissible, … }`.

```tsx
import { Button, Toaster, toast } from "@sisyphos-ui/react";

export function Example() {
  return (
    <>
      <Toaster position="bottom-right" />
      <Button
        onClick={() =>
          toast.success("Deployment finished", {
            description: "Build #4291 shipped to production in 1m 42s.",
          })
        }
      >
        Rich success toast
      </Button>
    </>
  );
}
```

### With undo action

Pass any ReactNode as `action`. Use `toast.dismiss(id)` to close from inside the action.

```tsx
import { Button, Toaster, toast } from "@sisyphos-ui/react";

export function Example() {
  return (
    <>
      <Toaster position="bottom-right" />
      <Button
        onClick={() => {
          let id = "";
          id = toast.success("Invitation sent", {
            description: "Ada will receive an email shortly.",
            duration: 8000,
            action: (
              <Button size="sm" variant="text" onClick={() => {
                toast.dismiss(id);
                toast.info("Invitation revoked");
              }}>
                Undo
              </Button>
            ),
          });
        }}
      >
        Send invite
      </Button>
    </>
  );
}
```

### Promise + loading

`toast.promise(p, { loading, success, error })` morphs a loading toast into success or error when the promise settles — same id, same slot, animated transition. `success` / `error` accept a function of the resolved value / thrown error.

```tsx
import { Button, toast } from "@sisyphos-ui/react";

export function Example() {
  return (
    <Button
      onClick={() => {
        const p = fetch("/api/invite", { method: "POST" }).then((r) => r.json());
        toast.promise(p, {
          loading: "Inviting teammate…",
          success: (user) => `${user.name} will receive an email shortly`,
          error: (err) => (err instanceof Error ? err.message : "Could not send invite"),
        });
      }}
    >
      Send invite
    </Button>
  );
}
```

### All positions

Pick any of the six anchor points. The `<Toaster />` itself drives layout.

```tsx
import { useState } from "react";
import { Button, Toaster, toast, type ToasterPosition } from "@sisyphos-ui/react";

const ALL: ToasterPosition[] = [
  "top-left", "top-center", "top-right",
  "bottom-left", "bottom-center", "bottom-right",
];

export function Example() {
  const [position, setPosition] = useState<ToasterPosition>("bottom-right");

  return (
    <>
      <Toaster position={position} />
      {ALL.map((p) => (
        <Button key={p} size="sm" onClick={() => setPosition(p)}>{p}</Button>
      ))}
      <Button onClick={() => toast.info(`Hello from ${position}`)}>Fire toast</Button>
    </>
  );
}
```

<!-- exports: { "Toast": "@sisyphos-ui/react" } -->