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