{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toast",
  "title": "Toast",
  "description": "Short notifications that appear over the page, built on Base UI Toast, with types, actions and loading states.",
  "dependencies": [
    "@base-ui/react@^1.8.0",
    "lucide-react@^1.48.0"
  ],
  "registryDependencies": [
    "https://rdf.dev.360online.com/r/button.json"
  ],
  "files": [
    {
      "path": "registry/rapid/ui/toast/toast.tsx",
      "content": "'use client'\n\nimport {\n\ttype ToastManager,\n\ttype ToastManagerPromiseOptions,\n\ttype ToastManagerUpdateOptions,\n\tToast as ToastPrimitive,\n\ttype UseToastManagerReturnValue,\n} from '@base-ui/react/toast'\nimport { CircleCheckIcon, CircleXIcon, InfoIcon, Loader2Icon, TriangleAlertIcon, XIcon } from 'lucide-react'\nimport * as React from 'react'\nimport { Button } from '@/components/ui/button'\nimport { cn } from '@/lib/utils'\n\n// The toast types that get a coloured edge, an icon and a type label. `error` and `destructive` look the same;\n// `toast.promise()` uses `loading`, `success` and `error`.\ntype ToastType = 'info' | 'success' | 'warning' | 'error' | 'destructive' | 'loading'\n\n// A toast with an action button stays until the user closes it or takes the action, unless it sets its own `timeout`.\n// Toasts that time out also pause while the pointer or focus is inside the toast area (Base UI's viewport).\nfunction withToastDefaults<T extends { actionProps?: unknown; timeout?: number }>(options: T): T {\n\tif (options.actionProps && options.timeout === undefined) {\n\t\treturn { ...options, timeout: 0 }\n\t}\n\treturn options\n}\n\nfunction withPromiseDefaults<Value, Data extends object>(\n\toptions: ToastManagerPromiseOptions<Value, Data>,\n): ToastManagerPromiseOptions<Value, Data> {\n\tconst resolve = <A,>(\n\t\toption: string | ToastManagerUpdateOptions<Data> | ((arg: A) => string | ToastManagerUpdateOptions<Data>),\n\t) => {\n\t\tif (typeof option === 'function') {\n\t\t\treturn (arg: A) => {\n\t\t\t\tconst resolved = option(arg)\n\t\t\t\treturn typeof resolved === 'string' ? resolved : withToastDefaults(resolved)\n\t\t\t}\n\t\t}\n\t\treturn typeof option === 'string' ? option : withToastDefaults(option)\n\t}\n\treturn {\n\t\tloading: options.loading,\n\t\tsuccess: resolve(options.success),\n\t\terror: resolve(options.error),\n\t}\n}\n\nfunction withManagerDefaults<M extends Pick<ToastManager, 'add' | 'update' | 'promise'>>(manager: M): M {\n\treturn {\n\t\t...manager,\n\t\tadd: (options) => manager.add(withToastDefaults(options)),\n\t\tupdate: (id, updates) =>\n\t\t\tmanager.update(\n\t\t\t\tid,\n\t\t\t\ttypeof updates === 'function' ? (prev) => withToastDefaults(updates(prev)) : withToastDefaults(updates),\n\t\t\t),\n\t\tpromise: (promise, options) => manager.promise(promise, withPromiseDefaults(options)),\n\t}\n}\n\n// Creates a toast manager to pass to `<Toaster toastManager={…} />`. Use it to call toasts from outside React, or to give\n// one part of the page its own toasts.\nfunction createToastManager<Data extends object = object>(): ToastManager<Data> {\n\treturn withManagerDefaults(ToastPrimitive.createToastManager<Data>())\n}\n\n// The app-wide toast manager used by `<Toaster />`: `toast.add({ title, description, type })`, `toast.promise()`,\n// `toast.update()` and `toast.close()`.\nconst toast = createToastManager()\n\n// The toasts and toast methods of the nearest Toaster/ToastProvider, for use inside React components.\nfunction useToastManager<Data extends object = object>(): UseToastManagerReturnValue<Data> {\n\tconst manager = ToastPrimitive.useToastManager<Data>()\n\treturn React.useMemo(() => withManagerDefaults(manager), [manager])\n}\n\nfunction ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {\n\treturn <ToastPrimitive.Provider {...props} />\n}\n\nfunction ToastPortal({ ...props }: ToastPrimitive.Portal.Props) {\n\treturn <ToastPrimitive.Portal data-slot=\"toast-portal\" {...props} />\n}\n\n// A labelled region (default \"Notifications\") that announces new toasts politely, or urgently for `priority: 'high'`.\n// F6 moves focus into it.\nfunction ToastViewport({ className, ...props }: ToastPrimitive.Viewport.Props) {\n\treturn (\n\t\t<ToastPrimitive.Viewport\n\t\t\tdata-slot=\"toast-viewport\"\n\t\t\tclassName={cn(\n\t\t\t\t'pointer-events-none fixed inset-x-4 bottom-4 z-50 mx-auto w-auto max-w-sm outline-none sm:right-4 sm:left-auto sm:mx-0 sm:w-full',\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t)\n}\n\nfunction Toast({ className, ...props }: ToastPrimitive.Root.Props) {\n\treturn (\n\t\t<ToastPrimitive.Root\n\t\t\tdata-slot=\"toast\"\n\t\t\tclassName={cn(\n\t\t\t\t'group/toast pointer-events-auto absolute right-0 bottom-0 z-[calc(1000-var(--toast-index))] w-full origin-bottom rounded-lg border bg-popover text-sm text-foreground shadow-lg will-change-transform outline-none select-none focus-visible:ring-3 focus-visible:ring-ring',\n\t\t\t\t// Typed toasts: a tinted background, a 3px coloured left edge and a coloured icon. Text stays `text-foreground`.\n\t\t\t\t// Info uses `primary`, success and warning their own tokens, and error `destructive` mixed 30% towards\n\t\t\t\t// `foreground` so the edge and icon keep at least 3:1 against the toast in both themes and modes.\n\t\t\t\t'data-[type=info]:[--toast-accent:var(--primary)]',\n\t\t\t\t'data-[type=success]:[--toast-accent:var(--success)]',\n\t\t\t\t'data-[type=warning]:[--toast-accent:var(--warning)]',\n\t\t\t\t'data-[type=error]:[--toast-accent:color-mix(in_srgb,var(--destructive)_70%,var(--foreground))]',\n\t\t\t\t'data-[type=destructive]:[--toast-accent:color-mix(in_srgb,var(--destructive)_70%,var(--foreground))]',\n\t\t\t\t'data-[type=loading]:[--toast-accent:var(--muted-foreground)]',\n\t\t\t\t'data-type:border-l-3 data-type:border-l-[color:var(--toast-accent,var(--border))] data-type:bg-[color-mix(in_srgb,var(--toast-accent,var(--popover))_8%,var(--popover))]',\n\t\t\t\t// Stacking, expanding and swipe animations from shadcn\n\t\t\t\t'[--gap:0.75rem] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))]',\n\t\t\t\t'h-(--height) [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))] [transition:transform_500ms_cubic-bezier(0.22,1,0.36,1),opacity_500ms,height_150ms] motion-reduce:[transition:none]',\n\t\t\t\t\"after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']\",\n\t\t\t\t'data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]',\n\t\t\t\t'data-limited:opacity-0 data-starting-style:[transform:translateY(150%)]',\n\t\t\t\t'[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(150%)]',\n\t\t\t\t'data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]',\n\t\t\t\t'data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]',\n\t\t\t\t'data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]',\n\t\t\t\t'data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]',\n\t\t\t\t'data-expanded:data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]',\n\t\t\t\t'data-expanded:data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]',\n\t\t\t\t'data-expanded:data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]',\n\t\t\t\t'data-expanded:data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]',\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t)\n}\n\nfunction ToastContent({ className, ...props }: ToastPrimitive.Content.Props) {\n\treturn (\n\t\t<ToastPrimitive.Content\n\t\t\tdata-slot=\"toast-content\"\n\t\t\tclassName={cn(\n\t\t\t\t'flex h-full items-start gap-3 overflow-hidden p-4 transition-opacity duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] data-behind:opacity-0 data-expanded:opacity-100 motion-reduce:transition-none',\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t)\n}\n\nfunction ToastTitle({ className, ...props }: ToastPrimitive.Title.Props) {\n\treturn <ToastPrimitive.Title data-slot=\"toast-title\" className={cn('text-sm font-medium', className)} {...props} />\n}\n\nfunction ToastDescription({ className, ...props }: ToastPrimitive.Description.Props) {\n\treturn (\n\t\t<ToastPrimitive.Description\n\t\t\tdata-slot=\"toast-description\"\n\t\t\tclassName={cn('text-sm text-foreground', className)}\n\t\t\t{...props}\n\t\t/>\n\t)\n}\n\nfunction ToastAction({\n\tclassName,\n\trender = <Button variant=\"outline\" size=\"sm\" />,\n\t...props\n}: ToastPrimitive.Action.Props) {\n\treturn (\n\t\t<ToastPrimitive.Action\n\t\t\tdata-slot=\"toast-action\"\n\t\t\trender={render}\n\t\t\tclassName={cn('-mt-1.5 shrink-0', className)}\n\t\t\t{...props}\n\t\t/>\n\t)\n}\n\n// Icon-only close button (32px). Pass `aria-label` to translate it; Toaster sets it from `closeLabel`.\nfunction ToastClose({\n\tclassName,\n\tchildren,\n\trender = <Button variant=\"ghost\" size=\"icon-sm\" />,\n\t...props\n}: ToastPrimitive.Close.Props) {\n\treturn (\n\t\t<ToastPrimitive.Close\n\t\t\tdata-slot=\"toast-close\"\n\t\t\taria-label=\"Close notification\"\n\t\t\trender={render}\n\t\t\tclassName={cn('-mt-1.5 shrink-0 text-muted-foreground hover:text-foreground', className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children ?? <XIcon aria-hidden=\"true\" />}\n\t\t</ToastPrimitive.Close>\n\t)\n}\n\nconst toastIcons: Partial<Record<string, React.ComponentType<React.SVGProps<SVGSVGElement>>>> = {\n\tinfo: InfoIcon,\n\tsuccess: CircleCheckIcon,\n\twarning: TriangleAlertIcon,\n\terror: CircleXIcon,\n\tdestructive: CircleXIcon,\n\tloading: Loader2Icon,\n}\n\n// Decorative: the type is announced by the visually hidden type label in the title, so the icon is hidden from\n// assistive technology.\nfunction ToastIcon({ type, className, ...props }: React.ComponentProps<'span'> & { type: string | undefined }) {\n\tconst Icon = type ? toastIcons[type] : undefined\n\tif (!Icon) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<span\n\t\t\tdata-slot=\"toast-icon\"\n\t\t\taria-hidden=\"true\"\n\t\t\tclassName={cn(\n\t\t\t\t\"shrink-0 text-(--toast-accent) [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<Icon className={cn(type === 'loading' && 'animate-spin motion-reduce:animate-none')} />\n\t\t</span>\n\t)\n}\n\n// The built-in types, which editors suggest, plus any custom `type` an app gives its toasts\ntype ToastTypeLabels = Partial<Record<ToastType, string>> & Record<string, string | undefined>\n\nconst defaultTypeLabels: ToastTypeLabels = {\n\tinfo: 'Information',\n\tsuccess: 'Success',\n\twarning: 'Warning',\n\terror: 'Error',\n\tdestructive: 'Error',\n}\n\nfunction ToastList({ closeLabel, typeLabels }: { closeLabel: string; typeLabels: ToastTypeLabels }) {\n\tconst { toasts } = ToastPrimitive.useToastManager()\n\n\treturn toasts.map((toastItem) => {\n\t\tconst typeLabel = toastItem.type ? typeLabels[toastItem.type] : undefined\n\t\tconst srType = typeLabel ? <span className=\"sr-only\">{`${typeLabel}: `}</span> : null\n\t\tconst hasTitle = toastItem.title != null && toastItem.title !== ''\n\n\t\treturn (\n\t\t\t<Toast key={toastItem.id} toast={toastItem}>\n\t\t\t\t<ToastContent>\n\t\t\t\t\t<ToastIcon type={toastItem.type} />\n\t\t\t\t\t<div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n\t\t\t\t\t\t{hasTitle ? (\n\t\t\t\t\t\t\t<ToastTitle>\n\t\t\t\t\t\t\t\t{srType}\n\t\t\t\t\t\t\t\t{toastItem.title}\n\t\t\t\t\t\t\t</ToastTitle>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{!hasTitle && toastItem.description ? (\n\t\t\t\t\t\t\t<ToastDescription>\n\t\t\t\t\t\t\t\t{srType}\n\t\t\t\t\t\t\t\t{toastItem.description}\n\t\t\t\t\t\t\t</ToastDescription>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<ToastDescription />\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t<ToastAction />\n\t\t\t\t\t<ToastClose aria-label={closeLabel} />\n\t\t\t\t</ToastContent>\n\t\t\t</Toast>\n\t\t)\n\t})\n}\n\ntype ToasterProps = ToastPrimitive.Provider.Props & {\n\t/** Accessible name of the notification region. */\n\tlabel?: string\n\t/** Accessible name of each toast's close button. */\n\tcloseLabel?: string\n\t/** Visually hidden prefix per toast type, read before the title, e.g. \"Error: \". Merged with the English defaults. */\n\ttypeLabels?: ToastTypeLabels\n\t/** Class names for the viewport, e.g. to move it to the top of the screen. */\n\tclassName?: string\n}\n\n// Renders the toasts of `toastManager` (the app-wide `toast` by default). Put one in the root layout, or wrap part of the\n// app in it and call `useToastManager()` inside.\nfunction Toaster({\n\tchildren,\n\ttoastManager = toast,\n\tlabel = 'Notifications',\n\tcloseLabel = 'Close notification',\n\ttypeLabels,\n\tclassName,\n\t...props\n}: ToasterProps) {\n\tconst labels = React.useMemo(() => ({ ...defaultTypeLabels, ...typeLabels }), [typeLabels])\n\n\treturn (\n\t\t<ToastProvider toastManager={toastManager} {...props}>\n\t\t\t{children}\n\t\t\t<ToastPortal>\n\t\t\t\t<ToastViewport aria-label={label} className={className}>\n\t\t\t\t\t<ToastList closeLabel={closeLabel} typeLabels={labels} />\n\t\t\t\t</ToastViewport>\n\t\t\t</ToastPortal>\n\t\t</ToastProvider>\n\t)\n}\n\nexport {\n\tToaster,\n\tToast,\n\tToastAction,\n\tToastClose,\n\tToastContent,\n\tToastDescription,\n\tToastIcon,\n\tToastPortal,\n\tToastProvider,\n\tToastTitle,\n\tToastViewport,\n\tcreateToastManager,\n\ttoast,\n\tuseToastManager,\n\ttype ToasterProps,\n\ttype ToastType,\n\ttype ToastTypeLabels,\n}\n",
      "type": "registry:ui",
      "target": "components/ui/toast.tsx"
    }
  ],
  "meta": {
    "new": true
  },
  "type": "registry:ui"
}