Skip to content

g1t/apps/web/app/components/ui/sheet.tsx

70 lines2,597 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all1import { X } from "lucide-react";
2import { Dialog as Primitive } from "radix-ui";
3import type { ComponentProps } from "react";
4
5import { cn } from "../../lib/cn";
6import { DialogOverlay } from "./dialog";
7
8// shadcn/ui's sheet, styled with g1t's tokens: a dialog that slides in
9// from the edge of the screen, for a panel to work in beside the page.
10
11export const Sheet = Primitive.Root;
12export const SheetTrigger = Primitive.Trigger;
13export const SheetClose = Primitive.Close;
14
15type Side = "right" | "left";
16
17const SIDES: Record<Side, string> = {
18 // Full width on a phone, a column beside the page from there up.
19 right:
20 "inset-y-0 right-0 border-l data-[state=open]:animate-sheet-in-right data-[state=closed]:animate-sheet-out-right sm:max-w-md",
21 left: "inset-y-0 left-0 border-r data-[state=open]:animate-sheet-in-left data-[state=closed]:animate-sheet-out-left sm:max-w-md",
22};
23
24export function SheetContent({
25 side = "right",
26 className,
27 children,
28 showClose = true,
29 ...props
30}: ComponentProps<typeof Primitive.Content> & { side?: Side; showClose?: boolean }) {
31 return (
32 <Primitive.Portal>
33 <DialogOverlay />
34 <Primitive.Content
35 className={cn(
36 "fixed z-50 flex h-dvh w-full flex-col border-line-strong bg-surface text-fg shadow-2xl shadow-black/60 outline-none motion-reduce:animate-none",
37 SIDES[side],
38 className,
39 )}
40 {...props}
41 >
42 {children}
43 {showClose && (
44 <Primitive.Close
45 aria-label="Close"
46 className="absolute top-4 right-4 rounded-md p-1 text-faint transition-colors hover:bg-raised hover:text-fg"
47 >
48 <X size={16} />
49 </Primitive.Close>
50 )}
51 </Primitive.Content>
52 </Primitive.Portal>
53 );
54}
55
56export function SheetHeader({ className, ...props }: ComponentProps<"div">) {
57 return <div className={cn("flex flex-col gap-1.5 border-b border-line px-5 py-4 pr-12", className)} {...props} />;
58}
59
60export function SheetFooter({ className, ...props }: ComponentProps<"div">) {
61 return <div className={cn("mt-auto flex items-center gap-2 border-t border-line px-5 py-3", className)} {...props} />;
62}
63
64export function SheetTitle({ className, ...props }: ComponentProps<typeof Primitive.Title>) {
65 return <Primitive.Title className={cn("text-base font-semibold tracking-tight", className)} {...props} />;
66}
67
68export function SheetDescription({ className, ...props }: ComponentProps<typeof Primitive.Description>) {
69 return <Primitive.Description className={cn("text-sm text-muted", className)} {...props} />;
70}