{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toast",
  "title": "Snackbar",
  "description": "The Material snackbar and its imperative `toast` API, with stacking and actions.",
  "dependencies": [
    "@base-ui/react@^1.7.0",
    "lucide-react@^1.33.0"
  ],
  "registryDependencies": [
    "https://guillermo-rebolledo.github.io/materialcn/r/button.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/materialcn-theme.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/theme-context.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/utils.json"
  ],
  "files": [
    {
      "path": "src/components/ui/toast.tsx",
      "content": "import * as React from \"react\"\nimport { Toast as ToastPrimitive } from \"@base-ui/react/toast\"\nimport type { ToastManager } from \"@base-ui/react/toast\"\nimport { X } from \"lucide-react\"\n\nimport { ThemeContext } from \"@/components/theme-context\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"./button\"\nimport { toast, useToastManager, type SnackbarData } from \"./toast-manager\"\n\nfunction ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {\n  return <ToastPrimitive.Provider {...props} />\n}\n\nfunction ToastPortal({ className, ...props }: ToastPrimitive.Portal.Props) {\n  const theme = React.use(ThemeContext)\n\n  return (\n    <ToastPrimitive.Portal\n      data-slot=\"toast-portal\"\n      className={cn(theme?.resolvedTheme, className)}\n      {...props}\n    />\n  )\n}\n\nfunction ToastViewport({ className, ...props }: ToastPrimitive.Viewport.Props) {\n  return (\n    <ToastPrimitive.Viewport\n      data-slot=\"toast-viewport\"\n      className={cn(\n        \"group/toast-viewport pointer-events-none fixed inset-x-4 bottom-4 mx-auto w-auto max-w-[344px] outline-none\",\n        \"sm:right-6 sm:left-auto sm:mx-0 sm:w-[344px]\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\n/**\n * Later toasts sit *below* earlier ones, which is why the z-index counts down\n * from the snackbar layer rather than up: the frontmost toast is index 0. The\n * scale leaves 100 between layers, so the stack would have to reach a hundred\n * simultaneous toasts before it reached the layer beneath.\n */\nfunction Toast({ className, ...props }: ToastPrimitive.Root.Props) {\n  return (\n    <ToastPrimitive.Root\n      data-slot=\"toast\"\n      className={cn(\n        \"group/toast pointer-events-auto absolute right-0 bottom-0 z-[calc(var(--m3-z-snackbar)-var(--toast-index))] w-full origin-bottom\",\n        \"rounded-m3-xs bg-m3-inverse-surface text-m3-inverse-on-surface shadow-m3-3\",\n        \"outline-none select-none will-change-transform\",\n        \"focus-visible:outline-m3-inverse-primary focus-visible:outline-3 focus-visible:outline-offset-2\",\n        \"[--gap:0.5rem] [--peek:0.5rem]\",\n        \"[--height:var(--toast-frontmost-height,var(--toast-height))]\",\n        \"[--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))]\",\n        \"[--scale:calc(max(0,1-(var(--toast-index)*0.06)))] [--shrink:calc(1-var(--scale))]\",\n        \"h-(--height)\",\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))]\",\n        \"[transition:transform_var(--m3-spring-spatial-default-duration)_var(--m3-spring-spatial-default),opacity_var(--m3-spring-effects-fast-duration)_var(--m3-spring-effects-fast),height_var(--m3-spring-effects-fast-duration)_var(--m3-spring-effects-fast)]\",\n        \"after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']\",\n        \"data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]\",\n        \"data-limited:opacity-0 data-starting-style:opacity-0 data-starting-style:[transform:translateY(100%)]\",\n        \"[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:opacity-0\",\n        \"[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(100%)]\",\n        \"data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]\",\n        \"data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]\",\n        \"data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]\",\n        \"data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]\",\n        \"data-expanded:data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]\",\n        \"data-expanded:data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]\",\n        \"data-expanded:data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]\",\n        \"data-expanded:data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ToastContent({ className, ...props }: ToastPrimitive.Content.Props) {\n  return (\n    <ToastPrimitive.Content\n      data-slot=\"toast-content\"\n      className={cn(\n        \"flex w-full items-center overflow-hidden\",\n        \"transition-opacity duration-(--m3-spring-effects-fast-duration) ease-(--m3-spring-effects-fast)\",\n        \"data-behind:opacity-0 data-expanded:opacity-100\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ToastTitle({ className, ...props }: ToastPrimitive.Title.Props) {\n  return (\n    <ToastPrimitive.Title\n      data-slot=\"toast-title\"\n      className={cn(\"text-m3-body-md\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ToastDescription({ className, ...props }: ToastPrimitive.Description.Props) {\n  return (\n    <ToastPrimitive.Description\n      data-slot=\"toast-description\"\n      className={cn(\"text-m3-body-md\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ToastAction({\n  className,\n  render = <Button variant=\"ghost\" size=\"sm\" />,\n  ...props\n}: ToastPrimitive.Action.Props) {\n  return (\n    <ToastPrimitive.Action\n      data-slot=\"toast-action\"\n      render={render}\n      className={cn(\n        \"h-10 shrink-0 rounded-[20px] px-3 text-m3-label-lg text-m3-inverse-primary\",\n        \"focus-visible:outline-m3-inverse-primary\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ToastClose({\n  className,\n  children,\n  render = <Button variant=\"ghost\" size=\"icon-sm\" />,\n  ...props\n}: ToastPrimitive.Close.Props) {\n  return (\n    <ToastPrimitive.Close\n      data-slot=\"toast-close\"\n      aria-label=\"Dismiss notification\"\n      render={render}\n      className={cn(\n        \"size-12 shrink-0 rounded-[24px] text-m3-inverse-on-surface [&_svg]:size-6\",\n        \"focus-visible:outline-m3-inverse-primary\",\n        className,\n      )}\n      {...props}\n    >\n      {children ?? <X aria-hidden=\"true\" />}\n    </ToastPrimitive.Close>\n  )\n}\n\nfunction ToastList() {\n  const { toasts } = useToastManager()\n\n  return toasts.map((toastItem, index) => {\n    const layout = toastItem.data?.layout ?? \"auto\"\n    const dismissible = toastItem.data?.dismissible ?? false\n    const hasAction = Boolean(toastItem.actionProps)\n    const hasControls = hasAction || dismissible\n\n    return (\n      <Toast\n        key={toastItem.id}\n        toast={toastItem}\n        data-dismissible={dismissible || undefined}\n        data-has-action={hasAction || undefined}\n        data-layout={layout}\n        swipeDirection={[\"left\", \"right\", \"down\"]}\n        className={cn(\n          index === 0 &&\n            \"group-focus-within/toast-viewport:[outline:3px_solid_var(--m3-inverse-primary)] group-focus-within/toast-viewport:[outline-offset:2px]\",\n        )}\n      >\n        <ToastContent\n          data-layout={layout}\n          className={cn(\n            layout === \"stacked\" && \"min-h-28 flex-col items-stretch gap-2.5\",\n          )}\n        >\n          <div\n            className={cn(\n              \"flex min-w-0 flex-1 flex-col justify-center py-3.5 pl-4\",\n              !hasControls && \"pr-4\",\n              layout === \"stacked\" && \"w-full flex-none pt-3.5 pb-0 pr-4\",\n            )}\n          >\n            {toastItem.title != null && <ToastTitle />}\n            <ToastDescription />\n          </div>\n          {hasControls && (\n            <div\n              data-slot=\"toast-controls\"\n              className={cn(\n                \"flex shrink-0 items-center\",\n                // Kit: 8dp trailing inset when the action is the last control.\n                hasAction && !dismissible && \"pr-2\",\n                layout === \"stacked\" && \"h-12 w-full justify-end px-2\",\n              )}\n            >\n              {hasAction && <ToastAction />}\n              {dismissible && <ToastClose />}\n            </div>\n          )}\n        </ToastContent>\n      </Toast>\n    )\n  })\n}\n\ntype ToasterProps = Omit<ToastPrimitive.Provider.Props, \"toastManager\"> & {\n  /** An isolated manager; defaults to the exported app-wide `toast`. */\n  toastManager?: ToastManager<SnackbarData>\n  /** Portal customization, including a theme-scoped `container`. */\n  portalProps?: ToastPrimitive.Portal.Props\n  /** Position or label overrides for the notification viewport. */\n  viewportProps?: ToastPrimitive.Viewport.Props\n}\n\n/**\n * Renders the app's snackbar region. Mount once near the application root.\n * Base UI supplies timeout pausing, swipe dismissal, stacking, F6 navigation,\n * focus restoration, and polite/assertive live-region behavior.\n */\nfunction Toaster({\n  children,\n  toastManager = toast,\n  portalProps,\n  viewportProps,\n  ...props\n}: ToasterProps) {\n  return (\n    <ToastProvider toastManager={toastManager} {...props}>\n      {children}\n      <ToastPortal {...portalProps}>\n        <ToastViewport {...viewportProps}>\n          <ToastList />\n        </ToastViewport>\n      </ToastPortal>\n    </ToastProvider>\n  )\n}\n\nexport {\n  Toaster,\n  Toast,\n  ToastAction,\n  ToastClose,\n  ToastContent,\n  ToastDescription,\n  ToastPortal,\n  ToastProvider,\n  ToastTitle,\n  ToastViewport,\n}\nexport { createToastManager, toast, useToastManager } from \"./toast-manager\"\nexport type { SnackbarData } from \"./toast-manager\"\nexport type { ToasterProps }\n",
      "type": "registry:ui"
    },
    {
      "path": "src/components/ui/toast-manager.ts",
      "content": "import { Toast as ToastPrimitive } from \"@base-ui/react/toast\"\n\n/** Material-specific presentation options carried by Base UI's toast manager. */\ntype SnackbarData = {\n  /** Add the kit's trailing 48dp close affordance. */\n  dismissible?: boolean\n  /** Put a longer action below the message instead of beside it. */\n  layout?: \"auto\" | \"stacked\"\n}\n\n/**\n * Creates an isolated snackbar manager, useful for nested apps and previews.\n * Most applications should use the exported global `toast` manager instead.\n */\nfunction createToastManager() {\n  return ToastPrimitive.createToastManager<SnackbarData>()\n}\n\n/** App-wide imperative manager. Mount one `Toaster`, then call `toast.add()`. */\nconst toast = createToastManager()\n\nfunction useToastManager() {\n  return ToastPrimitive.useToastManager<SnackbarData>()\n}\n\nexport { createToastManager, toast, useToastManager }\nexport type { SnackbarData }\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "feedback"
  ],
  "type": "registry:ui"
}