toast.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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. import { cn } from "@/lib/utils"
  6. const ToastProvider = ToastPrimitives.Provider
  7. const ToastViewport = React.forwardRef<
  8. React.ElementRef<typeof ToastPrimitives.Viewport>,
  9. React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
  10. >(({ className, ...props }, ref) => (
  11. <ToastPrimitives.Viewport
  12. ref={ref}
  13. className={cn(
  14. "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]",
  15. className
  16. )}
  17. {...props}
  18. />
  19. ))
  20. ToastViewport.displayName = ToastPrimitives.Viewport.displayName
  21. const toastVariants = cva(
  22. "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",
  23. {
  24. variants: {
  25. variant: {
  26. default: "border bg-background text-foreground",
  27. destructive:
  28. "destructive group border-destructive bg-destructive text-destructive-foreground",
  29. },
  30. },
  31. defaultVariants: {
  32. variant: "default",
  33. },
  34. }
  35. )
  36. const Toast = React.forwardRef<
  37. React.ElementRef<typeof ToastPrimitives.Root>,
  38. React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
  39. VariantProps<typeof toastVariants>
  40. >(({ className, variant, ...props }, ref) => {
  41. return (
  42. <ToastPrimitives.Root
  43. ref={ref}
  44. className={cn(toastVariants({ variant }), className)}
  45. {...props}
  46. />
  47. )
  48. })
  49. Toast.displayName = ToastPrimitives.Root.displayName
  50. const ToastAction = React.forwardRef<
  51. React.ElementRef<typeof ToastPrimitives.Action>,
  52. React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
  53. >(({ className, ...props }, ref) => (
  54. <ToastPrimitives.Action
  55. ref={ref}
  56. className={cn(
  57. "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",
  58. className
  59. )}
  60. {...props}
  61. />
  62. ))
  63. ToastAction.displayName = ToastPrimitives.Action.displayName
  64. const ToastClose = React.forwardRef<
  65. React.ElementRef<typeof ToastPrimitives.Close>,
  66. React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
  67. >(({ className, ...props }, ref) => (
  68. <ToastPrimitives.Close
  69. ref={ref}
  70. className={cn(
  71. "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",
  72. className
  73. )}
  74. toast-close=""
  75. {...props}
  76. >
  77. <X className="h-4 w-4" />
  78. </ToastPrimitives.Close>
  79. ))
  80. ToastClose.displayName = ToastPrimitives.Close.displayName
  81. const ToastTitle = React.forwardRef<
  82. React.ElementRef<typeof ToastPrimitives.Title>,
  83. React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
  84. >(({ className, ...props }, ref) => (
  85. <ToastPrimitives.Title
  86. ref={ref}
  87. className={cn("text-sm font-semibold", className)}
  88. {...props}
  89. />
  90. ))
  91. ToastTitle.displayName = ToastPrimitives.Title.displayName
  92. const ToastDescription = React.forwardRef<
  93. React.ElementRef<typeof ToastPrimitives.Description>,
  94. React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
  95. >(({ className, ...props }, ref) => (
  96. <ToastPrimitives.Description
  97. ref={ref}
  98. className={cn("text-sm opacity-90", className)}
  99. {...props}
  100. />
  101. ))
  102. ToastDescription.displayName = ToastPrimitives.Description.displayName
  103. type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
  104. type ToastActionElement = React.ReactElement<typeof ToastAction>
  105. export {
  106. type ToastProps,
  107. type ToastActionElement,
  108. ToastProvider,
  109. ToastViewport,
  110. Toast,
  111. ToastTitle,
  112. ToastDescription,
  113. ToastClose,
  114. ToastAction,
  115. }