{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "toast", "title": "Toast", "description": "Toast component with success/info/alert/generic variants and optional CTA.", "dependencies": [ "@phosphor-icons/react" ], "files": [ { "path": "registry/components/toast.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { CheckCircle, Warning, WarningCircle, X } from \"@phosphor-icons/react\";\nimport { Button } from \"@/registry/components/masai-button\";\nimport { cn } from \"@/lib/utils\";\n\ntype ToastType = \"success\" | \"info\" | \"alert\" | \"generic\";\ntype ToastDirection =\n | \"top-right\"\n | \"top-left\"\n | \"bottom-right\"\n | \"bottom-left\"\n | \"top-center\"\n | \"bottom-center\";\n\nconst toastTypeStyles: Record = {\n success: \"bg-white\",\n info: \"bg-white\",\n alert: \"bg-white\",\n generic: \"bg-white\",\n};\n\nconst toastIconColor: Record, string> = {\n success: \"text-green-600\",\n info: \"text-blue-600\",\n alert: \"text-orange-600\",\n};\n\nconst toastTextColorVar: Record = {\n success: \"var(--color-green-500)\",\n info: \"var(--color-blue-500)\",\n alert: \"var(--color-red-500)\",\n generic: \"var(--color-gray-600)\",\n};\n\nconst toastDirectionClasses: Record = {\n \"top-right\": \"fixed right-4 top-4 z-50\",\n \"top-left\": \"fixed left-4 top-4 z-50\",\n \"bottom-right\": \"fixed bottom-4 right-4 z-50\",\n \"bottom-left\": \"fixed bottom-4 left-4 z-50\",\n \"top-center\": \"fixed left-1/2 top-4 z-50 -translate-x-1/2\",\n \"bottom-center\": \"fixed bottom-4 left-1/2 z-50 -translate-x-1/2\",\n};\n\nexport type ToastProps = {\n type?: ToastType;\n direction?: ToastDirection;\n title: React.ReactNode;\n ctaText?: string;\n onClick?: () => void;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n durationMs?: number;\n className?: string;\n};\n\nexport function Toast({\n type = \"generic\",\n direction,\n title,\n ctaText,\n onClick,\n open,\n onOpenChange,\n durationMs = 2000,\n className,\n}: ToastProps) {\n const [internalOpen, setInternalOpen] = React.useState(true);\n const isControlled = open !== undefined;\n const isOpen = isControlled ? open : internalOpen;\n const showCta = Boolean(ctaText);\n const iconColorClass = type === \"generic\" ? null : toastIconColor[type];\n\n const closeToast = React.useCallback(() => {\n if (!isControlled) {\n setInternalOpen(false);\n }\n onOpenChange?.(false);\n }, [isControlled, onOpenChange]);\n\n React.useEffect(() => {\n if (!isOpen || durationMs <= 0) return;\n const timeoutId = window.setTimeout(() => {\n closeToast();\n }, durationMs);\n return () => window.clearTimeout(timeoutId);\n }, [closeToast, durationMs, isOpen, title, type, ctaText, direction]);\n\n React.useEffect(() => {\n if (!isControlled) {\n setInternalOpen(true);\n }\n }, [isControlled, title, type, ctaText, direction]);\n\n if (!isOpen) return null;\n\n return (\n \n {type !== \"generic\" ? (\n \n {type === \"success\" ? : null}\n {type === \"info\" ? : null}\n {type === \"alert\" ? : null}\n \n ) : null}\n\n
\n
\n \n {title}\n

\n\n {showCta ? (\n \n ) : null}\n
\n\n \n \n \n
\n \n );\n}\n", "type": "registry:ui" } ], "type": "registry:ui" }