{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toast",
  "type": "registry:ui",
  "description": "Show temporary notifications with automatic dismissal and optional actions.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "components/ui/toast.tsx",
      "content": "\"use client\"\r\n\r\nimport * as React from \"react\"\r\nimport { Toast as BaseToast } from \"@base-ui/react/toast\"\r\nimport { cn } from \"@/lib/utils\"\r\n\r\ntype ToastPosition =\r\n    | \"top-left\"\r\n    | \"top-right\"\r\n    | \"top-center\"\r\n    | \"bottom-left\"\r\n    | \"bottom-right\"\r\n    | \"bottom-center\"\r\n\r\ninterface ToastOptions {\r\n    title?: React.ReactNode\r\n    description?: React.ReactNode\r\n    timeout?: number\r\n    priority?: \"low\" | \"high\"\r\n    onClose?: () => void\r\n    onRemove?: () => void\r\n    actionProps?: React.ComponentPropsWithoutRef<\"button\">\r\n}\r\n\r\ninterface ToastPromiseOptions<Value> {\r\n    loading: string | ToastOptions\r\n    success: string | ToastOptions | ((result: Value) => string | ToastOptions)\r\n    error: string | ToastOptions | ((error: unknown) => string | ToastOptions)\r\n}\r\n\r\ntype ToastProps = React.ComponentPropsWithoutRef<typeof BaseToast.Root>\r\n\r\ninterface ToasterProps {\r\n    position?: ToastPosition\r\n}\r\n\r\nconst VIEWPORT_POSITIONS: Record<ToastPosition, string> = {\r\n    \"top-left\": \"fixed top-4 left-4 z-50 w-full max-w-sm flex\",\r\n    \"top-right\": \"fixed top-4 right-4 z-50 w-full max-w-sm flex\",\r\n    \"top-center\": \"fixed top-4 left-1/2 -translate-x-1/2 z-50 w-full max-w-sm flex\",\r\n    \"bottom-left\": \"fixed bottom-4 left-4 z-50 w-full max-w-sm flex\",\r\n    \"bottom-right\": \"fixed bottom-4 right-4 z-50 w-full max-w-sm flex\",\r\n    \"bottom-center\": \"fixed bottom-4 left-1/2 -translate-x-1/2 z-50 w-full max-w-sm flex\",\r\n}\r\n\r\nfunction getSwipeDirection(\r\n    position: ToastPosition,\r\n): (\"up\" | \"down\" | \"left\" | \"right\")[] {\r\n    const isTop = position.startsWith(\"top\")\r\n    const isLeft = position.includes(\"left\")\r\n    const isCenter = position.includes(\"center\")\r\n    if (isCenter) return isTop ? [\"up\"] : [\"down\"]\r\n    if (isLeft) return isTop ? [\"up\", \"left\"] : [\"down\", \"left\"]\r\n    return isTop ? [\"up\", \"right\"] : [\"down\", \"right\"]\r\n}\r\n\r\nconst COMMON_TOAST_CLASSES = [\r\n    \"[--gap:0.75rem]\",\r\n    \"[--peek:0.75rem]\",\r\n    \"[--scale:calc(max(0,1-(var(--toast-index)*0.1)))]\",\r\n    \"[--shrink:calc(1-var(--scale))]\",\r\n    \"[--height:var(--toast-frontmost-height,var(--toast-height))]\",\r\n    \"absolute w-full z-[calc(1000-var(--toast-index))]\",\r\n    \"rounded-base border-2 bg-white bg-clip-padding p-6 pr-8 shadow-brutal select-none\",\r\n    \"after:absolute after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']\",\r\n    \"data-ending-style:opacity-0\",\r\n    \"data-limited:opacity-0\",\r\n    \"h-(--height) data-expanded:h-(--toast-height)\",\r\n    \"transition-[transform_0.5s_cubic-bezier(0.22,1,0.36,1),opacity_0.5s,height_0.15s]\",\r\n    \"data-[type=success]:bg-mint data-[type=success]:text-black\",\r\n    \"data-[type=error]:bg-hot-pink data-[type=error]:text-white\",\r\n    \"data-[type=warning]:bg-lemon data-[type=warning]:text-black\",\r\n    \"data-[type=info]:bg-main data-[type=info]:text-black\",\r\n].join(\" \")\r\n\r\nconst BOTTOM_TOAST_CLASSES = [\r\n    \"[--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))]\",\r\n    \"bottom-0 origin-bottom\",\r\n    \"transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--peek))-(var(--shrink)*var(--height))))_scale(var(--scale))]\",\r\n    \"after:top-full\",\r\n    \"data-expanded:transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--offset-y)))]\",\r\n    \"data-starting-style:transform-[translateY(150%)]\",\r\n    \"[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:transform-[translateY(150%)]\",\r\n    \"data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+150%))]\",\r\n    \"data-expanded:data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+150%))]\",\r\n    \"data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]\",\r\n    \"data-expanded:data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]\",\r\n    \"data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]\",\r\n    \"data-expanded:data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]\",\r\n    \"data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-150%))]\",\r\n    \"data-expanded:data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-150%))]\",\r\n].join(\" \")\r\n\r\nconst TOP_TOAST_CLASSES = [\r\n    \"[--offset-y:calc(var(--toast-offset-y)+calc(var(--toast-index)*var(--gap))+var(--toast-swipe-movement-y))]\",\r\n    \"top-0 origin-top\",\r\n    \"transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+(var(--toast-index)*var(--peek))+(var(--shrink)*var(--height))))_scale(var(--scale))]\",\r\n    \"after:bottom-full\",\r\n    \"data-expanded:transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--offset-y)))]\",\r\n    \"data-starting-style:transform-[translateY(-150%)]\",\r\n    \"[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:transform-[translateY(-150%)]\",\r\n    \"data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-150%))]\",\r\n    \"data-expanded:data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-150%))]\",\r\n    \"data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]\",\r\n    \"data-expanded:data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]\",\r\n    \"data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]\",\r\n    \"data-expanded:data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]\",\r\n    \"data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+150%))]\",\r\n    \"data-expanded:data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+150%))]\",\r\n].join(\" \")\r\n\r\nfunction resolvePromisePhase(\r\n    option: string | ToastOptions,\r\n    defaultType: string,\r\n) {\r\n    if (typeof option === \"string\") {\r\n        return { title: option, type: defaultType }\r\n    }\r\n    return { type: defaultType, ...option }\r\n}\r\n\r\nconst Toast = React.forwardRef<\r\n    React.ComponentRef<typeof BaseToast.Root>,\r\n    ToastProps\r\n>(({ className, ...props }, ref) => (\r\n    <BaseToast.Root\r\n        ref={ref}\r\n        className={cn(\r\n            \"group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-base border-2 border-black p-6 pr-8 shadow-brutal transition-[transform,opacity,box-shadow,background-color,color] duration-200\",\r\n            className,\r\n        )}\r\n        {...props}\r\n    />\r\n))\r\nToast.displayName = \"Toast\"\r\n\r\nconst ToastViewport = React.forwardRef<\r\n    React.ComponentRef<typeof BaseToast.Viewport>,\r\n    React.ComponentPropsWithoutRef<typeof BaseToast.Viewport>\r\n>(({ className, ...props }, ref) => (\r\n    <BaseToast.Viewport\r\n        ref={ref}\r\n        className={cn(\r\n            \"fixed bottom-4 right-4 z-50 w-full max-w-sm flex\",\r\n            className,\r\n        )}\r\n        {...props}\r\n    />\r\n))\r\nToastViewport.displayName = \"ToastViewport\"\r\n\r\nconst ToastAction = React.forwardRef<\r\n    React.ComponentRef<typeof BaseToast.Action>,\r\n    React.ComponentPropsWithoutRef<typeof BaseToast.Action>\r\n>(({ className, ...props }, ref) => (\r\n    <BaseToast.Action\r\n        ref={ref}\r\n        className={cn(\r\n            \"inline-flex h-8 shrink-0 items-center justify-center rounded-base border-2 border-black bg-white px-3 text-sm font-bold text-black shadow-brutal-sm transition-brutal hover:translate-x-px hover:translate-y-px hover:shadow-none focus-brutal disabled:pointer-events-none disabled:opacity-50\",\r\n            className,\r\n        )}\r\n        {...props}\r\n    />\r\n))\r\nToastAction.displayName = \"ToastAction\"\r\n\r\nconst ToastClose = React.forwardRef<\r\n    React.ComponentRef<typeof BaseToast.Close>,\r\n    React.ComponentPropsWithoutRef<typeof BaseToast.Close>\r\n>(({ className, ...props }, ref) => (\r\n    <BaseToast.Close\r\n        ref={ref}\r\n        className={cn(\r\n            \"absolute right-2 top-2 rounded-base p-1 text-black/50 opacity-0 transition-opacity hover:text-black focus-visible:opacity-100 focus-brutal group-hover:opacity-100\",\r\n            className,\r\n        )}\r\n        toast-close=\"\"\r\n        {...props}\r\n    >\r\n        ×\r\n    </BaseToast.Close>\r\n))\r\nToastClose.displayName = \"ToastClose\"\r\n\r\nconst ToastTitle = React.forwardRef<\r\n    React.ComponentRef<typeof BaseToast.Title>,\r\n    React.ComponentPropsWithoutRef<typeof BaseToast.Title>\r\n>(({ className, ...props }, ref) => (\r\n    <BaseToast.Title\r\n        ref={ref}\r\n        className={cn(\"text-sm font-semibold\", className)}\r\n        {...props}\r\n    />\r\n))\r\nToastTitle.displayName = \"ToastTitle\"\r\n\r\nconst ToastDescription = React.forwardRef<\r\n    React.ComponentRef<typeof BaseToast.Description>,\r\n    React.ComponentPropsWithoutRef<typeof BaseToast.Description>\r\n>(({ className, ...props }, ref) => (\r\n    <BaseToast.Description\r\n        ref={ref}\r\n        className={cn(\"text-sm opacity-90\", className)}\r\n        {...props}\r\n    />\r\n))\r\nToastDescription.displayName = \"ToastDescription\"\r\n\r\ntype ToastProviderProps = React.ComponentProps<typeof BaseToast.Provider>\r\n\r\nconst ToastProvider = ({ ...props }: ToastProviderProps) => {\r\n    return <BaseToast.Provider {...props} />\r\n}\r\n\r\nconst ToastRenderer = ({ position }: { position: ToastPosition }) => {\r\n    const { toasts } = BaseToast.useToastManager()\r\n    const isTop = position.startsWith(\"top\")\r\n    const positionClasses = isTop ? TOP_TOAST_CLASSES : BOTTOM_TOAST_CLASSES\r\n\r\n    return (\r\n        <BaseToast.Portal>\r\n            <ToastViewport className={VIEWPORT_POSITIONS[position]}>\r\n                {toasts.map((t) => (\r\n                    <BaseToast.Root\r\n                        key={t.id}\r\n                        toast={t}\r\n                        swipeDirection={getSwipeDirection(position)}\r\n                        className={cn(COMMON_TOAST_CLASSES, positionClasses)}\r\n                    >\r\n                        <BaseToast.Content className=\"flex flex-col gap-1 overflow-hidden transition-opacity duration-250 data-behind:pointer-events-none data-behind:opacity-0 data-expanded:pointer-events-auto data-expanded:opacity-100\">\r\n                            <BaseToast.Title className=\"text-sm font-semibold\" />\r\n                            <BaseToast.Description className=\"text-sm opacity-90\" />\r\n                            {t.actionProps && (\r\n                                <div className=\"mt-1\">\r\n                                    <BaseToast.Action\r\n                                        className=\"inline-flex h-7 shrink-0 items-center justify-center rounded-base border-2 border-black bg-white px-3 text-xs font-bold text-black shadow-brutal-sm transition-brutal hover:translate-x-px hover:translate-y-px hover:shadow-none focus-brutal disabled:pointer-events-none disabled:opacity-50\"\r\n                                        {...t.actionProps}\r\n                                    />\r\n                                </div>\r\n                            )}\r\n                            <BaseToast.Close\r\n                                className=\"absolute right-2 top-2 rounded-base p-1 text-black/80 transition-opacity hover:text-black focus-visible:opacity-100 focus-brutal\"\r\n                                aria-label=\"Close\"\r\n                            >\r\n                                ×\r\n                            </BaseToast.Close>\r\n                        </BaseToast.Content>\r\n                    </BaseToast.Root>\r\n                ))}\r\n            </ToastViewport>\r\n        </BaseToast.Portal>\r\n    )\r\n}\r\n\r\nconst Toaster = ({ position = \"bottom-right\" }: ToasterProps) => {\r\n    return (\r\n        <ToastProvider toastManager={toastManager}>\r\n            <ToastRenderer position={position} />\r\n        </ToastProvider>\r\n    )\r\n}\r\n\r\nconst toastManager = BaseToast.createToastManager()\r\n\r\nexport const toast = {\r\n    success: (message: string, options?: ToastOptions) =>\r\n        toastManager.add({ description: message, type: \"success\", ...options }),\r\n    error: (message: string, options?: ToastOptions) =>\r\n        toastManager.add({ description: message, type: \"error\", ...options }),\r\n    info: (message: string, options?: ToastOptions) =>\r\n        toastManager.add({ description: message, type: \"info\", ...options }),\r\n    warning: (message: string, options?: ToastOptions) =>\r\n        toastManager.add({ description: message, type: \"warning\", ...options }),\r\n    promise: <Value,>(\r\n        promiseValue: Promise<Value>,\r\n        options: ToastPromiseOptions<Value>,\r\n    ) => {\r\n        const { loading, success, error } = options\r\n        return toastManager.promise(promiseValue, {\r\n            loading: resolvePromisePhase(loading, \"info\"),\r\n            success:\r\n                typeof success === \"function\"\r\n                    ? (result: Value) =>\r\n                          resolvePromisePhase(success(result), \"success\")\r\n                    : resolvePromisePhase(success, \"success\"),\r\n            error:\r\n                typeof error === \"function\"\r\n                    ? (err: unknown) =>\r\n                          resolvePromisePhase(error(err), \"error\")\r\n                    : resolvePromisePhase(error, \"error\"),\r\n        })\r\n    },\r\n    dismiss: (toastId: string) => toastManager.close(toastId),\r\n}\r\n\r\nexport {\r\n    type ToastPosition,\r\n    type ToastProps,\r\n    type ToastOptions,\r\n    type ToastPromiseOptions,\r\n    type ToasterProps,\r\n    ToastProvider,\r\n    ToastViewport,\r\n    Toast,\r\n    ToastTitle,\r\n    ToastDescription,\r\n    ToastClose,\r\n    ToastAction,\r\n    Toaster,\r\n}\r\n",
      "type": "registry:ui"
    }
  ]
}