Sheet
Extends the Dialog component to display content that complements the main content of the screen.
Installation
Install the following dependencies:
pnpm add @radix-ui/react-dialog
Copy and paste the following code into your project.
import * as SheetPrimitive from "@radix-ui/react-dialog";
import type { VariantProps } from "tailwind-variants";
import { XIcon } from "lucide-react";
import { tv, cn } from "~/utils/tailwind";
import {
dialogDescriptionVariants,
dialogFooterVariants,
dialogHeaderVariants,
dialogOverlayVariants,
dialogTitleVariants,
} from "~/components/ui/_Dialog";
import { Button } from "~/components/ui/Button";
const Sheet = SheetPrimitive.Root;
const SheetTrigger = SheetPrimitive.Trigger;
const SheetClose = SheetPrimitive.Close;
const SheetPortal = SheetPrimitive.Portal;
function SheetOverlay({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
return (
<SheetPrimitive.Overlay
className={dialogOverlayVariants({ className })}
{...props}
/>
);
}
const sheetVariants = tv({
base: [
"fixed z-50 flex flex-col bg-background p-6 transition ease-in-out",
"data-[state=open]:animate-in",
"data-[state=closed]:animate-out",
"[&>button]:absolute [&>button]:right-2.5 [&>button]:top-2.5",
],
variants: {
side: {
top: cn(
"inset-x-0 top-0 border-b",
"data-[state=open]:slide-in-from-top",
"data-[state=closed]:slide-out-to-top",
),
bottom: cn(
"inset-x-0 bottom-0 border-t",
"data-[state=open]:slide-in-from-bottom",
"data-[state=closed]:slide-out-to-bottom",
),
left: cn(
"inset-y-0 left-0 h-full w-3/4 border-r",
"sm:max-w-96",
"data-[state=open]:slide-in-from-left",
"data-[state=closed]:slide-out-to-left",
),
right: cn(
"inset-y-0 right-0 h-full w-3/4 border-l",
"sm:max-w-96",
"data-[state=open]:slide-in-from-right",
"data-[state=closed]:slide-out-to-right",
),
},
},
defaultVariants: {
side: "right",
},
});
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
function SheetContent({
side = "right",
className,
children,
...props
}: SheetContentProps) {
return (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
<SheetPrimitive.Close asChild>
<Button variant="ghost" size="sm" subject="icon">
<XIcon />
<span className="sr-only">Close</span>
</Button>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
);
}
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
return <div className={dialogHeaderVariants({ className })} {...props} />;
}
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
return <div className={dialogFooterVariants({ className })} {...props} />;
}
function SheetTitle({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
return (
<SheetPrimitive.Title
className={dialogTitleVariants({ className })}
{...props}
/>
);
}
function SheetDescription({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
return (
<SheetPrimitive.Description
className={dialogDescriptionVariants({ className })}
{...props}
/>
);
}
export {
Sheet,
SheetPortal,
SheetOverlay,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
};
Update the import paths to match your project setup.
Usage
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "~/components/ui/Sheet";
<Sheet>
<SheetTrigger>Open</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Are you absolutely sure?</SheetTitle>
<SheetDescription>
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
</SheetDescription>
</SheetHeader>
</SheetContent>
</Sheet>
Examples
Side
Use the side
property to <SheetContent />
to indicate the edge of the screen where the component will appear. The values can be top
, right
, bottom
or left
.