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.

g1t.sh
87 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";
7
8// shadcn/ui's dialog, styled with g1t's tokens.
9
10export const Dialog = Primitive.Root;
11export const DialogTrigger = Primitive.Trigger;
12export const DialogClose = Primitive.Close;
13export const DialogPortal = Primitive.Portal;
14
15export function DialogOverlay({ className, ...props }: ComponentProps<typeof Primitive.Overlay>) {
16 return (
17 <Primitive.Overlay
18 className={cn(
19 "fixed inset-0 z-50 bg-black/60 backdrop-blur-[2px]",
20 "data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out motion-reduce:animate-none",
21 className,
22 )}
23 {...props}
24 />
25 );
26}
27
28/**
29 * The panel, centred. On a phone (below 640px) it is a sheet from the
30 * bottom instead, the full width, clear of the home indicator and sitting
31 * on the keyboard while it is up (`--keyboard-inset`, components/mobile.tsx).
32 */
33export const DIALOG_PANEL =
34 "fixed top-1/2 left-1/2 z-50 grid max-h-[calc(100dvh-2rem)] w-[calc(100vw-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto overscroll-contain rounded-xl border border-line-strong bg-surface p-6 text-fg shadow-2xl shadow-black/60 outline-none data-[state=open]:animate-dialog-in data-[state=closed]:animate-dialog-out motion-reduce:animate-none " +
35 "max-sm:top-auto max-sm:bottom-(--keyboard-inset,0px) max-sm:left-0 max-sm:w-full max-sm:max-w-none max-sm:translate-x-0 max-sm:translate-y-0 max-sm:max-h-[calc(var(--vv-height,100dvh)-env(safe-area-inset-top)-0.5rem)] max-sm:rounded-b-none max-sm:border-x-0 max-sm:border-b-0 max-sm:pb-[calc(1.5rem+env(safe-area-inset-bottom))] max-sm:data-[state=open]:animate-[sheet-in-bottom_0.24s_cubic-bezier(0.16,1,0.3,1)] max-sm:data-[state=closed]:animate-fade-out";
36
37/**
38 * `split`: a wide panel with no padding of its own, for a dialog in two
39 * columns (a rail and a pane, as Getting started is) whose parts scroll on
40 * their own. Its height is fixed on a computer so moving between steps
41 * does not make it jump; on a phone it is the sheet, as tall as it needs.
42 */
43const SIZES = {
44 default: "",
45 split:
46 "max-w-4xl gap-0 p-0 sm:h-[min(42rem,calc(100dvh-4rem))] sm:grid-cols-[16rem_minmax(0,1fr)] sm:grid-rows-[minmax(0,1fr)] sm:overflow-hidden max-sm:grid-cols-[minmax(0,1fr)] max-sm:pb-[env(safe-area-inset-bottom)]",
47} as const;
48
49export function DialogContent({
50 className,
51 children,
52 showClose = true,
53 size = "default",
54 ...props
55}: ComponentProps<typeof Primitive.Content> & { showClose?: boolean; size?: keyof typeof SIZES }) {
56 return (
57 <Primitive.Portal>
58 <DialogOverlay />
59 <Primitive.Content className={cn(DIALOG_PANEL, SIZES[size], className)} {...props}>
60 {children}
61 {showClose && (
62 <Primitive.Close asChild>
63 <Button variant="ghost" size="inline" aria-label="Close" className="absolute top-4 right-4 p-1 text-faint max-sm:top-2.5 max-sm:right-2.5 max-sm:size-10">
64 <X size={16} />
65 </Button>
66 </Primitive.Close>
67 )}
68 </Primitive.Content>
69 </Primitive.Portal>
70 );
71}
72
73export function DialogHeader({ className, ...props }: ComponentProps<"div">) {
74 return <div className={cn("flex flex-col gap-1.5 pr-6", className)} {...props} />;
75}
76
77export function DialogFooter({ className, ...props }: ComponentProps<"div">) {
78 return <div className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)} {...props} />;
79}
80
81export function DialogTitle({ className, ...props }: ComponentProps<typeof Primitive.Title>) {
82 return <Primitive.Title className={cn("text-base font-semibold tracking-tight", className)} {...props} />;
83}
84
85export function DialogDescription({ className, ...props }: ComponentProps<typeof Primitive.Description>) {
86 return <Primitive.Description className={cn("text-sm text-muted", className)} {...props} />;
87}