"use client"; import { type ComponentProps } from "react"; import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"; import { Button } from "./button"; import { cn } from "./cn"; /* * A Sheet is a Dialog pinned to an edge, and it is built on the same primitive * for that reason — focus capture, focus return, Escape, outside click, and * inerting the background are the modal behaviours a drawer needs too, and none * of them are re-implemented here. * * Both products already ship exactly this anatomy under exactly these names, so * the shape is theirs rather than invented: ten parts, a `side` defaulting to * right, three-quarter width capped at `sm` on wider viewports, and a close * button in the top corner. */ function Sheet(props: SheetPrimitive.Root.Props) { return ; } function SheetTrigger(props: SheetPrimitive.Trigger.Props) { return ; } function SheetPortal(props: SheetPrimitive.Portal.Props) { return ; } function SheetClose(props: SheetPrimitive.Close.Props) { return ; } function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) { return ( ); } function XIcon() { return ( ); } /* * Side styling is a lookup rather than `data-[side=…]:` variants, and that is * not a style preference. A variant-prefixed `data-[side=right]:w-3/4` and a * caller's plain `w-72` are different groups to tailwind-merge, so both survive * the merge — and the prefixed one wins on specificity, silently defeating the * override. Both products pass a width (`w-72`, `sm:max-w-md`, `w-[380px]`) and * all of them were being ignored until this became a lookup. The `data-side` * attribute stays for styling hooks and tests; it just does not carry layout. * * A full-width translate rather than a fixed nudge: the sheet leaves by the * edge it is pinned to, which is what makes it read as a drawer instead of a * dialog that fades. */ const sideClasses = { right: "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm " + "data-starting-style:translate-x-full data-ending-style:translate-x-full", left: "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm " + "data-starting-style:-translate-x-full data-ending-style:-translate-x-full", top: "inset-x-0 top-0 h-auto border-b " + "data-starting-style:-translate-y-full data-ending-style:-translate-y-full", bottom: "inset-x-0 bottom-0 h-auto border-t " + "data-starting-style:translate-y-full data-ending-style:translate-y-full", } as const; export interface SheetContentProps extends SheetPrimitive.Popup.Props { /** Edge the sheet is pinned to. @default "right" */ side?: "top" | "right" | "bottom" | "left"; /** @default true */ showCloseButton?: boolean; /** See DialogContent — retargets the portal so brand tokens are inherited. */ container?: SheetPrimitive.Portal.Props["container"]; } function SheetContent({ className, children, side = "right", showCloseButton = true, container, ...props }: SheetContentProps) { return ( {children} {showCloseButton && ( } > Close )} ); } function SheetHeader({ className, ...props }: ComponentProps<"div">) { return (
); } function SheetFooter({ className, ...props }: ComponentProps<"div">) { return (
); } function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) { return ( ); } function SheetDescription({ className, ...props }: SheetPrimitive.Description.Props) { return ( ); } export { Sheet, SheetTrigger, SheetPortal, SheetClose, SheetOverlay, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };