{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toast",
  "title": "Toast",
  "description": "Generates toast notifications.",
  "dependencies": [
    "@base-ui/react",
    "lucide-react",
    "class-variance-authority"
  ],
  "registryDependencies": [
    "@lumi-ui/button",
    "@lumi-ui/arrow-svg"
  ],
  "files": [
    {
      "path": "registry/ui/toast.tsx",
      "content": "\"use client\";\n\nimport type {\n  ToastManagerUpdateOptions,\n  TooltipPositionerProps,\n} from \"@base-ui/react\";\nimport {\n  Toast as BaseToast,\n  type ToastManagerAddOptions,\n  type ToastRootProps,\n} from \"@base-ui/react/toast\";\nimport { cva } from \"class-variance-authority\";\nimport {\n  AlertCircle,\n  AlertTriangle,\n  CheckCircle2,\n  Info,\n  Loader2,\n  X,\n} from \"lucide-react\";\nimport { cn } from \"@/registry/lib/utils\";\nimport { ArrowSvg } from \"@/registry/ui/arrow-svg\";\nimport { buttonVariants } from \"@/registry/ui/button\";\n\nconst stackedManager = BaseToast.createToastManager();\nconst anchoredManager = BaseToast.createToastManager();\n\ntype ToastType =\n  | \"success\"\n  | \"error\"\n  | \"warning\"\n  | \"info\"\n  | \"loading\"\n  | \"default\";\n\ntype ToastPosition =\n  | \"top-left\"\n  | \"top-center\"\n  | \"top-right\"\n  | \"bottom-left\"\n  | \"bottom-center\"\n  | \"bottom-right\";\n\ntype SwipePosition = ToastRootProps[\"swipeDirection\"];\n\ntype ToasterProps = {\n  position?: ToastPosition;\n  swipeDirection?: SwipePosition;\n  limit?: number;\n};\n\ninterface ToastData {\n  closable?: boolean;\n  customContent?: React.ReactNode;\n}\n\ninterface ToastProps extends BaseToast.Root.Props {\n  type?: ToastType;\n  data?: ToastData;\n}\n\ntype ToastOptions = ToastManagerAddOptions<ToastProps> & ToastData;\ntype ToastUpdateOptions = ToastManagerUpdateOptions<ToastProps> & ToastData;\n\nconst Icons = {\n  default: null,\n  error: AlertCircle,\n  info: Info,\n  loading: Loader2,\n  success: CheckCircle2,\n  warning: AlertTriangle,\n};\n\nconst toastVariants = cva(\n  // Custom styles, change the look of the toast\n  \"rounded-md overlay-outline shadow-lg transition-all select-none\",\n  {\n    defaultVariants: {\n      type: \"default\",\n    },\n    variants: {\n      type: {\n        default: \"outline-border bg-popover text-popover-foreground\",\n        error:\n          \"outline-red-500/40 bg-red-50 text-red-900 dark:bg-red-950 dark:text-red-200\",\n        info: \"outline-sky-500/40 bg-sky-50 text-sky-900 dark:bg-sky-950 dark:text-sky-200\",\n        loading: \"outline-border bg-popover text-popover-foreground\",\n        success:\n          \"outline-green-500/40 bg-green-50 text-green-900 dark:bg-green-950 dark:text-green-200\",\n        warning:\n          \"outline-yellow-500/40 bg-yellow-50 text-yellow-900 dark:bg-yellow-950 dark:text-yellow-200\",\n      },\n    },\n  },\n);\n\nconst getOptions = (options?: ToastOptions) => {\n  const { customContent, closable, data, ...rest } = options || {};\n  return {\n    ...rest,\n    data: { ...data, closable, customContent },\n  };\n};\n\nconst toast = {\n  add: (options: ToastOptions) => stackedManager.add(getOptions(options)),\n  anchor: (\n    anchor: HTMLElement | null,\n    options?: Omit<ToastOptions, \"positionerProps\"> & {\n      sideOffset?: TooltipPositionerProps[\"sideOffset\"];\n      side?: TooltipPositionerProps[\"side\"];\n    },\n  ) => {\n    if (!anchor) return \"\";\n    const { sideOffset, side, ...rest } = options || {};\n    const normalized = getOptions(rest);\n    return anchoredManager.add({\n      ...normalized,\n      positionerProps: {\n        anchor,\n        side: side ?? \"bottom\",\n        sideOffset: sideOffset ?? 8,\n      },\n    });\n  },\n  close: (id: string) => {\n    stackedManager.close(id);\n    anchoredManager.close(id);\n  },\n  error: (options?: ToastOptions) =>\n    stackedManager.add({ ...getOptions(options), type: \"error\" }),\n  info: (options?: ToastOptions) =>\n    stackedManager.add({ ...getOptions(options), type: \"info\" }),\n  promise: <Value,>(\n    promise: Promise<Value>,\n    options: {\n      loading: ToastOptions | string;\n      success:\n        | ToastOptions\n        | string\n        | ((value: Value) => ToastOptions | string);\n      error: ToastOptions | string | ((error: Error) => ToastOptions | string);\n    },\n  ) => {\n    const normalize = (opt: ToastOptions | string) =>\n      typeof opt === \"string\" ? { title: opt } : getOptions(opt);\n\n    return stackedManager.promise(promise, {\n      error: (err) => {\n        const resolved =\n          typeof options.error === \"function\"\n            ? options.error(err)\n            : options.error;\n        return normalize(resolved);\n      },\n      loading: normalize(options.loading),\n      success: (val) => {\n        const resolved =\n          typeof options.success === \"function\"\n            ? options.success(val)\n            : options.success;\n        return normalize(resolved);\n      },\n    });\n  },\n  success: (options?: ToastOptions) =>\n    stackedManager.add({ ...getOptions(options), type: \"success\" }),\n  update: (id: string, options?: ToastUpdateOptions) =>\n    stackedManager.update(id, getOptions(options)),\n  warning: (options?: ToastOptions) =>\n    stackedManager.add({ ...getOptions(options), type: \"warning\" }),\n};\n\nconst ToastViewport = ({ ...props }: BaseToast.Viewport.Props) => {\n  return <BaseToast.Viewport data-slot=\"toast-viewport\" {...props} />;\n};\n\nconst ToastContent = ({ children, ...props }: BaseToast.Content.Props) => {\n  return (\n    <BaseToast.Content data-slot=\"toast-content\" {...props}>\n      {children}\n    </BaseToast.Content>\n  );\n};\n\nconst ToastTitle = ({ children, ...props }: BaseToast.Title.Props) => {\n  return (\n    <BaseToast.Title\n      className=\"text-sm font-semibold\"\n      data-slot=\"toast-title\"\n      {...props}\n    >\n      {children}\n    </BaseToast.Title>\n  );\n};\n\nconst ToastDescription = ({\n  children,\n  ...props\n}: BaseToast.Description.Props) => {\n  return (\n    <BaseToast.Description\n      className=\"text-sm\"\n      data-slot=\"toast-description\"\n      {...props}\n    >\n      {children}\n    </BaseToast.Description>\n  );\n};\n\nconst ToastClose = ({ children, ...props }: BaseToast.Close.Props) => {\n  return (\n    <BaseToast.Close aria-label=\"Close\" data-slot=\"toast-close\" {...props}>\n      {children}\n    </BaseToast.Close>\n  );\n};\n\nconst ToastAction = (props: BaseToast.Action.Props) => {\n  return <BaseToast.Action data-slot=\"toast-action\" {...props} />;\n};\n\nconst ToastArrow = (props: BaseToast.Arrow.Props) => {\n  return (\n    <BaseToast.Arrow\n      className=\"data-[side=bottom]:-top-2 data-[side=left]:right-[-13px] data-[side=left]:rotate-90 data-[side=right]:left-[-13px] data-[side=right]:-rotate-90 data-[side=top]:-bottom-2 data-[side=top]:rotate-180\"\n      data-slot=\"toast-arrow\"\n      {...props}\n    >\n      <ArrowSvg />\n    </BaseToast.Arrow>\n  );\n};\n\nconst Toaster = ({\n  position = \"bottom-right\",\n  swipeDirection = [\"down\", \"right\"],\n  limit = 3,\n}: ToasterProps) => {\n  return (\n    <>\n      <BaseToast.Provider limit={limit} toastManager={stackedManager}>\n        <StackedToast position={position} swipeDirection={swipeDirection} />\n      </BaseToast.Provider>\n      <BaseToast.Provider toastManager={anchoredManager}>\n        <AnchoredToast />\n      </BaseToast.Provider>\n    </>\n  );\n};\n\nconst StackedToast = ({\n  position,\n  swipeDirection,\n}: {\n  position: ToastPosition;\n  swipeDirection: SwipePosition;\n}) => {\n  const { toasts } = BaseToast.useToastManager();\n  return (\n    <BaseToast.Portal>\n      <ToastViewport className=\"toast-viewport\" data-position={position}>\n        {toasts.map((toast) => {\n          const toastType = (toast.type as ToastType) || \"default\";\n          const Icon = Icons[toastType];\n          const isCustomContent = toast.data?.customContent;\n\n          return (\n            <BaseToast.Root\n              className={cn(\n                \"toast-root\",\n                !isCustomContent && toastVariants({ type: toastType }),\n              )}\n              data-position={position}\n              data-slot=\"toast-root\"\n              key={toast.id}\n              swipeDirection={swipeDirection}\n              toast={toast}\n            >\n              <ToastContent\n                className={cn(\n                  \"overflow-hidden transition-opacity duration-250 select-none\",\n                  \"data-[behind]:pointer-events-none data-[behind]:opacity-0 data-[expanded]:pointer-events-auto data-[expanded]:opacity-100\",\n                )}\n              >\n                {isCustomContent ? (\n                  toast.data.customContent\n                ) : (\n                  <div className=\"flex items-start gap-3 p-4\">\n                    {Icon && (\n                      <Icon\n                        className={cn(\n                          \"size-5 shrink-0 mt-0.5\",\n                          toastType === \"loading\" && \"animate-spin mt-0\",\n                        )}\n                        data-slot=\"toast-icon\"\n                      />\n                    )}\n                    <div className=\"flex flex-col flex-wrap flex-1 gap-2\">\n                      <div className=\"space-y-1\">\n                        {toast.title && <ToastTitle>{toast.title}</ToastTitle>}\n                        {toast.description && (\n                          <ToastDescription>\n                            {toast.description}\n                          </ToastDescription>\n                        )}\n                      </div>\n                      {toast.actionProps && (\n                        <ToastAction\n                          className={cn(\n                            buttonVariants({ size: \"sm\" }),\n                            \"w-fit\",\n                          )}\n                        >\n                          {toast.actionProps.children}\n                        </ToastAction>\n                      )}\n                      {toast.data?.closable && (\n                        <ToastClose\n                          className={cn(\n                            buttonVariants({\n                              size: \"icon-sm\",\n                              variant: \"ghost\",\n                            }),\n                            \"absolute top-1 right-1 size-6\",\n                            toastType === \"error\" && \"hover:bg-red-500/20!\",\n                            toastType === \"success\" && \"hover:bg-green-500/20!\",\n                            toastType === \"warning\" &&\n                              \"hover:bg-yellow-500/20!\",\n                            toastType === \"info\" && \"hover:bg-blue-500/20!\",\n                          )}\n                        >\n                          <X className=\"size-4\" />\n                        </ToastClose>\n                      )}\n                    </div>\n                  </div>\n                )}\n              </ToastContent>\n            </BaseToast.Root>\n          );\n        })}\n      </ToastViewport>\n    </BaseToast.Portal>\n  );\n};\n\nconst AnchoredToast = () => {\n  const { toasts } = BaseToast.useToastManager();\n\n  return (\n    <BaseToast.Portal>\n      <ToastViewport className=\"fixed inset-0 pointer-events-none\">\n        {toasts.map((toast) => {\n          const isCustomContent = toast.data?.customContent;\n          return (\n            <BaseToast.Positioner\n              className=\"outline-none\"\n              data-slot=\"toast-positioner\"\n              key={toast.id}\n              toast={toast}\n            >\n              <BaseToast.Root\n                className={cn(\n                  !isCustomContent && toastVariants({ type: \"default\" }),\n                  \"animate-popup\",\n                )}\n                data-slot=\"toast-root\"\n                toast={toast}\n              >\n                {isCustomContent ? (\n                  toast.data.customContent\n                ) : (\n                  <>\n                    <ToastArrow />\n                    <ToastContent className=\"flex flex-col py-2 px-3 gap-2\">\n                      {toast.title && <ToastTitle>{toast.title}</ToastTitle>}\n                      {toast.description && (\n                        <ToastDescription>{toast.description}</ToastDescription>\n                      )}\n                    </ToastContent>\n                  </>\n                )}\n              </BaseToast.Root>\n            </BaseToast.Positioner>\n          );\n        })}\n      </ToastViewport>\n    </BaseToast.Portal>\n  );\n};\n\nexport {\n  Toaster,\n  ToastClose,\n  ToastDescription,\n  ToastTitle,\n  ToastAction,\n  ToastArrow,\n  toast,\n};\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}