agentic-ui

Toast

Non-blocking notifications. Use the built-in ToastViewport or compose ToastRoot/Title/Description/Close with a raw base-ui Provider.

Toast types

import { useState } from "react";
import { Button, type ButtonTone } from "@brijbyte/agentic-ui/button";
import { Switch } from "@brijbyte/agentic-ui/switch";
import { ToastProvider, ToastViewport, useToastManager } from "@brijbyte/agentic-ui/toast";

import "@brijbyte/agentic-ui/button.css";
import "@brijbyte/agentic-ui/switch.css";
import "@brijbyte/agentic-ui/toast.css";

const TYPES = ["default", "success", "warning", "error", "info"] as const;

const MESSAGES: Record<string, { title: string; description: string }> = {
  default: {
    title: "Notification",
    description: "Something happened in your app.",
  },
  success: {
    title: "Deployed",
    description: "Your changes are live on production.",
  },
  warning: {
    title: "Approaching limit",
    description: "You've used 85% of your monthly quota.",
  },
  error: {
    title: "Deploy failed",
    description: "Build error in step 3/5. Check the logs.",
  },
  info: {
    title: "Update available",
    description: "v2.1.0 is ready. Run pnpm update.",
  },
};

const BUTTON_TONE_MAP: Record<(typeof TYPES)[number], ButtonTone> = {
  default: "primary",
  success: "success",
  warning: "warning",
  error: "destructive",
  info: "primary",
};

function ToastButtons() {
  const manager = useToastManager();

  const show = (type: string) => {
    const msg = MESSAGES[type] ?? MESSAGES["default"]!;
    manager.add({ title: msg.title, description: msg.description, type });
  };

  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: "var(--space-2)" }}>
      {TYPES.map((t) => {
        const buttonTone: ButtonTone = BUTTON_TONE_MAP[t] || "primary";
        return (
          <Button key={t} tone={buttonTone} variant="outline" size="sm" onClick={() => show(t)}>
            {t.charAt(0).toUpperCase() + t.slice(1)}
          </Button>
        );
      })}
    </div>
  );
}

export default function ToastDemo() {
  const [stacked, setStacked] = useState(false);
  const variant = stacked ? "stacked" : "list";

  return (
    <ToastProvider variant={variant}>
      <div
        style={{
          display: "flex",
          flexDirection: "column",
          gap: "var(--space-3)",
        }}
      >
        <ToastButtons />
        <Switch checked={stacked} onCheckedChange={setStacked}>
          Stacked
        </Switch>
      </div>
      <ToastViewport variant={variant} />
    </ToastProvider>
  );
}