1"use client";
2
3import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
4import { useRef } from "react";
5
6import { cn } from "~/lib/utils";
7import {
8 DIALOG_BACKDROP_CLASS,
9 DIALOG_MOBILE_SHEET_CLASS,
10 DIALOG_POPUP_CLASS,
11} from "~/components/ui/dialog-styles";
12
13const AlertDialog = AlertDialogPrimitive.Root;
14
15const AlertDialogPortal = AlertDialogPrimitive.Portal;
16
17function AlertDialogBackdrop({ className, ...props }: AlertDialogPrimitive.Backdrop.Props) {
18 return (
19 <AlertDialogPrimitive.Backdrop
20 forceRender
21 className={cn(DIALOG_BACKDROP_CLASS, className)}
22 data-slot="alert-dialog-backdrop"
23 {...props}
24 />
25 );
26}
27
28function AlertDialogViewport({ className, ...props }: AlertDialogPrimitive.Viewport.Props) {
29 return (
30 <AlertDialogPrimitive.Viewport
31 className={cn(
32 "fixed inset-0 z-50 grid grid-rows-[1fr_auto_1fr] justify-items-center p-4",
33 className,
34 )}
35 data-slot="alert-dialog-viewport"
36 {...props}
37 />
38 );
39}
40
41function AlertDialogPopup({
42 className,
43 bottomStickOnMobile = true,
44 portalContainer,
45 initialFocus,
46 ref,
47 ...props
48}: AlertDialogPrimitive.Popup.Props & {
49 bottomStickOnMobile?: boolean;
50 portalContainer?: AlertDialogPrimitive.Portal.Props["container"];
51}) {
52 const popup = useRef<HTMLDivElement | null>(null);
53 return (
54 <AlertDialogPortal container={portalContainer}>
55 <AlertDialogBackdrop />
56 <AlertDialogViewport
57 className={cn(bottomStickOnMobile && "max-sm:grid-rows-[1fr_auto] max-sm:p-0 max-sm:pt-12")}
58 >
59 <AlertDialogPrimitive.Popup
60 ref={(element) => {
61 popup.current = element;
62 if (typeof ref === "function") return ref(element);
63 if (ref) ref.current = element;
64 }}
65 initialFocus={
66 initialFocus ??
67 (() => {
68 return (
69 popup.current?.querySelector<HTMLButtonElement>(
70 '[data-slot="alert-dialog-footer"] button:not([data-slot="alert-dialog-close"]):not(:disabled):not([aria-disabled="true"])',
71 ) ?? true
72 );
73 })
74 }
75 className={cn(
76 DIALOG_POPUP_CLASS,
77 "row-start-2 max-h-full max-w-lg text-popover-foreground",
78 bottomStickOnMobile && DIALOG_MOBILE_SHEET_CLASS,
79 className,
80 )}
81 data-slot="alert-dialog-popup"
82 {...props}
83 />
84 </AlertDialogViewport>
85 </AlertDialogPortal>
86 );
87}
88
89function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) {
90 return (
91 <div
92 className={cn(
93 "flex min-h-0 flex-col gap-2 p-6 text-center max-sm:pb-4 sm:text-left",
94 className,
95 )}
96 data-slot="alert-dialog-header"
97 {...props}
98 />
99 );
100}
101
102function AlertDialogFooter({
103 className,
104 variant = "default",
105 ...props
106}: React.ComponentProps<"div"> & {
107 variant?: "default" | "bare";
108}) {
109 return (
110 <div
111 className={cn(
112 "flex flex-col-reverse gap-2 px-6 sm:flex-row sm:justify-end sm:rounded-b-[calc(var(--radius-2xl)-1px)]",
113 variant === "default" && "border-t bg-muted/72 py-4",
114 variant === "bare" && "pb-6",
115 className,
116 )}
117 data-slot="alert-dialog-footer"
118 {...props}
119 />
120 );
121}
122
123function AlertDialogTitle({ className, ...props }: AlertDialogPrimitive.Title.Props) {
124 return (
125 <AlertDialogPrimitive.Title
126 className={cn("wrap-anywhere shrink-0 font-semibold text-xl leading-none", className)}
127 data-slot="alert-dialog-title"
128 {...props}
129 />
130 );
131}
132
133function AlertDialogDescription({ className, ...props }: AlertDialogPrimitive.Description.Props) {
134 return (
135 <AlertDialogPrimitive.Description
136 className={cn(
137 // A long description (say, the arguments of a tool call awaiting
138 // approval) scrolls in place instead of pushing the actions out of
139 // the popup, which the viewport already caps.
140 "wrap-anywhere max-h-[min(24rem,60dvh)] min-h-0 overflow-y-auto overscroll-contain text-muted-foreground text-sm select-text",
141 className,
142 )}
143 data-slot="alert-dialog-description"
144 {...props}
145 />
146 );
147}
148
149function AlertDialogClose(props: AlertDialogPrimitive.Close.Props) {
150 return <AlertDialogPrimitive.Close data-slot="alert-dialog-close" {...props} />;
151}
152
153export {
154 AlertDialog,
155 AlertDialogPortal,
156 AlertDialogBackdrop,
157 AlertDialogBackdrop as AlertDialogOverlay,
158 AlertDialogPopup,
159 AlertDialogPopup as AlertDialogContent,
160 AlertDialogHeader,
161 AlertDialogFooter,
162 AlertDialogTitle,
163 AlertDialogDescription,
164 AlertDialogClose,
165 AlertDialogViewport,
166};