Sheet
A sheet component that slides in from different sides with blur effects
source cached 2026-10-19
registry/preskok/ui/preskok-ui/sheet.tsx
"use client"
import { twJoin } from "cn"
import { composeRenderProps } from "react-aria-components/composeRenderProps"
import type {
DialogProps,
DialogTriggerProps,
ModalOverlayProps,
} from "react-aria-components/Modal"
import {
DialogTrigger as DialogTriggerPrimitive,
Modal,
ModalOverlay,
} from "react-aria-components/Modal"
import { tv } from "tailwind-variants"
import {
Dialog,
DialogBody,
DialogClose,
DialogCloseIcon,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "./dialog"
type Sides = "top" | "bottom" | "left" | "right"
const generateCompoundVariants = (sides: Array<Sides>) => {
return sides.map((side) => ({
side,
isFloat: true,
className:
side === "top"
? "top-2 inset-x-2 rounded-lg ring-1 border-b-0"
: side === "bottom"
? "bottom-2 inset-x-2 rounded-lg ring-1 border-t-0"
: side === "left"
? "left-2 inset-y-2 rounded-lg ring-1 border-r-0"
: "right-2 inset-y-2 rounded-lg ring-1 border-l-0",
}))
}
const sheetContentStyles = tv({
base: [
"fixed z-50 grid gap-4 border-muted-foreground/20 bg-overlay text-overlay-foreground shadow-lg dark:border-border",
"transform-gpu transition ease-in-out will-change-transform",
],
variants: {
isEntering: {
true: "animate-in duration-500 fade-in",
},
isExiting: {
true: "animate-out duration-300 fade-in",
},
side: {
top: "inset-x-0 top-0 rounded-b-2xl border-b entering:slide-in-from-top exiting:slide-out-to-top",
bottom:
"inset-x-0 bottom-0 rounded-t-2xl border-t entering:slide-in-from-bottom exiting:slide-out-to-bottom",
left: "inset-y-0 left-0 h-auto w-3/4 overflow-y-auto border-r sm:max-w-80 entering:slide-in-from-left exiting:slide-out-to-left-80",
right:
"inset-y-0 right-0 h-auto w-3/4 overflow-y-auto border-l sm:max-w-80 entering:slide-in-from-right exiting:slide-out-to-right-80",
},
isFloat: {
false: "border-foreground/20 dark:border-border",
true: "ring-foreground/5 dark:ring-border",
},
},
compoundVariants: generateCompoundVariants([
"top",
"bottom",
"left",
"right",
]),
})
type SheetProps = DialogTriggerProps
const Sheet = (props: SheetProps) => {
return <DialogTriggerPrimitive {...props} />
}
interface SheetContentProps
extends
Omit<ModalOverlayProps, "children">,
Pick<DialogProps, "aria-label" | "role" | "aria-labelledby" | "children"> {
closeButton?: boolean
isBlurred?: boolean
isFloat?: boolean
side?: Sides
overlay?: Omit<ModalOverlayProps, "children">
}
const SheetContent = ({
className,
isBlurred = false,
isDismissable: isDismissableInternal,
side = "right",
role = "dialog",
closeButton = true,
isFloat = true,
overlay,
children,
...props
}: SheetContentProps) => {
const isDismissable = isDismissableInternal ?? role !== "alertdialog"
return (
<ModalOverlay
{...overlay}
isDismissable={isDismissable}
className={composeRenderProps(
overlay?.className,
(className, { isExiting, isEntering }) =>
twJoin(
"fixed inset-0 z-50 h-(--visual-viewport-height,100vh) w-screen overflow-hidden bg-black/15",
isEntering && "fade-in animate-in duration-500",
isExiting && "fade-out animate-out duration-300",
isBlurred && "backdrop-blur-[1px] backdrop-filter",
className
)
)}
{...props}
>
<Modal
className={composeRenderProps(className, (className, renderProps) =>
sheetContentStyles({
...renderProps,
side,
isFloat,
className,
})
)}
>
<Dialog aria-label={props["aria-label"]} role={role}>
{(values) => (
<>
{typeof children === "function" ? children(values) : children}
{closeButton && (
<DialogCloseIcon
className="top-2.5 right-2.5"
isDismissable={isDismissable}
/>
)}
</>
)}
</Dialog>
</Modal>
</ModalOverlay>
)
}
const SheetTrigger = DialogTrigger
const SheetFooter = DialogFooter
const SheetHeader = DialogHeader
const SheetTitle = DialogTitle
const SheetDescription = DialogDescription
const SheetBody = DialogBody
const SheetClose = DialogClose
export {
Sheet,
SheetBody,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
}
export type { SheetContentProps, SheetProps, Sides }
- Visual style
- glass-blur
DependenciesBundle weight · light
react-aria-componentscn@^0.2.4tailwind-variants
Registry dependencies
https://ui-three-mu.vercel.app/r/dialog.json