{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toast",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "@coss/button"
  ],
  "files": [
    {
      "path": "registry/default/ui/toast.tsx",
      "content": "\"use client\";\n\nimport { Toast } from \"@base-ui/react/toast\";\nimport {\n  CircleAlertIcon,\n  CircleCheckIcon,\n  InfoIcon,\n  LoaderCircleIcon,\n  TriangleAlertIcon,\n} from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"@/registry/default/lib/utils\";\nimport { buttonVariants } from \"@/registry/default/ui/button\";\n\nconst TOAST_ICONS = {\n  error: CircleAlertIcon,\n  info: InfoIcon,\n  loading: LoaderCircleIcon,\n  success: CircleCheckIcon,\n  warning: TriangleAlertIcon,\n} as const;\n\ntype SwipeDirection = \"up\" | \"down\" | \"left\" | \"right\";\n\ntype ToastData = {\n  rootProps?: Omit<\n    React.ComponentProps<typeof Toast.Root>,\n    \"children\" | \"className\" | \"swipeDirection\" | \"toast\"\n  >;\n  tooltipStyle?: boolean;\n};\n\nfunction getSwipeDirection(position: ToastPosition): SwipeDirection[] {\n  const verticalDirection: SwipeDirection = position.startsWith(\"top\")\n    ? \"up\"\n    : \"down\";\n\n  if (position.includes(\"center\")) {\n    return [verticalDirection];\n  }\n\n  if (position.includes(\"left\")) {\n    return [\"left\", verticalDirection];\n  }\n\n  return [\"right\", verticalDirection];\n}\n\nfunction upsertReplayClassName(toast: {\n  type?: string;\n  updateKey?: number;\n}): string | undefined {\n  const k = toast.updateKey ?? 0;\n  if (k <= 0) return undefined;\n  const isEven = k % 2 === 0;\n  if (toast.type === \"error\") {\n    return isEven ? \"animate-toast-error-even\" : \"animate-toast-error-odd\";\n  }\n  return isEven ? \"animate-toast-success-even\" : \"animate-toast-success-odd\";\n}\n\nfunction Toasts({\n  position,\n  portalProps,\n}: {\n  position: ToastPosition;\n  portalProps?: React.ComponentProps<typeof Toast.Portal>;\n}): React.ReactElement {\n  const { toasts } = Toast.useToastManager();\n  const swipeDirection = getSwipeDirection(position);\n\n  return (\n    <Toast.Portal data-slot=\"toast-portal\" {...portalProps}>\n      <Toast.Viewport\n        className={cn(\n          \"fixed z-60 mx-auto flex w-[calc(100%-var(--toast-inset)*2)] max-w-90 [--toast-inset:--spacing(4)] sm:[--toast-inset:--spacing(8)]\",\n          // Vertical positioning\n          \"data-[position*=top]:top-(--toast-inset)\",\n          \"data-[position*=bottom]:bottom-(--toast-inset)\",\n          // Horizontal positioning\n          \"data-[position*=left]:left-(--toast-inset)\",\n          \"data-[position*=right]:right-(--toast-inset)\",\n          \"data-[position*=center]:left-1/2 data-[position*=center]:-translate-x-1/2\",\n        )}\n        data-position={position}\n        data-slot=\"toast-viewport\"\n      >\n        {toasts.map((toast) => {\n          const Icon = toast.type\n            ? TOAST_ICONS[toast.type as keyof typeof TOAST_ICONS]\n            : null;\n          const toastData = toast.data as ToastData | undefined;\n\n          return (\n            <Toast.Root\n              key={toast.id}\n              className={cn(\n                \"absolute z-[calc(9999-var(--toast-index))] h-(--toast-calc-height) w-full select-none rounded-lg border bg-[color-mix(in_srgb,var(--popover),var(--color-black)_calc(1%*max(0,var(--toast-index,0))))] not-dark:bg-clip-padding text-popover-foreground shadow-lg/5 [transition:transform_.5s_cubic-bezier(.22,1,.36,1),opacity_.5s,height_.15s,background-color_.5s] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-expanded:bg-popover dark:bg-[color-mix(in_srgb,var(--popover),var(--color-black)_calc(6%*max(0,var(--toast-index,0))))] dark:data-expanded:bg-popover dark:before:shadow-[0_-1px_--theme(--color-white/6%)]\",\n                // Base positioning using data-position\n                \"data-[position*=right]:right-0 data-[position*=right]:left-auto\",\n                \"data-[position*=left]:right-auto data-[position*=left]:left-0\",\n                \"data-[position*=center]:right-0 data-[position*=center]:left-0\",\n                \"data-[position*=top]:top-0 data-[position*=top]:bottom-auto data-[position*=top]:origin-[50%_calc(50%-50%*min(var(--toast-index,0),1))]\",\n                \"data-[position*=bottom]:top-auto data-[position*=bottom]:bottom-0 data-[position*=bottom]:origin-[50%_calc(50%+50%*min(var(--toast-index,0),1))]\",\n                // Gap fill for hover\n                \"after:absolute after:left-0 after:h-[calc(var(--toast-gap)+1px)] after:w-full\",\n                \"data-[position*=top]:after:top-full\",\n                \"data-[position*=bottom]:after:bottom-full\",\n                // Define some variables\n                \"[--toast-calc-height:var(--toast-frontmost-height,var(--toast-height))] [--toast-gap:--spacing(3)] [--toast-peek:--spacing(3)] [--toast-scale:calc(max(0,1-(var(--toast-index)*.1)))] [--toast-shrink:calc(1-var(--toast-scale))]\",\n                // Define offset-y variable\n                \"data-[position*=top]:[--toast-calc-offset-y:calc(var(--toast-offset-y)+var(--toast-index)*var(--toast-gap)+var(--toast-swipe-movement-y))]\",\n                \"data-[position*=bottom]:[--toast-calc-offset-y:calc(var(--toast-offset-y)*-1+var(--toast-index)*var(--toast-gap)*-1+var(--toast-swipe-movement-y))]\",\n                // Default state transform\n                \"data-[position*=top]:transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+(var(--toast-index)*var(--toast-peek))+(var(--toast-shrink)*var(--toast-calc-height))))_scale(var(--toast-scale))]\",\n                \"data-[position*=bottom]:transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--toast-peek))-(var(--toast-shrink)*var(--toast-calc-height))))_scale(var(--toast-scale))]\",\n                // Limited state\n                \"data-limited:opacity-0\",\n                // Expanded state\n                \"data-expanded:h-(--toast-height)\",\n                \"data-position:data-expanded:transform-[translateX(var(--toast-swipe-movement-x))_translateY(var(--toast-calc-offset-y))]\",\n                // Starting and ending animations\n                \"data-[position*=top]:data-starting-style:transform-[translateY(calc(-100%-var(--toast-inset)))]\",\n                \"data-[position*=bottom]:data-starting-style:transform-[translateY(calc(100%+var(--toast-inset)))]\",\n                \"data-ending-style:opacity-0\",\n                // Ending animations (direction-aware)\n                \"data-[position*=top]:data-ending-style:not-data-limited:not-data-swipe-direction:transform-[translateY(calc(-100%-var(--toast-inset)))]\",\n                \"data-[position*=bottom]:data-ending-style:not-data-limited:not-data-swipe-direction:transform-[translateY(calc(100%+var(--toast-inset)))]\",\n                \"data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-100%-var(--toast-inset)))_translateY(var(--toast-calc-offset-y))]\",\n                \"data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+100%+var(--toast-inset)))_translateY(var(--toast-calc-offset-y))]\",\n                \"data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-100%-var(--toast-inset)))]\",\n                \"data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+100%+var(--toast-inset)))]\",\n                // Ending animations (expanded)\n                \"data-expanded:data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-100%-var(--toast-inset)))_translateY(var(--toast-calc-offset-y))]\",\n                \"data-expanded:data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+100%+var(--toast-inset)))_translateY(var(--toast-calc-offset-y))]\",\n                \"data-expanded:data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-100%-var(--toast-inset)))]\",\n                \"data-expanded:data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+100%+var(--toast-inset)))]\",\n                upsertReplayClassName(toast),\n              )}\n              {...toastData?.rootProps}\n              data-position={position}\n              swipeDirection={swipeDirection}\n              toast={toast}\n            >\n              <Toast.Content className=\"pointer-events-auto flex items-center justify-between gap-1.5 overflow-hidden px-3.5 py-3 text-sm transition-opacity duration-250 data-behind:not-data-expanded:pointer-events-none data-behind:opacity-0 data-expanded:opacity-100\">\n                <div className=\"flex gap-2\">\n                  {Icon && (\n                    <div\n                      className=\"[&>svg]:h-lh [&>svg]:w-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\"\n                      data-slot=\"toast-icon\"\n                    >\n                      <Icon className=\"in-data-[type=loading]:animate-spin in-data-[type=error]:text-destructive in-data-[type=info]:text-info in-data-[type=success]:text-success in-data-[type=warning]:text-warning in-data-[type=loading]:opacity-80\" />\n                    </div>\n                  )}\n\n                  <div className=\"flex flex-col gap-0.5\">\n                    <Toast.Title\n                      className=\"font-medium\"\n                      data-slot=\"toast-title\"\n                    />\n                    <Toast.Description\n                      className=\"text-muted-foreground\"\n                      data-slot=\"toast-description\"\n                    />\n                  </div>\n                </div>\n                {toast.actionProps && (\n                  <Toast.Action\n                    className={buttonVariants({ size: \"xs\" })}\n                    data-slot=\"toast-action\"\n                  >\n                    {toast.actionProps.children}\n                  </Toast.Action>\n                )}\n              </Toast.Content>\n            </Toast.Root>\n          );\n        })}\n      </Toast.Viewport>\n    </Toast.Portal>\n  );\n}\n\nfunction AnchoredToasts({\n  portalProps,\n}: {\n  portalProps?: React.ComponentProps<typeof Toast.Portal>;\n}): React.ReactElement {\n  const { toasts } = Toast.useToastManager();\n\n  return (\n    <Toast.Portal data-slot=\"toast-portal-anchored\" {...portalProps}>\n      <Toast.Viewport\n        className=\"outline-none\"\n        data-slot=\"toast-viewport-anchored\"\n      >\n        {toasts.map((toast) => {\n          const Icon = toast.type\n            ? TOAST_ICONS[toast.type as keyof typeof TOAST_ICONS]\n            : null;\n          const toastData = toast.data as ToastData | undefined;\n          const tooltipStyle = toastData?.tooltipStyle ?? false;\n          const positionerProps = toast.positionerProps;\n\n          if (!positionerProps?.anchor) {\n            return null;\n          }\n\n          return (\n            <Toast.Positioner\n              key={toast.id}\n              className=\"z-50 max-w-[min(--spacing(64),var(--available-width))]\"\n              data-slot=\"toast-positioner\"\n              sideOffset={positionerProps.sideOffset ?? 4}\n              toast={toast}\n            >\n              <Toast.Root\n                className={cn(\n                  \"relative text-balance border bg-popover not-dark:bg-clip-padding text-popover-foreground text-xs transition-[scale,opacity] before:pointer-events-none before:absolute before:inset-0 before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]\",\n                  tooltipStyle\n                    ? \"rounded-md shadow-md/5 before:rounded-[calc(var(--radius-md)-1px)]\"\n                    : \"rounded-lg shadow-lg/5 before:rounded-[calc(var(--radius-lg)-1px)]\",\n                  upsertReplayClassName(toast),\n                )}\n                {...toastData?.rootProps}\n                data-slot=\"toast-popup\"\n                toast={toast}\n              >\n                {tooltipStyle ? (\n                  <Toast.Content className=\"pointer-events-auto px-2 py-1\">\n                    <Toast.Title data-slot=\"toast-title\" />\n                  </Toast.Content>\n                ) : (\n                  <Toast.Content className=\"pointer-events-auto flex items-center justify-between gap-1.5 overflow-hidden px-3.5 py-3 text-sm\">\n                    <div className=\"flex gap-2\">\n                      {Icon && (\n                        <div\n                          className=\"[&>svg]:h-lh [&>svg]:w-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\"\n                          data-slot=\"toast-icon\"\n                        >\n                          <Icon className=\"in-data-[type=loading]:animate-spin in-data-[type=error]:text-destructive in-data-[type=info]:text-info in-data-[type=success]:text-success in-data-[type=warning]:text-warning in-data-[type=loading]:opacity-80\" />\n                        </div>\n                      )}\n\n                      <div className=\"flex flex-col gap-0.5\">\n                        <Toast.Title\n                          className=\"font-medium\"\n                          data-slot=\"toast-title\"\n                        />\n                        <Toast.Description\n                          className=\"text-muted-foreground\"\n                          data-slot=\"toast-description\"\n                        />\n                      </div>\n                    </div>\n                    {toast.actionProps && (\n                      <Toast.Action\n                        className={buttonVariants({ size: \"xs\" })}\n                        data-slot=\"toast-action\"\n                      >\n                        {toast.actionProps.children}\n                      </Toast.Action>\n                    )}\n                  </Toast.Content>\n                )}\n              </Toast.Root>\n            </Toast.Positioner>\n          );\n        })}\n      </Toast.Viewport>\n    </Toast.Portal>\n  );\n}\n\nexport const toastManager: ReturnType<typeof Toast.createToastManager> =\n  Toast.createToastManager();\n\nexport const anchoredToastManager: ReturnType<typeof Toast.createToastManager> =\n  Toast.createToastManager();\n\nexport type ToastPosition =\n  | \"top-left\"\n  | \"top-center\"\n  | \"top-right\"\n  | \"bottom-left\"\n  | \"bottom-center\"\n  | \"bottom-right\";\n\nexport interface ToastProviderProps extends Toast.Provider.Props {\n  position?: ToastPosition;\n  portalProps?: React.ComponentProps<typeof Toast.Portal>;\n}\n\nexport function ToastProvider({\n  children,\n  position = \"bottom-right\",\n  portalProps,\n  ...props\n}: ToastProviderProps): React.ReactElement {\n  return (\n    <Toast.Provider toastManager={toastManager} {...props}>\n      {children}\n      <Toasts portalProps={portalProps} position={position} />\n    </Toast.Provider>\n  );\n}\n\nexport interface AnchoredToastProviderProps extends Toast.Provider.Props {\n  portalProps?: React.ComponentProps<typeof Toast.Portal>;\n}\n\nexport function AnchoredToastProvider({\n  children,\n  portalProps,\n  ...props\n}: AnchoredToastProviderProps): React.ReactElement {\n  return (\n    <Toast.Provider toastManager={anchoredToastManager} {...props}>\n      {children}\n      <AnchoredToasts portalProps={portalProps} />\n    </Toast.Provider>\n  );\n}\n\nexport { Toast as ToastPrimitive };\n",
      "type": "registry:ui"
    }
  ],
  "cssVars": {
    "theme": {
      "--animate-toast-success-odd": "toast-success-odd 0.32s cubic-bezier(0.5, 1, 0.89, 1)",
      "--animate-toast-success-even": "toast-success-even 0.32s cubic-bezier(0.5, 1, 0.89, 1)",
      "--animate-toast-error-odd": "toast-error-odd 0.28s cubic-bezier(0.5, 1, 0.89, 1)",
      "--animate-toast-error-even": "toast-error-even 0.28s cubic-bezier(0.5, 1, 0.89, 1)"
    }
  },
  "css": {
    "@keyframes toast-success-odd": {
      "0%": {
        "scale": "1"
      },
      "30%": {
        "scale": "1.025"
      },
      "60%": {
        "scale": "0.99"
      },
      "100%": {
        "scale": "1"
      }
    },
    "@keyframes toast-error-odd": {
      "0%": {
        "translate": "0 0"
      },
      "25%": {
        "translate": "-3px 0"
      },
      "50%": {
        "translate": "3px 0"
      },
      "75%": {
        "translate": "-3px 0"
      },
      "100%": {
        "translate": "0 0"
      }
    },
    "@keyframes toast-success-even": {
      "0%": {
        "scale": "1"
      },
      "30%": {
        "scale": "1.025"
      },
      "60%": {
        "scale": "0.99"
      },
      "100%": {
        "scale": "1"
      }
    },
    "@keyframes toast-error-even": {
      "0%": {
        "translate": "0 0"
      },
      "25%": {
        "translate": "-3px 0"
      },
      "50%": {
        "translate": "3px 0"
      },
      "75%": {
        "translate": "-3px 0"
      },
      "100%": {
        "translate": "0 0"
      }
    }
  },
  "type": "registry:ui"
}