appos 0.4.3-0 → 0.5.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/dist/exports/api/index.d.mts +3 -3
  2. package/dist/exports/api/index.mjs +34 -9
  3. package/dist/exports/api/workflows/index.d.mts +2 -2
  4. package/dist/exports/api/workflows/index.mjs +2 -2
  5. package/dist/exports/{auth-By0xx0MI.d.mts → auth-BCOIpGDO.d.mts} +325 -214
  6. package/dist/exports/cli/index.d.mts +105 -106
  7. package/dist/exports/cli/index.mjs +87 -13
  8. package/dist/exports/{constants-BePPc_yF.mjs → constants-BicCnEiJ.mjs} +7 -1
  9. package/dist/exports/{index-Bpo5QE7k.d.mts → index-CiuTp085.d.mts} +580 -614
  10. package/dist/exports/{openapi-crG3j4xx.mjs → openapi-uisUTLq7.mjs} +45 -102
  11. package/dist/exports/{send-email-Bgcdjy-e.mjs → send-email-CXxlkfFL.mjs} +1 -1
  12. package/dist/exports/{server-BlNxgHUc.mjs → server-CA4aI0U6.mjs} +6 -2
  13. package/dist/exports/storybook/index.d.ts +40 -0
  14. package/dist/exports/storybook/index.js +43 -0
  15. package/dist/exports/tests/api.d.mts +2 -2
  16. package/dist/exports/tests/api.mjs +3 -3
  17. package/dist/exports/vite/index.d.mts +19 -3
  18. package/dist/exports/vite/index.mjs +82 -6
  19. package/dist/exports/web/index.d.ts +2247 -700
  20. package/dist/exports/web/index.js +2445 -754
  21. package/dist/exports/web/routes.d.mts +41 -43
  22. package/dist/exports/web/ssr.d.mts +2 -2
  23. package/package.json +48 -31
  24. package/src/storybook/.storybook/main.ts +119 -0
  25. package/src/storybook/.storybook/manager.ts +75 -0
  26. package/src/storybook/.storybook/preview.tsx +76 -0
  27. package/src/storybook/components/ColorGrid.tsx +324 -0
  28. package/src/storybook/components/IconGallery.tsx +268 -0
  29. package/src/storybook/components/SpacingScale.tsx +236 -0
  30. package/src/storybook/components/TypographyScale.tsx +277 -0
  31. package/src/storybook/components/index.ts +3 -0
  32. package/src/storybook/docs/Introduction.mdx +29 -0
  33. package/src/storybook/docs/foundations/Colors.mdx +27 -0
  34. package/src/storybook/docs/foundations/Icons.mdx +26 -0
  35. package/src/storybook/docs/foundations/Spacing.mdx +23 -0
  36. package/src/storybook/docs/foundations/Typography.mdx +39 -0
  37. package/src/storybook/index.ts +147 -0
  38. package/src/storybook/shims/void-elements.js +18 -0
  39. package/src/web/client/auth-paths.ts +148 -0
  40. package/src/web/client/auth.ts +218 -0
  41. package/src/web/client/i18n.d.ts +9 -0
  42. package/src/web/client/i18n.ts +127 -0
  43. package/src/web/client/react-router.ts +84 -0
  44. package/src/web/hooks/index.ts +1 -0
  45. package/src/web/hooks/use-mobile.ts +19 -0
  46. package/src/web/index.ts +42 -0
  47. package/src/web/lib/colors.ts +220 -0
  48. package/src/web/lib/utils.ts +52 -0
  49. package/src/web/routes/auth/_layout.tsx +5 -11
  50. package/src/web/routes.ts +101 -0
  51. package/src/web/server/context.ts +14 -0
  52. package/src/web/server/render.ts +11 -0
  53. package/src/web/ssr.ts +17 -0
  54. package/src/web/ui/accordion.stories.tsx +183 -0
  55. package/src/web/ui/accordion.tsx +134 -0
  56. package/src/web/ui/alert-dialog.stories.tsx +215 -0
  57. package/src/web/ui/alert-dialog.tsx +328 -0
  58. package/src/web/ui/alert.stories.tsx +224 -0
  59. package/src/web/ui/alert.tsx +136 -0
  60. package/src/web/ui/animated-check.stories.tsx +200 -0
  61. package/src/web/ui/animated-check.tsx +98 -0
  62. package/src/web/ui/aspect-ratio.stories.tsx +204 -0
  63. package/src/web/ui/aspect-ratio.tsx +36 -0
  64. package/src/web/ui/auth/__mocks__/auth-context.tsx +273 -0
  65. package/src/web/ui/auth/auth-card.stories.tsx +295 -0
  66. package/src/web/ui/auth/auth-card.tsx +124 -0
  67. package/src/web/ui/auth/auth-layout.stories.tsx +330 -0
  68. package/src/web/ui/auth/auth-layout.tsx +269 -0
  69. package/src/web/ui/auth/captcha-widget.stories.tsx +200 -0
  70. package/src/web/ui/auth/captcha-widget.tsx +197 -0
  71. package/src/web/ui/auth/context.tsx +87 -0
  72. package/src/web/ui/auth/email-input.stories.tsx +167 -0
  73. package/src/web/ui/auth/email-input.tsx +84 -0
  74. package/src/web/ui/auth/forms/email-otp-form.tsx +377 -0
  75. package/src/web/ui/auth/forms/forgot-password-form.tsx +240 -0
  76. package/src/web/ui/auth/forms/index.ts +40 -0
  77. package/src/web/ui/auth/forms/login-form.tsx +486 -0
  78. package/src/web/ui/auth/forms/phone-otp-form.tsx +388 -0
  79. package/src/web/ui/auth/forms/reset-password-form.tsx +331 -0
  80. package/src/web/ui/auth/forms/signup-form.tsx +499 -0
  81. package/src/web/ui/auth/forms/two-factor-form.tsx +410 -0
  82. package/src/web/ui/auth/forms/verify-email-form.tsx +373 -0
  83. package/src/web/ui/auth/hooks.ts +266 -0
  84. package/src/web/ui/auth/index.ts +31 -0
  85. package/src/web/ui/auth/magic-link-form.tsx +191 -0
  86. package/src/web/ui/auth/otp-input.stories.tsx +199 -0
  87. package/src/web/ui/auth/otp-input.tsx +157 -0
  88. package/src/web/ui/auth/passkey-button.stories.tsx +115 -0
  89. package/src/web/ui/auth/passkey-button.tsx +113 -0
  90. package/src/web/ui/auth/passkey-register-button.stories.tsx +109 -0
  91. package/src/web/ui/auth/passkey-register-button.tsx +106 -0
  92. package/src/web/ui/auth/password-input.stories.tsx +287 -0
  93. package/src/web/ui/auth/password-input.tsx +306 -0
  94. package/src/web/ui/auth/phone-input.stories.tsx +160 -0
  95. package/src/web/ui/auth/phone-input.tsx +89 -0
  96. package/src/web/ui/auth/social-buttons.stories.tsx +217 -0
  97. package/src/web/ui/auth/social-buttons.tsx +196 -0
  98. package/src/web/ui/auth/sso-button.stories.tsx +160 -0
  99. package/src/web/ui/auth/sso-button.tsx +108 -0
  100. package/src/web/ui/auth/trust-signals.stories.tsx +208 -0
  101. package/src/web/ui/auth/trust-signals.tsx +260 -0
  102. package/src/web/ui/avatar.stories.tsx +302 -0
  103. package/src/web/ui/avatar.tsx +189 -0
  104. package/src/web/ui/badge.stories.tsx +225 -0
  105. package/src/web/ui/badge.tsx +66 -0
  106. package/src/web/ui/brand.stories.tsx +266 -0
  107. package/src/web/ui/brand.tsx +162 -0
  108. package/src/web/ui/breadcrumb.stories.tsx +271 -0
  109. package/src/web/ui/breadcrumb.tsx +214 -0
  110. package/src/web/ui/button-group.stories.tsx +251 -0
  111. package/src/web/ui/button-group.tsx +87 -0
  112. package/src/web/ui/button.stories.tsx +264 -0
  113. package/src/web/ui/button.tsx +120 -0
  114. package/src/web/ui/calendar.stories.tsx +235 -0
  115. package/src/web/ui/calendar.tsx +221 -0
  116. package/src/web/ui/card.stories.tsx +262 -0
  117. package/src/web/ui/card.tsx +199 -0
  118. package/src/web/ui/carousel.stories.tsx +244 -0
  119. package/src/web/ui/carousel.tsx +241 -0
  120. package/src/web/ui/chart.stories.tsx +833 -0
  121. package/src/web/ui/chart.tsx +390 -0
  122. package/src/web/ui/checkbox.stories.tsx +208 -0
  123. package/src/web/ui/checkbox.tsx +39 -0
  124. package/src/web/ui/collapsible.stories.tsx +239 -0
  125. package/src/web/ui/collapsible.tsx +21 -0
  126. package/src/web/ui/command.stories.tsx +291 -0
  127. package/src/web/ui/command.tsx +189 -0
  128. package/src/web/ui/context-menu.stories.tsx +255 -0
  129. package/src/web/ui/context-menu.tsx +263 -0
  130. package/src/web/ui/dialog.stories.tsx +263 -0
  131. package/src/web/ui/dialog.tsx +273 -0
  132. package/src/web/ui/drawer.stories.tsx +299 -0
  133. package/src/web/ui/drawer.tsx +130 -0
  134. package/src/web/ui/dropdown-menu.stories.tsx +320 -0
  135. package/src/web/ui/dropdown-menu.tsx +263 -0
  136. package/src/web/ui/empty.stories.tsx +204 -0
  137. package/src/web/ui/empty.tsx +101 -0
  138. package/src/web/ui/error-page.stories.tsx +181 -0
  139. package/src/web/ui/error-page.tsx +136 -0
  140. package/src/web/ui/field.stories.tsx +364 -0
  141. package/src/web/ui/field.tsx +252 -0
  142. package/src/web/ui/globals.css +3 -0
  143. package/src/web/ui/hover-card.stories.tsx +261 -0
  144. package/src/web/ui/hover-card.tsx +50 -0
  145. package/src/web/ui/index.ts +55 -0
  146. package/src/web/ui/input-group.stories.tsx +290 -0
  147. package/src/web/ui/input-group.tsx +157 -0
  148. package/src/web/ui/input-otp.stories.tsx +235 -0
  149. package/src/web/ui/input-otp.tsx +86 -0
  150. package/src/web/ui/input.stories.tsx +205 -0
  151. package/src/web/ui/input.tsx +30 -0
  152. package/src/web/ui/item.stories.tsx +348 -0
  153. package/src/web/ui/item.tsx +200 -0
  154. package/src/web/ui/kbd.stories.tsx +236 -0
  155. package/src/web/ui/kbd.tsx +27 -0
  156. package/src/web/ui/label.stories.tsx +180 -0
  157. package/src/web/ui/label.tsx +32 -0
  158. package/src/web/ui/locales/en/ui.json +13 -30
  159. package/src/web/ui/locales/zh-CN/ui.json +17 -34
  160. package/src/web/ui/locales/zh-TW/ui.json +17 -34
  161. package/src/web/ui/menubar.stories.tsx +385 -0
  162. package/src/web/ui/menubar.tsx +273 -0
  163. package/src/web/ui/navigation-menu.stories.tsx +361 -0
  164. package/src/web/ui/navigation-menu.tsx +168 -0
  165. package/src/web/ui/pagination.stories.tsx +312 -0
  166. package/src/web/ui/pagination.tsx +221 -0
  167. package/src/web/ui/popover.stories.tsx +315 -0
  168. package/src/web/ui/popover.tsx +89 -0
  169. package/src/web/ui/progress.stories.tsx +239 -0
  170. package/src/web/ui/progress.tsx +128 -0
  171. package/src/web/ui/radio-group.stories.tsx +315 -0
  172. package/src/web/ui/radio-group.tsx +38 -0
  173. package/src/web/ui/resizable.stories.tsx +304 -0
  174. package/src/web/ui/resizable.tsx +53 -0
  175. package/src/web/ui/scroll-area.stories.tsx +276 -0
  176. package/src/web/ui/scroll-area.tsx +53 -0
  177. package/src/web/ui/select.stories.tsx +314 -0
  178. package/src/web/ui/select.tsx +312 -0
  179. package/src/web/ui/separator.stories.tsx +246 -0
  180. package/src/web/ui/separator.tsx +36 -0
  181. package/src/web/ui/sheet.stories.tsx +417 -0
  182. package/src/web/ui/sheet.tsx +133 -0
  183. package/src/web/ui/sidebar.stories.tsx +402 -0
  184. package/src/web/ui/sidebar.tsx +723 -0
  185. package/src/web/ui/skeleton.stories.tsx +235 -0
  186. package/src/web/ui/skeleton.tsx +26 -0
  187. package/src/web/ui/slider.stories.tsx +294 -0
  188. package/src/web/ui/slider.tsx +75 -0
  189. package/src/web/ui/sonner.stories.tsx +279 -0
  190. package/src/web/ui/sonner.tsx +46 -0
  191. package/src/web/ui/spinner.stories.tsx +204 -0
  192. package/src/web/ui/spinner.tsx +28 -0
  193. package/src/web/ui/switch.stories.tsx +285 -0
  194. package/src/web/ui/switch.tsx +43 -0
  195. package/src/web/ui/table.stories.tsx +362 -0
  196. package/src/web/ui/table.tsx +115 -0
  197. package/src/web/ui/tabs.stories.tsx +401 -0
  198. package/src/web/ui/tabs.tsx +136 -0
  199. package/src/web/ui/textarea.stories.tsx +272 -0
  200. package/src/web/ui/textarea.tsx +28 -0
  201. package/src/web/ui/toggle-group.stories.tsx +334 -0
  202. package/src/web/ui/toggle-group.tsx +88 -0
  203. package/src/web/ui/toggle.stories.tsx +264 -0
  204. package/src/web/ui/toggle.tsx +57 -0
  205. package/src/web/ui/tooltip.stories.tsx +342 -0
  206. package/src/web/ui/tooltip.tsx +116 -0
@@ -0,0 +1,263 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import {
3
+ Button,
4
+ Dialog,
5
+ DialogClose,
6
+ DialogContent,
7
+ DialogDescription,
8
+ DialogFooter,
9
+ DialogHeader,
10
+ DialogTitle,
11
+ DialogTrigger,
12
+ Input,
13
+ Label,
14
+ } from "appos/web";
15
+ import { expect, userEvent, within } from "storybook/test";
16
+
17
+ const meta = {
18
+ title: "Base/Dialog",
19
+ component: Dialog,
20
+ tags: ["autodocs"],
21
+ parameters: {
22
+ layout: "centered",
23
+ docs: {
24
+ description: {
25
+ component:
26
+ "A modal dialog component for displaying content that requires user attention. Supports headers, footers, forms, and customizable close behavior.",
27
+ },
28
+ },
29
+ },
30
+ } satisfies Meta<typeof Dialog>;
31
+
32
+ export default meta;
33
+ type Story = StoryObj<typeof meta>;
34
+
35
+ export const Default: Story = {
36
+ render: () => (
37
+ <Dialog>
38
+ <DialogTrigger render={<Button variant="outline" />}>
39
+ Edit Profile
40
+ </DialogTrigger>
41
+ <DialogContent>
42
+ <DialogHeader>
43
+ <DialogTitle>Edit Profile</DialogTitle>
44
+ <DialogDescription>
45
+ Make changes to your profile here. Click save when you&apos;re done.
46
+ </DialogDescription>
47
+ </DialogHeader>
48
+ <div className="grid gap-4 py-4">
49
+ <div className="grid grid-cols-4 items-center gap-4">
50
+ <Label htmlFor="name" className="text-right">
51
+ Name
52
+ </Label>
53
+ <Input id="name" defaultValue="John Doe" className="col-span-3" />
54
+ </div>
55
+ <div className="grid grid-cols-4 items-center gap-4">
56
+ <Label htmlFor="username" className="text-right">
57
+ Username
58
+ </Label>
59
+ <Input
60
+ id="username"
61
+ defaultValue="@johndoe"
62
+ className="col-span-3"
63
+ />
64
+ </div>
65
+ </div>
66
+ <DialogFooter>
67
+ <Button type="submit">Save changes</Button>
68
+ </DialogFooter>
69
+ </DialogContent>
70
+ </Dialog>
71
+ ),
72
+ parameters: {
73
+ docs: {
74
+ description: {
75
+ story:
76
+ "Default dialog with a form. Includes header, content area, and footer with actions.",
77
+ },
78
+ },
79
+ },
80
+ play: async ({ canvasElement }) => {
81
+ const canvas = within(canvasElement);
82
+ const trigger = canvas.getByRole("button", { name: /edit profile/i });
83
+ await userEvent.click(trigger);
84
+ await expect(
85
+ await within(document.body).findByRole("dialog"),
86
+ ).toBeInTheDocument();
87
+ },
88
+ };
89
+
90
+ export const WithCloseButton: Story = {
91
+ render: () => (
92
+ <Dialog>
93
+ <DialogTrigger render={<Button variant="outline" />}>
94
+ Open Dialog
95
+ </DialogTrigger>
96
+ <DialogContent showCloseButton>
97
+ <DialogHeader>
98
+ <DialogTitle>Dialog with Close Button</DialogTitle>
99
+ <DialogDescription>
100
+ This dialog has a close button in the top-right corner.
101
+ </DialogDescription>
102
+ </DialogHeader>
103
+ <div className="py-4">
104
+ <p>Click the X button or press Escape to close this dialog.</p>
105
+ </div>
106
+ </DialogContent>
107
+ </Dialog>
108
+ ),
109
+ parameters: {
110
+ docs: {
111
+ description: {
112
+ story:
113
+ "Dialog with an explicit close button. Use showCloseButton prop on DialogContent.",
114
+ },
115
+ },
116
+ },
117
+ };
118
+
119
+ export const WithFooterCloseButton: Story = {
120
+ render: () => (
121
+ <Dialog>
122
+ <DialogTrigger render={<Button variant="outline" />}>
123
+ View Details
124
+ </DialogTrigger>
125
+ <DialogContent>
126
+ <DialogHeader>
127
+ <DialogTitle>Order Details</DialogTitle>
128
+ <DialogDescription>
129
+ View the details of your recent order.
130
+ </DialogDescription>
131
+ </DialogHeader>
132
+ <div className="py-4">
133
+ <dl className="space-y-2 text-sm">
134
+ <div className="flex justify-between">
135
+ <dt className="text-muted-foreground">Order ID</dt>
136
+ <dd>#12345</dd>
137
+ </div>
138
+ <div className="flex justify-between">
139
+ <dt className="text-muted-foreground">Date</dt>
140
+ <dd>Jan 15, 2025</dd>
141
+ </div>
142
+ <div className="flex justify-between">
143
+ <dt className="text-muted-foreground">Total</dt>
144
+ <dd>$99.99</dd>
145
+ </div>
146
+ </dl>
147
+ </div>
148
+ <DialogFooter showCloseButton />
149
+ </DialogContent>
150
+ </Dialog>
151
+ ),
152
+ parameters: {
153
+ docs: {
154
+ description: {
155
+ story:
156
+ "Dialog with a close button in the footer. Use showCloseButton prop on DialogFooter.",
157
+ },
158
+ },
159
+ },
160
+ };
161
+
162
+ export const SimpleAlert: Story = {
163
+ render: () => (
164
+ <Dialog>
165
+ <DialogTrigger render={<Button variant="outline" />}>
166
+ Show Alert
167
+ </DialogTrigger>
168
+ <DialogContent className="sm:max-w-md">
169
+ <DialogHeader>
170
+ <DialogTitle>Are you sure?</DialogTitle>
171
+ <DialogDescription>This action cannot be undone.</DialogDescription>
172
+ </DialogHeader>
173
+ <DialogFooter className="sm:justify-between">
174
+ <DialogClose render={<Button variant="outline" />}>
175
+ Cancel
176
+ </DialogClose>
177
+ <Button variant="destructive">Delete</Button>
178
+ </DialogFooter>
179
+ </DialogContent>
180
+ </Dialog>
181
+ ),
182
+ parameters: {
183
+ docs: {
184
+ description: {
185
+ story:
186
+ "Simple alert-style dialog for confirmations. Use DialogClose for cancel actions.",
187
+ },
188
+ },
189
+ },
190
+ };
191
+
192
+ export const CustomWidth: Story = {
193
+ render: () => (
194
+ <Dialog>
195
+ <DialogTrigger render={<Button variant="outline" />}>
196
+ Wide Dialog
197
+ </DialogTrigger>
198
+ <DialogContent className="sm:max-w-2xl">
199
+ <DialogHeader>
200
+ <DialogTitle>Wide Dialog</DialogTitle>
201
+ <DialogDescription>
202
+ This dialog has a custom max-width for larger content.
203
+ </DialogDescription>
204
+ </DialogHeader>
205
+ <div className="py-4">
206
+ <p className="text-muted-foreground text-sm">
207
+ Use Tailwind classes like sm:max-w-2xl to customize the dialog
208
+ width. The default is sm:max-w-md.
209
+ </p>
210
+ </div>
211
+ </DialogContent>
212
+ </Dialog>
213
+ ),
214
+ parameters: {
215
+ docs: {
216
+ description: {
217
+ story:
218
+ "Dialog with custom width. Override max-width using Tailwind classes.",
219
+ },
220
+ },
221
+ },
222
+ };
223
+
224
+ export const ScrollableContent: Story = {
225
+ render: () => (
226
+ <Dialog>
227
+ <DialogTrigger render={<Button variant="outline" />}>
228
+ Terms of Service
229
+ </DialogTrigger>
230
+ <DialogContent className="max-h-[80vh] overflow-y-auto">
231
+ <DialogHeader>
232
+ <DialogTitle>Terms of Service</DialogTitle>
233
+ <DialogDescription>
234
+ Please read and accept our terms of service.
235
+ </DialogDescription>
236
+ </DialogHeader>
237
+ <div className="py-4 text-sm">
238
+ {Array.from({ length: 10 }).map((_, i) => (
239
+ <p key={i} className="mb-4">
240
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
241
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
242
+ enim ad minim veniam, quis nostrud exercitation ullamco laboris
243
+ nisi ut aliquip ex ea commodo consequat.
244
+ </p>
245
+ ))}
246
+ </div>
247
+ <DialogFooter>
248
+ <DialogClose render={<Button variant="outline" />}>
249
+ Decline
250
+ </DialogClose>
251
+ <Button>Accept</Button>
252
+ </DialogFooter>
253
+ </DialogContent>
254
+ </Dialog>
255
+ ),
256
+ parameters: {
257
+ docs: {
258
+ description: {
259
+ story: "Dialog with scrollable content for long text or lists.",
260
+ },
261
+ },
262
+ },
263
+ };
@@ -0,0 +1,273 @@
1
+ "use client";
2
+
3
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
4
+ import { XIcon } from "lucide-react";
5
+ import type * as React from "react";
6
+ import { cn } from "#src/web/lib/utils.ts";
7
+ import { Button } from "#src/web/ui/button.tsx";
8
+
9
+ /**
10
+ * A modal window that appears above the page content.
11
+ *
12
+ * Use for focused tasks like forms, confirmations, or displaying detailed
13
+ * information. Traps focus and prevents interaction with the background.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <Dialog>
18
+ * <DialogTrigger render={<Button />}>Open Dialog</DialogTrigger>
19
+ * <DialogContent>
20
+ * <DialogHeader>
21
+ * <DialogTitle>Edit Profile</DialogTitle>
22
+ * <DialogDescription>Make changes to your profile.</DialogDescription>
23
+ * </DialogHeader>
24
+ * <DialogFooter>
25
+ * <Button>Save changes</Button>
26
+ * </DialogFooter>
27
+ * </DialogContent>
28
+ * </Dialog>
29
+ * ```
30
+ */
31
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
32
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
33
+ }
34
+
35
+ /**
36
+ * The button that opens the dialog.
37
+ *
38
+ * Use the `render` prop to customize the trigger element while maintaining
39
+ * proper accessibility attributes.
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * <DialogTrigger render={<Button />}>Open</DialogTrigger>
44
+ * ```
45
+ */
46
+ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
47
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
48
+ }
49
+
50
+ /**
51
+ * Portals the dialog content to the end of the document body.
52
+ *
53
+ * Used internally by DialogContent. Ensures the dialog renders above
54
+ * other content regardless of DOM position.
55
+ */
56
+ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
57
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
58
+ }
59
+
60
+ /**
61
+ * A button that closes the dialog when clicked.
62
+ *
63
+ * Can be placed anywhere within the dialog content.
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * <DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
68
+ * ```
69
+ */
70
+ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
71
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
72
+ }
73
+
74
+ /**
75
+ * The backdrop overlay behind the dialog.
76
+ *
77
+ * Dims the background and prevents interaction with the page. Includes
78
+ * blur effect on supported browsers. Used internally by DialogContent.
79
+ */
80
+ function DialogOverlay({
81
+ className,
82
+ ...props
83
+ }: DialogPrimitive.Backdrop.Props) {
84
+ return (
85
+ <DialogPrimitive.Backdrop
86
+ data-slot="dialog-overlay"
87
+ className={cn(
88
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50",
89
+ className,
90
+ )}
91
+ {...props}
92
+ />
93
+ );
94
+ }
95
+
96
+ /**
97
+ * The container for the dialog content.
98
+ *
99
+ * Centers the dialog on screen with smooth open/close animations. Includes
100
+ * the portal and overlay automatically. Optionally shows a close button.
101
+ *
102
+ * @example
103
+ * ```tsx
104
+ * <DialogContent>
105
+ * <DialogHeader>...</DialogHeader>
106
+ * <DialogFooter>...</DialogFooter>
107
+ * </DialogContent>
108
+ * ```
109
+ */
110
+ function DialogContent({
111
+ className,
112
+ children,
113
+ showCloseButton = true,
114
+ ...props
115
+ }: DialogPrimitive.Popup.Props & {
116
+ showCloseButton?: boolean;
117
+ }) {
118
+ return (
119
+ <DialogPortal>
120
+ <DialogOverlay />
121
+ <DialogPrimitive.Popup
122
+ data-slot="dialog-content"
123
+ className={cn(
124
+ "bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-6 rounded-xl p-6 text-sm ring-1 duration-100 sm:max-w-md fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none",
125
+ className,
126
+ )}
127
+ {...props}
128
+ >
129
+ {children}
130
+ {showCloseButton && (
131
+ <DialogPrimitive.Close
132
+ data-slot="dialog-close"
133
+ render={
134
+ <Button
135
+ variant="ghost"
136
+ className="absolute top-4 right-4"
137
+ size="icon-sm"
138
+ />
139
+ }
140
+ >
141
+ <XIcon />
142
+ <span className="sr-only">Close</span>
143
+ </DialogPrimitive.Close>
144
+ )}
145
+ </DialogPrimitive.Popup>
146
+ </DialogPortal>
147
+ );
148
+ }
149
+
150
+ /**
151
+ * The header section containing the dialog title and description.
152
+ *
153
+ * Provides consistent spacing and layout for dialog headers.
154
+ *
155
+ * @example
156
+ * ```tsx
157
+ * <DialogHeader>
158
+ * <DialogTitle>Edit Settings</DialogTitle>
159
+ * <DialogDescription>Configure your preferences.</DialogDescription>
160
+ * </DialogHeader>
161
+ * ```
162
+ */
163
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
164
+ return (
165
+ <div
166
+ data-slot="dialog-header"
167
+ className={cn("gap-2 flex flex-col", className)}
168
+ {...props}
169
+ />
170
+ );
171
+ }
172
+
173
+ /**
174
+ * The footer section containing dialog action buttons.
175
+ *
176
+ * Arranges buttons in a responsive layout with proper spacing.
177
+ * Optionally includes a close button.
178
+ *
179
+ * @example
180
+ * ```tsx
181
+ * <DialogFooter>
182
+ * <Button variant="outline">Cancel</Button>
183
+ * <Button>Save</Button>
184
+ * </DialogFooter>
185
+ * ```
186
+ */
187
+ function DialogFooter({
188
+ className,
189
+ showCloseButton = false,
190
+ children,
191
+ ...props
192
+ }: React.ComponentProps<"div"> & {
193
+ showCloseButton?: boolean;
194
+ }) {
195
+ return (
196
+ <div
197
+ data-slot="dialog-footer"
198
+ className={cn(
199
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
200
+ className,
201
+ )}
202
+ {...props}
203
+ >
204
+ {children}
205
+ {showCloseButton && (
206
+ <DialogPrimitive.Close render={<Button variant="outline" />}>
207
+ Close
208
+ </DialogPrimitive.Close>
209
+ )}
210
+ </div>
211
+ );
212
+ }
213
+
214
+ /**
215
+ * The title text for the dialog.
216
+ *
217
+ * Displayed prominently and announced by screen readers when the dialog opens.
218
+ *
219
+ * @example
220
+ * ```tsx
221
+ * <DialogTitle>Confirm Action</DialogTitle>
222
+ * ```
223
+ */
224
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
225
+ return (
226
+ <DialogPrimitive.Title
227
+ data-slot="dialog-title"
228
+ className={cn("leading-none font-medium", className)}
229
+ {...props}
230
+ />
231
+ );
232
+ }
233
+
234
+ /**
235
+ * The descriptive text explaining the dialog purpose.
236
+ *
237
+ * Provides context about the action or content within the dialog.
238
+ *
239
+ * @example
240
+ * ```tsx
241
+ * <DialogDescription>
242
+ * This action cannot be undone. Please confirm your choice.
243
+ * </DialogDescription>
244
+ * ```
245
+ */
246
+ function DialogDescription({
247
+ className,
248
+ ...props
249
+ }: DialogPrimitive.Description.Props) {
250
+ return (
251
+ <DialogPrimitive.Description
252
+ data-slot="dialog-description"
253
+ className={cn(
254
+ "text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3",
255
+ className,
256
+ )}
257
+ {...props}
258
+ />
259
+ );
260
+ }
261
+
262
+ export {
263
+ Dialog,
264
+ DialogClose,
265
+ DialogContent,
266
+ DialogDescription,
267
+ DialogFooter,
268
+ DialogHeader,
269
+ DialogOverlay,
270
+ DialogPortal,
271
+ DialogTitle,
272
+ DialogTrigger,
273
+ };