@shadow-js/ui 0.0.0-stage → 1.0.1

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 (190) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +598 -2
  4. package/dist/cli.mjs +2281 -0
  5. package/dist/dev/cli/schema.d.ts +71 -0
  6. package/dist/dev/index.d.ts +6 -0
  7. package/dist/dev/index.js +5 -0
  8. package/dist/dev/locales/ar.d.ts +139 -0
  9. package/dist/dev/locales/ar.js +127 -0
  10. package/dist/dev/locales/ar.js.map +1 -0
  11. package/dist/dev/locales/en.d.ts +139 -0
  12. package/dist/dev/locales/en.js +127 -0
  13. package/dist/dev/locales/en.js.map +1 -0
  14. package/dist/dev/registry/utils/utils.d.ts +27 -0
  15. package/dist/dev/registry/utils/utils.js +26 -0
  16. package/dist/dev/registry/utils/utils.js.map +1 -0
  17. package/dist/dev/theme/theme-logic.d.ts +185 -0
  18. package/dist/dev/theme/theme-logic.js +183 -0
  19. package/dist/dev/theme/theme-logic.js.map +1 -0
  20. package/dist/dev/theme/theme-provider.d.ts +88 -0
  21. package/dist/dev/theme/theme-provider.js +149 -0
  22. package/dist/dev/theme/theme-provider.js.map +1 -0
  23. package/dist/prod/index.js +1 -0
  24. package/dist/prod/locales/ar.js +2 -0
  25. package/dist/prod/locales/ar.js.map +1 -0
  26. package/dist/prod/locales/en.js +2 -0
  27. package/dist/prod/locales/en.js.map +1 -0
  28. package/dist/prod/registry/utils/utils.js +2 -0
  29. package/dist/prod/registry/utils/utils.js.map +1 -0
  30. package/dist/prod/theme/theme-logic.js +2 -0
  31. package/dist/prod/theme/theme-logic.js.map +1 -0
  32. package/dist/prod/theme/theme-provider.js +2 -0
  33. package/dist/prod/theme/theme-provider.js.map +1 -0
  34. package/package.json +102 -4
  35. package/registry/accordion/accordion.tsx +210 -0
  36. package/registry/accordion/item.json +4 -0
  37. package/registry/alert/alert.tsx +200 -0
  38. package/registry/alert/item.json +4 -0
  39. package/registry/alert-dialog/alert-dialog.tsx +530 -0
  40. package/registry/alert-dialog/item.json +4 -0
  41. package/registry/aspect-ratio/aspect-ratio.tsx +71 -0
  42. package/registry/aspect-ratio/item.json +4 -0
  43. package/registry/attachment/attachment.tsx +459 -0
  44. package/registry/attachment/item.json +4 -0
  45. package/registry/avatar/avatar.tsx +385 -0
  46. package/registry/avatar/item.json +4 -0
  47. package/registry/badge/badge.tsx +108 -0
  48. package/registry/badge/item.json +4 -0
  49. package/registry/breadcrumb/breadcrumb.tsx +354 -0
  50. package/registry/breadcrumb/item.json +4 -0
  51. package/registry/bubble/bubble.tsx +260 -0
  52. package/registry/bubble/item.json +4 -0
  53. package/registry/button/button.tsx +125 -0
  54. package/registry/button/item.json +4 -0
  55. package/registry/button-group/button-group.tsx +189 -0
  56. package/registry/button-group/item.json +4 -0
  57. package/registry/calendar/calendar.tsx +348 -0
  58. package/registry/calendar/item.json +4 -0
  59. package/registry/card/card.tsx +311 -0
  60. package/registry/card/item.json +4 -0
  61. package/registry/carousel/carousel.tsx +851 -0
  62. package/registry/carousel/item.json +4 -0
  63. package/registry/chart/chart.tsx +1956 -0
  64. package/registry/chart/item.json +4 -0
  65. package/registry/chart-scale/chart-scale.ts +670 -0
  66. package/registry/chart-scale/item.json +5 -0
  67. package/registry/checkbox/checkbox.tsx +79 -0
  68. package/registry/checkbox/item.json +4 -0
  69. package/registry/code-block/code-block.tsx +470 -0
  70. package/registry/code-block/item.json +4 -0
  71. package/registry/collapsible/collapsible.tsx +117 -0
  72. package/registry/collapsible/item.json +4 -0
  73. package/registry/combobox/combobox.tsx +977 -0
  74. package/registry/combobox/item.json +4 -0
  75. package/registry/command/command.tsx +757 -0
  76. package/registry/command/item.json +4 -0
  77. package/registry/context-menu/context-menu.tsx +697 -0
  78. package/registry/context-menu/item.json +4 -0
  79. package/registry/data-table/data-table.tsx +437 -0
  80. package/registry/data-table/item.json +4 -0
  81. package/registry/date-picker/date-picker.tsx +349 -0
  82. package/registry/date-picker/item.json +4 -0
  83. package/registry/dialog/dialog.tsx +412 -0
  84. package/registry/dialog/item.json +4 -0
  85. package/registry/direction/direction.tsx +88 -0
  86. package/registry/direction/item.json +4 -0
  87. package/registry/drawer/drawer.tsx +787 -0
  88. package/registry/drawer/item.json +4 -0
  89. package/registry/dropdown-menu/dropdown-menu.tsx +692 -0
  90. package/registry/dropdown-menu/item.json +4 -0
  91. package/registry/empty/empty.tsx +240 -0
  92. package/registry/empty/item.json +4 -0
  93. package/registry/field/field.tsx +624 -0
  94. package/registry/field/item.json +4 -0
  95. package/registry/form/form.tsx +1112 -0
  96. package/registry/form/item.json +4 -0
  97. package/registry/hover-card/hover-card.tsx +144 -0
  98. package/registry/hover-card/item.json +4 -0
  99. package/registry/icons/icons.tsx +1691 -0
  100. package/registry/icons/item.json +4 -0
  101. package/registry/input/input.tsx +43 -0
  102. package/registry/input/item.json +4 -0
  103. package/registry/input-group/input-group.tsx +315 -0
  104. package/registry/input-group/item.json +4 -0
  105. package/registry/input-otp/input-otp.tsx +329 -0
  106. package/registry/input-otp/item.json +4 -0
  107. package/registry/item/item.json +4 -0
  108. package/registry/item/item.tsx +439 -0
  109. package/registry/kbd/item.json +4 -0
  110. package/registry/kbd/kbd.tsx +65 -0
  111. package/registry/label/item.json +4 -0
  112. package/registry/label/label.tsx +60 -0
  113. package/registry/marker/item.json +4 -0
  114. package/registry/marker/marker.tsx +175 -0
  115. package/registry/menubar/item.json +4 -0
  116. package/registry/menubar/menubar.tsx +694 -0
  117. package/registry/message/item.json +4 -0
  118. package/registry/message/message.tsx +280 -0
  119. package/registry/message-scroller/item.json +4 -0
  120. package/registry/message-scroller/message-scroller.tsx +262 -0
  121. package/registry/native-select/item.json +4 -0
  122. package/registry/native-select/native-select.tsx +113 -0
  123. package/registry/navigation-menu/item.json +4 -0
  124. package/registry/navigation-menu/navigation-menu.tsx +362 -0
  125. package/registry/pagination/item.json +4 -0
  126. package/registry/pagination/pagination.tsx +460 -0
  127. package/registry/password-input/item.json +5 -0
  128. package/registry/password-input/password-input.tsx +494 -0
  129. package/registry/popover/item.json +4 -0
  130. package/registry/popover/popover.tsx +284 -0
  131. package/registry/progress/item.json +4 -0
  132. package/registry/progress/progress.tsx +190 -0
  133. package/registry/questionnaire/item.json +4 -0
  134. package/registry/questionnaire/questionnaire.tsx +569 -0
  135. package/registry/radio-group/item.json +4 -0
  136. package/registry/radio-group/radio-group.tsx +101 -0
  137. package/registry/resizable/item.json +4 -0
  138. package/registry/resizable/resizable.tsx +455 -0
  139. package/registry/scroll-area/item.json +4 -0
  140. package/registry/scroll-area/scroll-area.tsx +149 -0
  141. package/registry/select/item.json +4 -0
  142. package/registry/select/select.tsx +571 -0
  143. package/registry/separator/item.json +4 -0
  144. package/registry/separator/separator.tsx +52 -0
  145. package/registry/sheet/item.json +4 -0
  146. package/registry/sheet/sheet.tsx +476 -0
  147. package/registry/sidebar/item.json +4 -0
  148. package/registry/sidebar/sidebar.tsx +1870 -0
  149. package/registry/skeleton/item.json +4 -0
  150. package/registry/skeleton/skeleton.tsx +47 -0
  151. package/registry/slider/item.json +4 -0
  152. package/registry/slider/slider.tsx +149 -0
  153. package/registry/spinner/item.json +4 -0
  154. package/registry/spinner/spinner.tsx +51 -0
  155. package/registry/switch/item.json +4 -0
  156. package/registry/switch/switch.tsx +93 -0
  157. package/registry/table/item.json +4 -0
  158. package/registry/table/table.tsx +373 -0
  159. package/registry/tabs/item.json +4 -0
  160. package/registry/tabs/tabs.tsx +209 -0
  161. package/registry/textarea/item.json +4 -0
  162. package/registry/textarea/textarea.tsx +51 -0
  163. package/registry/toast/item.json +4 -0
  164. package/registry/toast/toast.tsx +949 -0
  165. package/registry/toggle/item.json +4 -0
  166. package/registry/toggle/toggle.tsx +110 -0
  167. package/registry/toggle-group/item.json +4 -0
  168. package/registry/toggle-group/toggle-group.tsx +189 -0
  169. package/registry/tooltip/item.json +4 -0
  170. package/registry/tooltip/tooltip.tsx +169 -0
  171. package/registry/trace/item.json +4 -0
  172. package/registry/trace/trace.tsx +356 -0
  173. package/registry/ui-strings/item.json +5 -0
  174. package/registry/ui-strings/ui-strings.tsx +454 -0
  175. package/registry/use-highlighted-code/item.json +5 -0
  176. package/registry/use-highlighted-code/use-highlighted-code.tsx +145 -0
  177. package/registry/use-mobile/item.json +5 -0
  178. package/registry/use-mobile/use-mobile.ts +46 -0
  179. package/registry/utils/item.json +5 -0
  180. package/registry/utils/utils.ts +38 -0
  181. package/registry.json +3274 -0
  182. package/styles/animations.css +321 -0
  183. package/styles/theme-core.css +55 -0
  184. package/styles/theme.css +12 -0
  185. package/styles/themes/gray.css +69 -0
  186. package/styles/themes/neutral.css +69 -0
  187. package/styles/themes/slate.css +69 -0
  188. package/styles/themes/stone.css +69 -0
  189. package/styles/themes/zinc.css +69 -0
  190. package/styles/typeset.css +251 -0
@@ -0,0 +1,530 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ type AlertDialogActionProps,
4
+ type AlertDialogCancelProps,
5
+ type AlertDialogContentProps,
6
+ AlertDialog as AlertDialogPrimitive,
7
+ type AlertDialogProps,
8
+ type DialogDescriptionProps,
9
+ type DialogOverlayProps,
10
+ type DialogPortalProps,
11
+ type DialogTitleProps,
12
+ type DialogTriggerProps,
13
+ type PolymorphicTarget,
14
+ } from "@shadow-js/primitives";
15
+ import type { ComponentProps, JSX } from "@shadow-js/web";
16
+ import { buttonVariants } from "@/components/ui/button";
17
+ import { type ClassValue, cn } from "@/lib/utils";
18
+
19
+ /** The props as the implementation sees them: the typed own props plus whatever the target takes. */
20
+ type Loose = {
21
+ readonly as?: PolymorphicTarget;
22
+ readonly class?: ClassValue;
23
+ readonly [prop: string]: unknown;
24
+ };
25
+
26
+ /**
27
+ * A modal dialog that interrupts the user with a message that needs a response, such as a
28
+ * confirmation before a delete. Pressing outside never dismisses it. Focus starts on
29
+ * {@link AlertDialogCancel}, and Escape closes it. Control it with `open` and `onOpenChange`, or
30
+ * start uncontrolled with `defaultOpen`.
31
+ *
32
+ * Keyboard and ARIA: Enter and Space activate the trigger. Tab and Shift+Tab move between the
33
+ * buttons inside the dialog and do not leave it while it is open. Escape closes it and returns focus
34
+ * to the trigger. The content has `role="alertdialog"` and `aria-modal="true"`. It is named by the
35
+ * {@link AlertDialogTitle} and described by the {@link AlertDialogDescription}. The trigger has
36
+ * `aria-haspopup="dialog"`, `aria-expanded` and `aria-controls`.
37
+ *
38
+ * Compared with shadcn/ui: `as` replaces `asChild` on {@link AlertDialogTrigger}, `class` replaces
39
+ * `className`, and every part carries `data-slot`.
40
+ *
41
+ * @param props - The root props: `open`, `onOpenChange`, `defaultOpen` and the children.
42
+ * @returns The alert dialog root.
43
+ * @example
44
+ * ```tsx
45
+ * import { Button } from "@/components/ui/button";
46
+ * import {
47
+ * AlertDialog,
48
+ * AlertDialogAction,
49
+ * AlertDialogCancel,
50
+ * AlertDialogContent,
51
+ * AlertDialogDescription,
52
+ * AlertDialogFooter,
53
+ * AlertDialogHeader,
54
+ * AlertDialogTitle,
55
+ * AlertDialogTrigger,
56
+ * } from "@/components/ui/alert-dialog";
57
+ *
58
+ * export function DeleteAccount() {
59
+ * const deleteAccount = (): void => {
60
+ * console.log("account deleted");
61
+ * };
62
+ * return (
63
+ * <AlertDialog>
64
+ * <AlertDialogTrigger as={Button} variant="outline">
65
+ * Delete account
66
+ * </AlertDialogTrigger>
67
+ * <AlertDialogContent>
68
+ * <AlertDialogHeader>
69
+ * <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
70
+ * <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
71
+ * </AlertDialogHeader>
72
+ * <AlertDialogFooter>
73
+ * <AlertDialogCancel>Cancel</AlertDialogCancel>
74
+ * <AlertDialogAction onClick={deleteAccount}>Continue</AlertDialogAction>
75
+ * </AlertDialogFooter>
76
+ * </AlertDialogContent>
77
+ * </AlertDialog>
78
+ * );
79
+ * }
80
+ * ```
81
+ * @since 1.0.0
82
+ * @see {@link AlertDialogTrigger}
83
+ * @see {@link AlertDialogContent}
84
+ * @see {@link AlertDialogCancel}
85
+ * @see {@link AlertDialogAction}
86
+ */
87
+ function AlertDialog(props: AlertDialogProps): JSX.Element {
88
+ return <AlertDialogPrimitive {...props} />;
89
+ }
90
+
91
+ /**
92
+ * The button that opens the alert dialog. It renders a native `button` by default. Use `as` to
93
+ * render your own button component, such as {@link Button}.
94
+ *
95
+ * Keyboard and ARIA: Enter and Space activate it. It carries `aria-haspopup="dialog"`,
96
+ * `aria-expanded` and `aria-controls`, which name the open dialog. Give it visible text, because
97
+ * that text is its accessible name.
98
+ *
99
+ * @param props - The trigger props: `as`, `children`, and the attributes of the element.
100
+ * @returns The trigger element.
101
+ * @example
102
+ * ```tsx
103
+ * import { Button } from "@/components/ui/button";
104
+ * import {
105
+ * AlertDialog,
106
+ * AlertDialogContent,
107
+ * AlertDialogTrigger,
108
+ * } from "@/components/ui/alert-dialog";
109
+ *
110
+ * export function OpenDialog() {
111
+ * return (
112
+ * <AlertDialog>
113
+ * <AlertDialogTrigger as={Button} variant="destructive">
114
+ * Delete
115
+ * </AlertDialogTrigger>
116
+ * <AlertDialogContent>Are you sure?</AlertDialogContent>
117
+ * </AlertDialog>
118
+ * );
119
+ * }
120
+ * ```
121
+ * @since 1.0.0
122
+ * @see {@link AlertDialog}
123
+ */
124
+ function AlertDialogTrigger<T extends PolymorphicTarget = "button">(
125
+ props: DialogTriggerProps<T>,
126
+ ): JSX.Element {
127
+ return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />;
128
+ }
129
+
130
+ /**
131
+ * Renders its children into `document.body` while the alert dialog is open, so the dialog sits
132
+ * above the page. It is server-rendered while open, and it keeps the content mounted until its exit
133
+ * animation ends.
134
+ *
135
+ * @param props - The portal props: `container` (default `document.body`) and the children.
136
+ * @returns The portal, or nothing while the dialog is closed.
137
+ * @example
138
+ * ```tsx
139
+ * import { AlertDialogOverlay, AlertDialogPortal } from "@/components/ui/alert-dialog";
140
+ *
141
+ * export function Layer() {
142
+ * return (
143
+ * <AlertDialogPortal>
144
+ * <AlertDialogOverlay />
145
+ * </AlertDialogPortal>
146
+ * );
147
+ * }
148
+ * ```
149
+ * @since 1.0.0
150
+ * @see {@link AlertDialogContent}
151
+ */
152
+ function AlertDialogPortal(props: DialogPortalProps): JSX.Element {
153
+ return <AlertDialogPrimitive.Portal {...props} />;
154
+ }
155
+
156
+ /**
157
+ * The dimmed backdrop behind the alert dialog. {@link AlertDialogContent} renders it for you. A
158
+ * click on it does not dismiss the alert dialog.
159
+ *
160
+ * @param props - The overlay props: `class` and the `div` attributes.
161
+ * @returns The overlay element.
162
+ * @example
163
+ * ```tsx
164
+ * import { AlertDialogOverlay } from "@/components/ui/alert-dialog";
165
+ *
166
+ * export function DarkBackdrop() {
167
+ * return <AlertDialogOverlay class="bg-black/70" />;
168
+ * }
169
+ * ```
170
+ * @since 1.0.0
171
+ * @see {@link AlertDialogContent}
172
+ */
173
+ function AlertDialogOverlay(props: DialogOverlayProps): JSX.Element {
174
+ const [local, rest] = splitProps(props, ["class"]);
175
+ return (
176
+ <AlertDialogPrimitive.Overlay
177
+ data-slot="alert-dialog-overlay"
178
+ class={cn(
179
+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
180
+ local.class,
181
+ )}
182
+ {...rest}
183
+ />
184
+ );
185
+ }
186
+
187
+ /**
188
+ * The alert dialog panel. It renders the portal and the overlay itself, then the centered panel
189
+ * with `role="alertdialog"`. Put the {@link AlertDialogHeader}, the {@link AlertDialogFooter} and
190
+ * your body text inside it.
191
+ *
192
+ * Keyboard and ARIA: focus starts on {@link AlertDialogCancel}. Escape closes the dialog, and a
193
+ * press outside does not. The panel has `role="alertdialog"` and `aria-modal="true"`, and it is
194
+ * named by the title and described by the description.
195
+ *
196
+ * @param props - The content props: `class`, `children`, and the attributes of the panel.
197
+ * @returns The portal with the overlay and the panel.
198
+ * @example
199
+ * ```tsx
200
+ * import {
201
+ * AlertDialog,
202
+ * AlertDialogCancel,
203
+ * AlertDialogContent,
204
+ * AlertDialogFooter,
205
+ * AlertDialogHeader,
206
+ * AlertDialogTitle,
207
+ * AlertDialogTrigger,
208
+ * } from "@/components/ui/alert-dialog";
209
+ *
210
+ * export function Confirm() {
211
+ * return (
212
+ * <AlertDialog>
213
+ * <AlertDialogTrigger>Leave</AlertDialogTrigger>
214
+ * <AlertDialogContent>
215
+ * <AlertDialogHeader>
216
+ * <AlertDialogTitle>Discard changes?</AlertDialogTitle>
217
+ * </AlertDialogHeader>
218
+ * <AlertDialogFooter>
219
+ * <AlertDialogCancel>Keep editing</AlertDialogCancel>
220
+ * </AlertDialogFooter>
221
+ * </AlertDialogContent>
222
+ * </AlertDialog>
223
+ * );
224
+ * }
225
+ * ```
226
+ * @since 1.0.0
227
+ * @see {@link AlertDialog}
228
+ * @see {@link AlertDialogOverlay}
229
+ */
230
+ function AlertDialogContent(
231
+ props: AlertDialogContentProps & { readonly size?: "default" | "sm" | undefined },
232
+ ): JSX.Element {
233
+ const [local, rest] = splitProps(props, ["class", "size"]);
234
+ return (
235
+ <AlertDialogPortal>
236
+ <AlertDialogOverlay />
237
+ <AlertDialogPrimitive.Content
238
+ data-slot="alert-dialog-content"
239
+ data-size={local.size ?? "default"}
240
+ class={cn(
241
+ "fixed start-1/2 top-1/2 z-50 grid max-h-[calc(100%-2rem)] w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg data-[size=sm]:max-w-xs rtl:translate-x-1/2",
242
+ local.class,
243
+ )}
244
+ {...rest}
245
+ />
246
+ </AlertDialogPortal>
247
+ );
248
+ }
249
+
250
+ /**
251
+ * An icon or picture at the top of an alert dialog, on a muted tile. Put it first in the
252
+ * {@link AlertDialogHeader}.
253
+ *
254
+ * Keyboard and ARIA: it is decorative and has no role; name the dialog with its title.
255
+ *
256
+ * @param props - The media props: `children` and the `div` attributes.
257
+ * @returns The media element.
258
+ * @example
259
+ * ```tsx
260
+ * import { AlertDialogHeader, AlertDialogMedia, AlertDialogTitle } from "@/components/ui/alert-dialog";
261
+ * import { TriangleAlertIcon } from "@/components/ui/icons";
262
+ *
263
+ * export function Header() {
264
+ * return (
265
+ * <AlertDialogHeader>
266
+ * <AlertDialogMedia>
267
+ * <TriangleAlertIcon />
268
+ * </AlertDialogMedia>
269
+ * <AlertDialogTitle>Delete?</AlertDialogTitle>
270
+ * </AlertDialogHeader>
271
+ * );
272
+ * }
273
+ * ```
274
+ * @since 1.0.0
275
+ * @see {@link AlertDialogContent}
276
+ */
277
+ function AlertDialogMedia(props: ComponentProps<"div">): JSX.Element {
278
+ const [local, rest] = splitProps(props, ["class", "children"]);
279
+ return (
280
+ <div
281
+ data-slot="alert-dialog-media"
282
+ class={cn(
283
+ "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted [&_svg:not([class*='size-'])]:size-6",
284
+ local.class,
285
+ )}
286
+ {...rest}
287
+ >
288
+ {local.children}
289
+ </div>
290
+ );
291
+ }
292
+
293
+ /**
294
+ * The layout of the title and description at the top of the alert dialog. Text is centered on
295
+ * small screens and aligned to the start from the `sm` breakpoint.
296
+ *
297
+ * @param props - The header props: `class` and the `div` attributes.
298
+ * @returns The header element.
299
+ * @example
300
+ * ```tsx
301
+ * import {
302
+ * AlertDialogDescription,
303
+ * AlertDialogHeader,
304
+ * AlertDialogTitle,
305
+ * } from "@/components/ui/alert-dialog";
306
+ *
307
+ * export function Heading() {
308
+ * return (
309
+ * <AlertDialogHeader>
310
+ * <AlertDialogTitle>Are you sure?</AlertDialogTitle>
311
+ * <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
312
+ * </AlertDialogHeader>
313
+ * );
314
+ * }
315
+ * ```
316
+ * @since 1.0.0
317
+ * @see {@link AlertDialogContent}
318
+ */
319
+ function AlertDialogHeader(props: ComponentProps<"div">): JSX.Element {
320
+ const [local, rest] = splitProps(props, ["class"]);
321
+ return (
322
+ <div
323
+ data-slot="alert-dialog-header"
324
+ class={cn("flex flex-col gap-2 text-center sm:text-start", local.class)}
325
+ {...rest}
326
+ />
327
+ );
328
+ }
329
+
330
+ /**
331
+ * The layout of the cancel and action buttons at the bottom of the alert dialog. The buttons stack
332
+ * on small screens and sit in a row, aligned to the end, from the `sm` breakpoint.
333
+ *
334
+ * @param props - The footer props: `class` and the `div` attributes.
335
+ * @returns The footer element.
336
+ * @example
337
+ * ```tsx
338
+ * import {
339
+ * AlertDialogAction,
340
+ * AlertDialogCancel,
341
+ * AlertDialogFooter,
342
+ * } from "@/components/ui/alert-dialog";
343
+ *
344
+ * export function Buttons() {
345
+ * return (
346
+ * <AlertDialogFooter>
347
+ * <AlertDialogCancel>Cancel</AlertDialogCancel>
348
+ * <AlertDialogAction>Continue</AlertDialogAction>
349
+ * </AlertDialogFooter>
350
+ * );
351
+ * }
352
+ * ```
353
+ * @since 1.0.0
354
+ * @see {@link AlertDialogContent}
355
+ */
356
+ function AlertDialogFooter(props: ComponentProps<"div">): JSX.Element {
357
+ const [local, rest] = splitProps(props, ["class"]);
358
+ return (
359
+ <div
360
+ data-slot="alert-dialog-footer"
361
+ class={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", local.class)}
362
+ {...rest}
363
+ />
364
+ );
365
+ }
366
+
367
+ /**
368
+ * The title of the alert dialog. It renders an `h2`, and the dialog is named by it.
369
+ *
370
+ * Keyboard and ARIA: no keys. The dialog's `aria-labelledby` points at this heading, so the
371
+ * screen reader announces the dialog by this text.
372
+ *
373
+ * @param props - The title props: `class`, `children`, and the `h2` attributes.
374
+ * @returns The title element.
375
+ * @example
376
+ * ```tsx
377
+ * import { AlertDialogTitle } from "@/components/ui/alert-dialog";
378
+ *
379
+ * export function Title() {
380
+ * return <AlertDialogTitle>Are you sure?</AlertDialogTitle>;
381
+ * }
382
+ * ```
383
+ * @since 1.0.0
384
+ * @see {@link AlertDialogDescription}
385
+ */
386
+ function AlertDialogTitle(props: DialogTitleProps): JSX.Element {
387
+ const [local, rest] = splitProps(props, ["class"]);
388
+ return (
389
+ <AlertDialogPrimitive.Title
390
+ data-slot="alert-dialog-title"
391
+ class={cn("text-lg font-semibold", local.class)}
392
+ {...rest}
393
+ />
394
+ );
395
+ }
396
+
397
+ /**
398
+ * The description of the alert dialog. It renders a `p`, and the dialog is described by it.
399
+ *
400
+ * Keyboard and ARIA: no keys. The dialog's `aria-describedby` points at this paragraph.
401
+ *
402
+ * @param props - The description props: `class`, `children`, and the `p` attributes.
403
+ * @returns The description element.
404
+ * @example
405
+ * ```tsx
406
+ * import { AlertDialogDescription } from "@/components/ui/alert-dialog";
407
+ *
408
+ * export function Description() {
409
+ * return <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>;
410
+ * }
411
+ * ```
412
+ * @since 1.0.0
413
+ * @see {@link AlertDialogTitle}
414
+ */
415
+ function AlertDialogDescription(props: DialogDescriptionProps): JSX.Element {
416
+ const [local, rest] = splitProps(props, ["class"]);
417
+ return (
418
+ <AlertDialogPrimitive.Description
419
+ data-slot="alert-dialog-description"
420
+ class={cn("text-sm text-muted-foreground", local.class)}
421
+ {...rest}
422
+ />
423
+ );
424
+ }
425
+
426
+ /**
427
+ * The confirming action. It is styled like a default button. Pressing it closes the dialog, so run
428
+ * the action itself from `onClick`.
429
+ *
430
+ * Keyboard and ARIA: a native button. Enter and Space activate it.
431
+ *
432
+ * @param props - The action props: `onClick`, `children`, and the attributes of the button.
433
+ * @returns The action element.
434
+ * @example
435
+ * ```tsx
436
+ * import {
437
+ * AlertDialog,
438
+ * AlertDialogAction,
439
+ * AlertDialogContent,
440
+ * AlertDialogTrigger,
441
+ * } from "@/components/ui/alert-dialog";
442
+ *
443
+ * export function Remove() {
444
+ * const remove = (): void => {
445
+ * console.log("removed");
446
+ * };
447
+ * return (
448
+ * <AlertDialog>
449
+ * <AlertDialogTrigger>Remove</AlertDialogTrigger>
450
+ * <AlertDialogContent>
451
+ * <AlertDialogAction onClick={remove}>Delete</AlertDialogAction>
452
+ * </AlertDialogContent>
453
+ * </AlertDialog>
454
+ * );
455
+ * }
456
+ * ```
457
+ * @since 1.0.0
458
+ * @see {@link AlertDialogCancel}
459
+ */
460
+ function AlertDialogAction<T extends PolymorphicTarget = "button">(
461
+ props: AlertDialogActionProps<T>,
462
+ ): JSX.Element {
463
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
464
+ return (
465
+ <AlertDialogPrimitive.Action
466
+ data-slot="alert-dialog-action"
467
+ class={cn(buttonVariants(), local.class)}
468
+ {...rest}
469
+ />
470
+ );
471
+ }
472
+
473
+ /**
474
+ * Closes the dialog without taking the action. It is styled like an outline button, and it receives
475
+ * the initial focus, so Enter does not confirm a destructive action by accident.
476
+ *
477
+ * Keyboard and ARIA: a native button. Enter and Space activate it.
478
+ *
479
+ * @param props - The cancel props: `children`, `class`, and the attributes of the button.
480
+ * @returns The cancel element.
481
+ * @example
482
+ * ```tsx
483
+ * import {
484
+ * AlertDialog,
485
+ * AlertDialogCancel,
486
+ * AlertDialogContent,
487
+ * AlertDialogTrigger,
488
+ * } from "@/components/ui/alert-dialog";
489
+ *
490
+ * export function KeepAccount() {
491
+ * return (
492
+ * <AlertDialog>
493
+ * <AlertDialogTrigger>Delete</AlertDialogTrigger>
494
+ * <AlertDialogContent>
495
+ * <AlertDialogCancel>Keep my account</AlertDialogCancel>
496
+ * </AlertDialogContent>
497
+ * </AlertDialog>
498
+ * );
499
+ * }
500
+ * ```
501
+ * @since 1.0.0
502
+ * @see {@link AlertDialogAction}
503
+ */
504
+ function AlertDialogCancel<T extends PolymorphicTarget = "button">(
505
+ props: AlertDialogCancelProps<T>,
506
+ ): JSX.Element {
507
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
508
+ return (
509
+ <AlertDialogPrimitive.Cancel
510
+ data-slot="alert-dialog-cancel"
511
+ class={cn(buttonVariants({ variant: "outline" }), local.class)}
512
+ {...rest}
513
+ />
514
+ );
515
+ }
516
+
517
+ export {
518
+ AlertDialog,
519
+ AlertDialogAction,
520
+ AlertDialogCancel,
521
+ AlertDialogContent,
522
+ AlertDialogDescription,
523
+ AlertDialogFooter,
524
+ AlertDialogHeader,
525
+ AlertDialogMedia,
526
+ AlertDialogOverlay,
527
+ AlertDialogPortal,
528
+ AlertDialogTitle,
529
+ AlertDialogTrigger,
530
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Alert Dialog",
3
+ "description": "A modal dialog that interrupts the user with important content and expects a response."
4
+ }
@@ -0,0 +1,71 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import { mergeElementProps } from "@shadow-js/primitives";
3
+ import type { ComponentProps, JSX } from "@shadow-js/web";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ /**
7
+ * Own props of {@link AspectRatio}. The rest of its props are the `div` attributes.
8
+ * @since 1.0.0
9
+ * @see {@link AspectRatio}
10
+ */
11
+ export interface AspectRatioExtraProps {
12
+ /**
13
+ * Width divided by height, for example `16 / 9`. It is written to the CSS `aspect-ratio`
14
+ * property.
15
+ * @default 1
16
+ */
17
+ readonly ratio?: number | undefined;
18
+ }
19
+
20
+ /**
21
+ * Props of {@link AspectRatio}: the attributes of a `div` plus `ratio`.
22
+ * @since 1.0.0
23
+ * @see {@link AspectRatio}
24
+ */
25
+ export type AspectRatioProps = ComponentProps<"div"> & AspectRatioExtraProps;
26
+
27
+ /**
28
+ * Keeps its content at a fixed width-to-height ratio, using the CSS `aspect-ratio` property. Make
29
+ * the child fill the box, for example with `class="size-full object-cover"` on an image.
30
+ *
31
+ * Keyboard and ARIA: the box is not interactive and has no role. It adds no keys and no ARIA
32
+ * attributes. Give an image inside it an `alt` text.
33
+ *
34
+ * Compared with shadcn/ui: `class` replaces `className`, and the element carries `data-slot`. A
35
+ * `style` you pass is merged with the ratio, not replaced.
36
+ *
37
+ * @param props - The box props: `ratio`, `children`, and the `div` attributes.
38
+ * @returns The box element.
39
+ * @example
40
+ * ```tsx
41
+ * import { AspectRatio } from "@/components/ui/aspect-ratio";
42
+ *
43
+ * export function Cover() {
44
+ * return (
45
+ * <AspectRatio ratio={16 / 9}>
46
+ * <img src="/cover.jpg" alt="Cover" class="size-full object-cover" />
47
+ * </AspectRatio>
48
+ * );
49
+ * }
50
+ * ```
51
+ * @since 1.0.0
52
+ * @see {@link AspectRatioProps}
53
+ */
54
+ function AspectRatio(props: AspectRatioProps): JSX.Element {
55
+ const [local, rest] = splitProps(props, ["class", "ratio", "children"]);
56
+ const merged = mergeElementProps(
57
+ {
58
+ get style() {
59
+ return { "aspect-ratio": String(local.ratio ?? 1) };
60
+ },
61
+ },
62
+ rest,
63
+ );
64
+ return (
65
+ <div data-slot="aspect-ratio" class={cn("relative w-full", local.class)} {...merged}>
66
+ {local.children}
67
+ </div>
68
+ );
69
+ }
70
+
71
+ export { AspectRatio };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Aspect Ratio",
3
+ "description": "Displays content within a desired ratio."
4
+ }