1
8import * as React from "react";
9import * as RadixDialog from "@radix-ui/react-dialog";
10import { clsx } from "clsx";
11
12export const Dialog = RadixDialog.Root;
13export const DialogTrigger = RadixDialog.Trigger;
14export const DialogClose = RadixDialog.Close;
15
16export const DialogContent = React.forwardRef<
17 React.ElementRef<typeof RadixDialog.Content>,
18 React.ComponentPropsWithoutRef<typeof RadixDialog.Content>
19>(({ className, children, ...props }, ref) => (
20 <RadixDialog.Portal>
21 <RadixDialog.Overlay className="fixed inset-0 bg-black/50 data-[state=open]:animate-in" />
22 <RadixDialog.Content
23 ref={ref}
24 className={clsx(
25 "fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2",
26 "w-full max-w-lg rounded-lg bg-popover p-6 shadow-lg",
27 "text-popover-foreground focus:outline-none",
28 className,
29 )}
30 {...props}
31 >
32 {children}
33 <RadixDialog.Close className="absolute right-4 top-4 cursor-pointer text-muted-foreground hover:text-foreground">
34 ✕<span className="sr-only">Close</span>
35 </RadixDialog.Close>
36 </RadixDialog.Content>
37 </RadixDialog.Portal>
38));
39DialogContent.displayName = "DialogContent";
40
41export const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
42 <div className={clsx("flex flex-col gap-1 text-left", className)} {...props} />
43);
44
45export const DialogTitle = React.forwardRef<
46 React.ElementRef<typeof RadixDialog.Title>,
47 React.ComponentPropsWithoutRef<typeof RadixDialog.Title>
48>(({ className, ...props }, ref) => (
49 <RadixDialog.Title
50 ref={ref}
51 className={clsx("text-lg font-semibold text-foreground", className)}
52 {...props}
53 />
54));
55DialogTitle.displayName = "DialogTitle";
56
57export const DialogDescription = React.forwardRef<
58 React.ElementRef<typeof RadixDialog.Description>,
59 React.ComponentPropsWithoutRef<typeof RadixDialog.Description>
60>(({ className, ...props }, ref) => (
61 <RadixDialog.Description
62 ref={ref}
63 className={clsx("text-sm text-muted-foreground", className)}
64 {...props}
65 />
66));
67DialogDescription.displayName = "DialogDescription";
68
69export const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
70 <div className={clsx("mt-6 flex justify-end gap-2", className)} {...props} />
71);
72
73
80export function useDialogState() {
81 const [state, setState] = React.useState<"closed" | "open" | "closing">("closed");
82 const [open, setOpen] = React.useState(false);
83
84 const openDialog = React.useCallback(() => {
85 setOpen(true);
86 setState("open");
87 }, []);
88 const closeDialog = React.useCallback(() => {
89 setOpen(false);
90 setState("closing");
91 }, []);
92
93 const onAnimationEnd = React.useCallback(() => {
94 setState("closed");
95 }, []);
96
97 return { open, openDialog, closeDialog, state, onAnimationEnd } as const;
98}
99