返回 JoyAI-Echo
sheet.tsx
1 import * as React from "react";
2 import * as DialogPrimitive from "@radix-ui/react-dialog";
3 import { X } from "lucide-react";
4 import { cva, type VariantProps } from "class-variance-authority";
5
6 import { cn } from "@/lib/utils";
7
8 const Sheet = DialogPrimitive.Root;
9 const SheetTrigger = DialogPrimitive.Trigger;
10 const SheetClose = DialogPrimitive.Close;
11 const SheetPortal = DialogPrimitive.Portal;
12
13 const SheetOverlay = React.forwardRef<
14 React.ElementRef<typeof DialogPrimitive.Overlay>,
15 React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
16 >(({ className, ...props }, ref) => (
17 <DialogPrimitive.Overlay
18 ref={ref}
19 className={cn(
20 "fixed inset-0 z-50 bg-black/40 backdrop-blur-sm",
21 "data-[state=open]:animate-in data-[state=closed]:animate-out",
22 "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
23 className,
24 )}
25 {...props}
26 />
27 ));
28 SheetOverlay.displayName = DialogPrimitive.Overlay.displayName;
29
30 const sheetVariants = cva(
31 "fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out",
32 {
33 variants: {
34 side: {
35 top: cn(
36 "inset-x-0 top-0 border-b",
37 "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
38 ),
39 bottom: cn(
40 "inset-x-0 bottom-0 border-t",
41 "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
42 ),
43 left: cn(
44 "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
45 "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left",
46 ),
47 right: cn(
48 "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
49 "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right",
50 ),
51 },
52 },
53 defaultVariants: {
54 side: "right",
55 },
56 },
57 );
58
59 interface SheetContentProps
60 extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>,
61 VariantProps<typeof sheetVariants> {
62 showCloseButton?: boolean;
63 }
64
65 const SheetContent = React.forwardRef<
66 React.ElementRef<typeof DialogPrimitive.Content>,
67 SheetContentProps
68 >(({ side = "right", className, children, showCloseButton = true, ...props }, ref) => (
69 <SheetPortal>
70 <SheetOverlay />
71 <DialogPrimitive.Content
72 ref={ref}
73 className={cn(
74 sheetVariants({ side }),
75 "data-[state=open]:animate-in data-[state=closed]:animate-out",
76 "duration-300",
77 className,
78 )}
79 {...props}
80 >
81 {children}
82 {showCloseButton ? (
83 <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none">
84 <X className="h-4 w-4" />
85 <span className="sr-only">Close</span>
86 </DialogPrimitive.Close>
87 ) : null}
88 </DialogPrimitive.Content>
89 </SheetPortal>
90 ));
91 SheetContent.displayName = DialogPrimitive.Content.displayName;
92
93 const SheetHeader = ({
94 className,
95 ...props
96 }: React.HTMLAttributes<HTMLDivElement>) => (
97 <div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
98 );
99 SheetHeader.displayName = "SheetHeader";
100
101 const SheetTitle = React.forwardRef<
102 React.ElementRef<typeof DialogPrimitive.Title>,
103 React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
104 >(({ className, ...props }, ref) => (
105 <DialogPrimitive.Title
106 ref={ref}
107 className={cn("text-lg font-semibold text-foreground", className)}
108 {...props}
109 />
110 ));
111 SheetTitle.displayName = DialogPrimitive.Title.displayName;
112
113 export { Sheet, SheetTrigger, SheetClose, SheetPortal, SheetOverlay, SheetContent, SheetHeader, SheetTitle };
114
114 lines Plain Text