{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toast",
  "type": "registry:ui",
  "title": "Toast",
  "description": "Minimal toast provider and useToast() hook.",
  "dependencies": [
    "radix-ui",
    "lucide-react"
  ],
  "registryDependencies": [
    "@wibo/utils",
    "@wibo/button"
  ],
  "files": [
    {
      "path": "src/components/toast.tsx",
      "type": "registry:ui",
      "content": "import {\n  createContext,\n  useCallback,\n  useContext,\n  useMemo,\n  useRef,\n  useState,\n  type ReactNode,\n} from \"react\";\nimport { Toast as ToastPrimitive } from \"radix-ui\";\nimport { CircleAlert, CircleCheck, Info, TriangleAlert, X } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\n\ntype ToastVariant = \"default\" | \"success\" | \"info\" | \"warning\" | \"danger\";\n\ninterface ToastOptions {\n  title: ReactNode;\n  description?: ReactNode;\n  variant?: ToastVariant;\n  /** Milliseconds before auto-dismiss. Defaults to the provider duration. */\n  duration?: number;\n  /** One optional action. `altText` tells screen-reader users how to reach it otherwise. */\n  action?: { label: string; onClick: () => void; altText?: string };\n}\n\ninterface ToastApi {\n  /** Show a toast; returns its id. */\n  toast: (options: ToastOptions) => string;\n  dismiss: (id: string) => void;\n}\n\ninterface ToastRecord extends ToastOptions {\n  id: string;\n  open: boolean;\n}\n\nconst ToastContext = createContext<ToastApi | null>(null);\n\nconst variantIcon = {\n  success: CircleCheck,\n  info: Info,\n  warning: TriangleAlert,\n  danger: CircleAlert,\n} as const;\n\nconst variantIconClass = {\n  success: \"text-success\",\n  info: \"text-info\",\n  warning: \"text-warning\",\n  danger: \"text-danger\",\n} as const;\n\ninterface ToastProviderProps {\n  children: ReactNode;\n  /** Default auto-dismiss in milliseconds. */\n  duration?: number;\n  /** Accessible name of the notification region. */\n  label?: string;\n  closeLabel?: string;\n}\n\n/** Mount once near the root; then call `useToast().toast(...)` anywhere below. */\nfunction ToastProvider({\n  children,\n  duration = 5000,\n  label = \"Notifications\",\n  closeLabel = \"Dismiss\",\n}: ToastProviderProps) {\n  const [toasts, setToasts] = useState<ToastRecord[]>([]);\n  const counter = useRef(0);\n\n  const dismiss = useCallback((id: string) => {\n    setToasts((current) => current.map((t) => (t.id === id ? { ...t, open: false } : t)));\n    // Remove after the exit animation.\n    setTimeout(() => setToasts((current) => current.filter((t) => t.id !== id)), 250);\n  }, []);\n\n  const toast = useCallback((options: ToastOptions) => {\n    counter.current += 1;\n    const id = `toast-${counter.current}`;\n    setToasts((current) => [...current, { ...options, id, open: true }]);\n    return id;\n  }, []);\n\n  const api = useMemo(() => ({ toast, dismiss }), [toast, dismiss]);\n\n  return (\n    <ToastContext.Provider value={api}>\n      <ToastPrimitive.Provider duration={duration} label={label} swipeDirection=\"right\">\n        {children}\n        {toasts.map((t) => {\n          const variant = t.variant ?? \"default\";\n          const Icon = variant === \"default\" ? null : variantIcon[variant];\n          return (\n            <ToastPrimitive.Root\n              key={t.id}\n              open={t.open}\n              onOpenChange={(open) => {\n                if (!open) dismiss(t.id);\n              }}\n              duration={t.duration}\n              type={variant === \"danger\" ? \"foreground\" : \"background\"}\n              data-variant={variant}\n              className={cn(\n                \"group pointer-events-auto relative flex w-full items-start gap-3 overflow-hidden rounded-lg border border-border bg-popover p-4 pr-10 text-popover-foreground shadow-overlay\",\n                \"data-[state=open]:animate-toast-in data-[state=closed]:animate-toast-out\",\n                \"data-[swipe=move]:translate-x-(--radix-toast-swipe-move-x) data-[swipe=cancel]:translate-x-0 data-[swipe=end]:animate-slide-out-right\",\n              )}\n            >\n              {Icon ? (\n                <Icon\n                  aria-hidden=\"true\"\n                  className={cn(\"mt-0.5 size-4 shrink-0\", variantIconClass[variant as Exclude<ToastVariant, \"default\">])}\n                />\n              ) : null}\n              <div className=\"grid min-w-0 flex-1 gap-1\">\n                <ToastPrimitive.Title className=\"text-sm font-medium\">{t.title}</ToastPrimitive.Title>\n                {t.description ? (\n                  <ToastPrimitive.Description className=\"text-sm text-muted-foreground\">\n                    {t.description}\n                  </ToastPrimitive.Description>\n                ) : null}\n                {t.action ? (\n                  <ToastPrimitive.Action\n                    altText={t.action.altText ?? t.action.label}\n                    asChild\n                    onClick={t.action.onClick}\n                  >\n                    <Button variant=\"outline\" size=\"sm\" className=\"mt-1 w-fit\">\n                      {t.action.label}\n                    </Button>\n                  </ToastPrimitive.Action>\n                ) : null}\n              </div>\n              <ToastPrimitive.Close\n                aria-label={closeLabel}\n                className=\"press-icon absolute top-3 right-3 inline-flex size-6 items-center justify-center rounded-md text-muted-foreground outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50\"\n              >\n                <X aria-hidden=\"true\" className=\"size-4\" />\n              </ToastPrimitive.Close>\n            </ToastPrimitive.Root>\n          );\n        })}\n        {/* Radix names the region \"Notifications ({hotkey})\" by default: the app's word instead, the shortcut kept. */}\n        <ToastPrimitive.Viewport label={`${label} ({hotkey})`} className=\"fixed right-0 bottom-0 z-[100] flex max-h-screen w-full flex-col gap-2 p-4 outline-none sm:max-w-sm\" />\n      </ToastPrimitive.Provider>\n    </ToastContext.Provider>\n  );\n}\n\n/** Show and dismiss toasts. Must be called under `ToastProvider`. */\nfunction useToast(): ToastApi {\n  const api = useContext(ToastContext);\n  if (!api) throw new Error(\"useToast must be used inside <ToastProvider>.\");\n  return api;\n}\n\nexport { ToastProvider, useToast };\nexport type { ToastApi, ToastOptions, ToastProviderProps, ToastVariant };\n"
    }
  ]
}
