/ src / components / ui / toast.tsx
toast.tsx
  1  import * as React from "react"
  2  import * as ToastPrimitives from "@radix-ui/react-toast"
  3  import { cva, type VariantProps } from "class-variance-authority"
  4  import { X } from "lucide-react"
  5  
  6  import { cn } from "@/lib/utils"
  7  
  8  const ToastProvider = ToastPrimitives.Provider
  9  
 10  const ToastViewport = React.forwardRef<
 11    React.ElementRef<typeof ToastPrimitives.Viewport>,
 12    React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
 13  >(({ className, ...props }, ref) => (
 14    <ToastPrimitives.Viewport
 15      ref={ref}
 16      className={cn(
 17        "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
 18        className
 19      )}
 20      {...props}
 21    />
 22  ))
 23  ToastViewport.displayName = ToastPrimitives.Viewport.displayName
 24  
 25  const toastVariants = cva(
 26    "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
 27    {
 28      variants: {
 29        variant: {
 30          default: "border bg-background text-foreground",
 31          destructive:
 32            "destructive group border-destructive bg-destructive text-destructive-foreground",
 33        },
 34      },
 35      defaultVariants: {
 36        variant: "default",
 37      },
 38    }
 39  )
 40  
 41  const Toast = React.forwardRef<
 42    React.ElementRef<typeof ToastPrimitives.Root>,
 43    React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
 44      VariantProps<typeof toastVariants>
 45  >(({ className, variant, ...props }, ref) => {
 46    return (
 47      <ToastPrimitives.Root
 48        ref={ref}
 49        className={cn(toastVariants({ variant }), className)}
 50        {...props}
 51      />
 52    )
 53  })
 54  Toast.displayName = ToastPrimitives.Root.displayName
 55  
 56  const ToastAction = React.forwardRef<
 57    React.ElementRef<typeof ToastPrimitives.Action>,
 58    React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
 59  >(({ className, ...props }, ref) => (
 60    <ToastPrimitives.Action
 61      ref={ref}
 62      className={cn(
 63        "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
 64        className
 65      )}
 66      {...props}
 67    />
 68  ))
 69  ToastAction.displayName = ToastPrimitives.Action.displayName
 70  
 71  const ToastClose = React.forwardRef<
 72    React.ElementRef<typeof ToastPrimitives.Close>,
 73    React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
 74  >(({ className, ...props }, ref) => (
 75    <ToastPrimitives.Close
 76      ref={ref}
 77      className={cn(
 78        "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
 79        className
 80      )}
 81      toast-close=""
 82      {...props}
 83    >
 84      <X className="h-4 w-4" />
 85    </ToastPrimitives.Close>
 86  ))
 87  ToastClose.displayName = ToastPrimitives.Close.displayName
 88  
 89  const ToastTitle = React.forwardRef<
 90    React.ElementRef<typeof ToastPrimitives.Title>,
 91    React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
 92  >(({ className, ...props }, ref) => (
 93    <ToastPrimitives.Title
 94      ref={ref}
 95      className={cn("text-sm font-semibold", className)}
 96      {...props}
 97    />
 98  ))
 99  ToastTitle.displayName = ToastPrimitives.Title.displayName
100  
101  const ToastDescription = React.forwardRef<
102    React.ElementRef<typeof ToastPrimitives.Description>,
103    React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
104  >(({ className, ...props }, ref) => (
105    <ToastPrimitives.Description
106      ref={ref}
107      className={cn("text-sm opacity-90", className)}
108      {...props}
109    />
110  ))
111  ToastDescription.displayName = ToastPrimitives.Description.displayName
112  
113  type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
114  
115  type ToastActionElement = React.ReactElement<typeof ToastAction>
116  
117  export {
118    type ToastProps,
119    type ToastActionElement,
120    ToastProvider,
121    ToastViewport,
122    Toast,
123    ToastTitle,
124    ToastDescription,
125    ToastClose,
126    ToastAction,
127  }