Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

75 linesCodeBlameRaw
1import { X } from "lucide-react";
2import { Dialog as Primitive } from "radix-ui";
3import type { ComponentProps } from "react";
4
5import { cn } from "../../lib/cn";
6import { Button } from "./button";
7import { DialogOverlay } from "./dialog";
8
9// shadcn/ui's sheet, styled with g1t's tokens: a dialog that slides in
10// from the edge of the screen, for a panel to work in beside the page.
11
12export const Sheet = Primitive.Root;
13export const SheetTrigger = Primitive.Trigger;
14export const SheetClose = Primitive.Close;
15
16type Side = "right" | "left";
17
18const SIDES: Record<Side, string> = {
19 // Full width on a phone, a column beside the page from there up.
20 right: "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 // Clear of the notch and the home indicator on a phone.
37 "fixed z-50 flex h-dvh w-full flex-col border-line-strong bg-surface pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)] text-fg shadow-2xl shadow-black/60 outline-none motion-reduce:animate-none",
38 SIDES[side],
39 className,
40 )}
41 {...props}
42 >
43 {children}
44 {showClose && (
45 <Primitive.Close asChild>
46 <Button
47 variant="ghost"
48 size="inline"
49 aria-label="Close"
50 className="absolute top-[calc(1rem+env(safe-area-inset-top))] right-4 p-1 text-faint max-sm:top-[calc(0.75rem+env(safe-area-inset-top))] max-sm:right-3 max-sm:size-10"
51 >
52 <X size={16} />
53 </Button>
54 </Primitive.Close>
55 )}
56 </Primitive.Content>
57 </Primitive.Portal>
58 );
59}
60
61export function SheetHeader({ className, ...props }: ComponentProps<"div">) {
62 return <div className={cn("flex flex-col gap-1.5 border-b border-line px-5 py-4 pr-12", className)} {...props} />;
63}
64
65export function SheetFooter({ className, ...props }: ComponentProps<"div">) {
66 return <div className={cn("mt-auto flex items-center gap-2 border-t border-line px-5 py-3", className)} {...props} />;
67}
68
69export function SheetTitle({ className, ...props }: ComponentProps<typeof Primitive.Title>) {
70 return <Primitive.Title className={cn("text-base font-semibold tracking-tight", className)} {...props} />;
71}
72
73export function SheetDescription({ className, ...props }: ComponentProps<typeof Primitive.Description>) {
74 return <Primitive.Description className={cn("text-sm text-muted", className)} {...props} />;
75}