toast.tsx 4.7 KB

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