@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,412 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ type DialogCloseProps,
4
+ type DialogContentProps,
5
+ type DialogDescriptionProps,
6
+ type DialogOverlayProps,
7
+ type DialogPortalProps,
8
+ Dialog as DialogPrimitive,
9
+ type DialogProps,
10
+ type DialogTitleProps,
11
+ type DialogTriggerProps,
12
+ type PolymorphicTarget,
13
+ } from "@shadow-js/primitives";
14
+ import type { ComponentProps, JSX } from "@shadow-js/web";
15
+ import { XIcon } from "@/components/ui/icons";
16
+ import { useUiStrings } from "@/lib/ui-strings";
17
+ import { cn } from "@/lib/utils";
18
+
19
+ /**
20
+ * A modal window over the page, for a task that needs an answer or an edit before the page continues.
21
+ * It is controlled with `open` and `onOpenChange`, or uncontrolled with `defaultOpen`. Its parts are
22
+ * {@link DialogTrigger}, {@link DialogContent} (with the overlay and the close button) and the header,
23
+ * title and description inside it.
24
+ *
25
+ * Keyboard and ARIA: Enter or Space on the trigger opens the dialog, which moves focus into it. Tab and
26
+ * Shift+Tab cycle inside it, and Escape closes it. Closing returns focus to the trigger. The content is
27
+ * a `role="dialog"` with `aria-modal="true"`, named by {@link DialogTitle} and described by
28
+ * {@link DialogDescription}, so render both. Without a title, pass `aria-label` on the content. The
29
+ * close button is a native button named "Close". A press on the overlay closes the dialog. In
30
+ * development, a dialog with no name logs a warning.
31
+ *
32
+ * shadcn/ui differences: `as` replaces `asChild` on {@link DialogTrigger} and {@link DialogClose}, and
33
+ * the prop is `class`, not `className`. Every part has a `data-slot`. The close button is part of
34
+ * {@link DialogContent} by default; pass `showCloseButton={false}` to remove it.
35
+ *
36
+ * @param props The dialog props: see {@link DialogProps}.
37
+ * @returns The dialog. It renders no element of its own.
38
+ * @example
39
+ * import { Button } from "@/components/ui/button";
40
+ * import {
41
+ * Dialog,
42
+ * DialogContent,
43
+ * DialogDescription,
44
+ * DialogHeader,
45
+ * DialogTitle,
46
+ * DialogTrigger,
47
+ * } from "@/components/ui/dialog";
48
+ *
49
+ * export function EditProfile() {
50
+ * return (
51
+ * <Dialog>
52
+ * <DialogTrigger as={Button} variant="outline">
53
+ * Edit profile
54
+ * </DialogTrigger>
55
+ * <DialogContent>
56
+ * <DialogHeader>
57
+ * <DialogTitle>Edit profile</DialogTitle>
58
+ * <DialogDescription>Make changes to your profile here.</DialogDescription>
59
+ * </DialogHeader>
60
+ * </DialogContent>
61
+ * </Dialog>
62
+ * );
63
+ * }
64
+ * @see {@link DialogContent}
65
+ * @see {@link DialogTrigger}
66
+ * @since 1.0.0
67
+ */
68
+ function Dialog(props: DialogProps): JSX.Element {
69
+ return <DialogPrimitive {...props} />;
70
+ }
71
+
72
+ /**
73
+ * The button that opens the dialog. It is a native button by default; `as` renders another element,
74
+ * such as the shadcn `Button`. Enter and Space activate it.
75
+ *
76
+ * @param props The trigger props: the attributes of the rendered element, and `as`.
77
+ * @returns The trigger.
78
+ * @throws Error when rendered outside a {@link Dialog}.
79
+ * @example
80
+ * import { Button } from "@/components/ui/button";
81
+ * import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
82
+ *
83
+ * export function OpenDialog() {
84
+ * return (
85
+ * <Dialog>
86
+ * <DialogTrigger as={Button}>Open</DialogTrigger>
87
+ * <DialogContent>Dialog body.</DialogContent>
88
+ * </Dialog>
89
+ * );
90
+ * }
91
+ * @see {@link Dialog}
92
+ * @since 1.0.0
93
+ */
94
+ function DialogTrigger<T extends PolymorphicTarget = "button">(
95
+ props: DialogTriggerProps<T>,
96
+ ): JSX.Element {
97
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
98
+ }
99
+
100
+ /**
101
+ * Renders the overlay and the content into `document.body`, or into the `container` you pass, while
102
+ * the dialog is open. {@link DialogContent} already portals itself, so use this only to choose another
103
+ * container.
104
+ *
105
+ * @param props The portal props: `container` and `forceMount`.
106
+ * @returns The portal, or nothing while the dialog is closed.
107
+ * @throws Error when rendered outside a {@link Dialog}.
108
+ * @example
109
+ * import { Dialog, DialogContent, DialogOverlay, DialogPortal, DialogTrigger } from "@/components/ui/dialog";
110
+ *
111
+ * export function Portaled() {
112
+ * return (
113
+ * <Dialog>
114
+ * <DialogTrigger>Open</DialogTrigger>
115
+ * <DialogPortal>
116
+ * <DialogOverlay />
117
+ * <DialogContent>Dialog body.</DialogContent>
118
+ * </DialogPortal>
119
+ * </Dialog>
120
+ * );
121
+ * }
122
+ * @see {@link DialogContent}
123
+ * @since 1.0.0
124
+ */
125
+ function DialogPortal(props: DialogPortalProps): JSX.Element {
126
+ return <DialogPrimitive.Portal {...props} />;
127
+ }
128
+
129
+ /**
130
+ * A button that closes the dialog. It is a native button by default; `as` renders another element. Enter
131
+ * and Space activate it.
132
+ *
133
+ * @param props The close props: the attributes of the rendered element, and `as`.
134
+ * @returns The close button.
135
+ * @throws Error when rendered outside a {@link Dialog}.
136
+ * @example
137
+ * import { Button } from "@/components/ui/button";
138
+ * import { Dialog, DialogClose, DialogContent, DialogFooter, DialogTrigger } from "@/components/ui/dialog";
139
+ *
140
+ * export function Confirm() {
141
+ * return (
142
+ * <Dialog>
143
+ * <DialogTrigger>Delete</DialogTrigger>
144
+ * <DialogContent>
145
+ * <DialogFooter>
146
+ * <DialogClose as={Button} variant="outline">
147
+ * Cancel
148
+ * </DialogClose>
149
+ * </DialogFooter>
150
+ * </DialogContent>
151
+ * </Dialog>
152
+ * );
153
+ * }
154
+ * @see {@link Dialog}
155
+ * @since 1.0.0
156
+ */
157
+ function DialogClose<T extends PolymorphicTarget = "button">(
158
+ props: DialogCloseProps<T>,
159
+ ): JSX.Element {
160
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
161
+ }
162
+
163
+ /**
164
+ * The dimmed backdrop behind a modal dialog. It is hidden from assistive technology, and a press on it
165
+ * closes the dialog. {@link DialogContent} renders one already.
166
+ *
167
+ * @param props The overlay props: `class`, which is merged with the backdrop classes.
168
+ * @returns The overlay.
169
+ * @throws Error when rendered outside a {@link Dialog}.
170
+ * @example
171
+ * import { Dialog, DialogContent, DialogOverlay, DialogPortal, DialogTrigger } from "@/components/ui/dialog";
172
+ *
173
+ * export function Dimmed() {
174
+ * return (
175
+ * <Dialog>
176
+ * <DialogTrigger>Open</DialogTrigger>
177
+ * <DialogPortal>
178
+ * <DialogOverlay class="bg-black/70" />
179
+ * <DialogContent>Dialog body.</DialogContent>
180
+ * </DialogPortal>
181
+ * </Dialog>
182
+ * );
183
+ * }
184
+ * @see {@link DialogContent}
185
+ * @since 1.0.0
186
+ */
187
+ function DialogOverlay(props: DialogOverlayProps): JSX.Element {
188
+ const [local, rest] = splitProps(props, ["class"]);
189
+ return (
190
+ <DialogPrimitive.Overlay
191
+ data-slot="dialog-overlay"
192
+ class={cn(
193
+ "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",
194
+ local.class,
195
+ )}
196
+ {...rest}
197
+ />
198
+ );
199
+ }
200
+
201
+ /**
202
+ * Own props of {@link DialogContent}: the parts the panel adds.
203
+ * @see {@link DialogContent}
204
+ * @since 1.0.0
205
+ */
206
+ export interface DialogContentExtraProps {
207
+ /**
208
+ * Shows the close button in the top-right corner. The button is named "Close".
209
+ * @default true
210
+ */
211
+ readonly showCloseButton?: boolean | undefined;
212
+ }
213
+
214
+ /**
215
+ * The dialog panel: it renders the portal and the overlay, and the centered content with a close
216
+ * button in the corner unless `showCloseButton` is `false`. Put the {@link DialogHeader} (with
217
+ * {@link DialogTitle} and {@link DialogDescription}) and your content inside it.
218
+ *
219
+ * @param props The panel props: see {@link DialogContentProps} and {@link DialogContentExtraProps}.
220
+ * @returns The panel, rendered in a portal while the dialog is open.
221
+ * @throws Error when rendered outside a {@link Dialog}.
222
+ * @example
223
+ * import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
224
+ *
225
+ * export function Notice() {
226
+ * return (
227
+ * <Dialog>
228
+ * <DialogTrigger>Show notice</DialogTrigger>
229
+ * <DialogContent showCloseButton={false}>
230
+ * <DialogHeader>
231
+ * <DialogTitle>Saved</DialogTitle>
232
+ * <DialogDescription>Your changes are saved.</DialogDescription>
233
+ * </DialogHeader>
234
+ * </DialogContent>
235
+ * </Dialog>
236
+ * );
237
+ * }
238
+ * @see {@link Dialog}
239
+ * @see {@link DialogHeader}
240
+ * @since 1.0.0
241
+ */
242
+ function DialogContent(props: DialogContentProps & DialogContentExtraProps): JSX.Element {
243
+ const [local, rest] = splitProps(props, ["class", "children", "showCloseButton"]);
244
+ const t = useUiStrings();
245
+ return (
246
+ <DialogPortal>
247
+ <DialogOverlay />
248
+ <DialogPrimitive.Content
249
+ data-slot="dialog-content"
250
+ class={cn(
251
+ "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 rtl:translate-x-1/2",
252
+ local.class,
253
+ )}
254
+ {...rest}
255
+ >
256
+ {local.children}
257
+ {local.showCloseButton !== false && (
258
+ <DialogPrimitive.Close
259
+ data-slot="dialog-close"
260
+ class="absolute end-4 top-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 outline-none focus:outline-solid focus:outline-2 focus:outline-offset-2 focus:outline-transparent disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
261
+ >
262
+ <XIcon />
263
+ <span class="sr-only">{t("dialog.close", "Close")}</span>
264
+ </DialogPrimitive.Close>
265
+ )}
266
+ </DialogPrimitive.Content>
267
+ </DialogPortal>
268
+ );
269
+ }
270
+
271
+ /**
272
+ * The layout of the title and description at the top of a dialog. It is a plain `div` with no role.
273
+ *
274
+ * @param props The header props: the attributes of the rendered `div`.
275
+ * @returns The header.
276
+ * @example
277
+ * import { DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
278
+ *
279
+ * export function Heading() {
280
+ * return (
281
+ * <DialogHeader>
282
+ * <DialogTitle>Edit profile</DialogTitle>
283
+ * <DialogDescription>Make changes here.</DialogDescription>
284
+ * </DialogHeader>
285
+ * );
286
+ * }
287
+ * @see {@link DialogTitle}
288
+ * @since 1.0.0
289
+ */
290
+ function DialogHeader(props: ComponentProps<"div">): JSX.Element {
291
+ const [local, rest] = splitProps(props, ["class"]);
292
+ return (
293
+ <div
294
+ data-slot="dialog-header"
295
+ class={cn("flex flex-col gap-2 text-center sm:text-start", local.class)}
296
+ {...rest}
297
+ />
298
+ );
299
+ }
300
+
301
+ /**
302
+ * The layout of the action buttons at the bottom of a dialog: stacked on small screens, and aligned to
303
+ * the end on larger ones. It is a plain `div` with no role.
304
+ *
305
+ * @param props The footer props: the attributes of the rendered `div`.
306
+ * @returns The footer.
307
+ * @example
308
+ * import { Button } from "@/components/ui/button";
309
+ * import { DialogFooter } from "@/components/ui/dialog";
310
+ *
311
+ * export function Actions() {
312
+ * return (
313
+ * <DialogFooter>
314
+ * <Button>Save</Button>
315
+ * </DialogFooter>
316
+ * );
317
+ * }
318
+ * @see {@link DialogContent}
319
+ * @since 1.0.0
320
+ */
321
+ function DialogFooter(props: ComponentProps<"div">): JSX.Element {
322
+ const [local, rest] = splitProps(props, ["class"]);
323
+ return (
324
+ <div
325
+ data-slot="dialog-footer"
326
+ class={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", local.class)}
327
+ {...rest}
328
+ />
329
+ );
330
+ }
331
+
332
+ /**
333
+ * The accessible name of the dialog, and its visible heading. The dialog is labeled by it. Use
334
+ * `aria-label` on the content instead only when there is no visible heading. The title has no keys.
335
+ *
336
+ * @param props The title props: the attributes of the rendered `h2`.
337
+ * @returns The heading.
338
+ * @throws Error when rendered outside a {@link Dialog}.
339
+ * @example
340
+ * import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
341
+ *
342
+ * export function Titled() {
343
+ * return (
344
+ * <Dialog>
345
+ * <DialogTrigger>Edit profile</DialogTrigger>
346
+ * <DialogContent>
347
+ * <DialogTitle>Edit profile</DialogTitle>
348
+ * </DialogContent>
349
+ * </Dialog>
350
+ * );
351
+ * }
352
+ * @see {@link DialogDescription}
353
+ * @since 1.0.0
354
+ */
355
+ function DialogTitle(props: DialogTitleProps): JSX.Element {
356
+ const [local, rest] = splitProps(props, ["class"]);
357
+ return (
358
+ <DialogPrimitive.Title
359
+ data-slot="dialog-title"
360
+ class={cn("text-lg leading-none font-semibold", local.class)}
361
+ {...rest}
362
+ />
363
+ );
364
+ }
365
+
366
+ /**
367
+ * The accessible description of the dialog: the text a screen reader reads after its name. It is a
368
+ * plain paragraph and has no keys.
369
+ *
370
+ * @param props The description props: the attributes of the rendered `p`.
371
+ * @returns The paragraph.
372
+ * @throws Error when rendered outside a {@link Dialog}.
373
+ * @example
374
+ * import { Dialog, DialogContent, DialogDescription, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
375
+ *
376
+ * export function Described() {
377
+ * return (
378
+ * <Dialog>
379
+ * <DialogTrigger>Delete</DialogTrigger>
380
+ * <DialogContent>
381
+ * <DialogTitle>Delete account</DialogTitle>
382
+ * <DialogDescription>This cannot be undone.</DialogDescription>
383
+ * </DialogContent>
384
+ * </Dialog>
385
+ * );
386
+ * }
387
+ * @see {@link DialogTitle}
388
+ * @since 1.0.0
389
+ */
390
+ function DialogDescription(props: DialogDescriptionProps): JSX.Element {
391
+ const [local, rest] = splitProps(props, ["class"]);
392
+ return (
393
+ <DialogPrimitive.Description
394
+ data-slot="dialog-description"
395
+ class={cn("text-sm text-muted-foreground", local.class)}
396
+ {...rest}
397
+ />
398
+ );
399
+ }
400
+
401
+ export {
402
+ Dialog,
403
+ DialogClose,
404
+ DialogContent,
405
+ DialogDescription,
406
+ DialogFooter,
407
+ DialogHeader,
408
+ DialogOverlay,
409
+ DialogPortal,
410
+ DialogTitle,
411
+ DialogTrigger,
412
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Dialog",
3
+ "description": "A window overlaid on the primary window, rendering the content underneath inert."
4
+ }
@@ -0,0 +1,88 @@
1
+ import type { Accessor } from "@shadow-js/core";
2
+ import {
3
+ type Direction,
4
+ DirectionProvider as DirectionPrimitive,
5
+ useDirection as useDirectionPrimitive,
6
+ } from "@shadow-js/primitives";
7
+ import type { JSX, JsxChild } from "@shadow-js/web";
8
+
9
+ /**
10
+ * Props of {@link DirectionProvider}: the direction and the subtree. `direction` is shadcn's name for `dir`; when both
11
+ * are given, `dir` wins.
12
+ * @since 1.0.0
13
+ * @see {@link DirectionProvider}
14
+ */
15
+ export interface DirectionProviderProps {
16
+ /** The reading direction for every component below: `"ltr"` or `"rtl"`. @default none */
17
+ readonly dir?: Direction | undefined;
18
+ /** The same, under the name shadcn uses. @default none */
19
+ readonly direction?: Direction | undefined;
20
+ /** The subtree that reads the direction. @default none */
21
+ readonly children?: JsxChild;
22
+ }
23
+
24
+ /**
25
+ * Sets the reading direction for the components below it: the arrow keys of tabs, menus, sliders and carousels,
26
+ * and the side popovers open on. It renders no element and writes no attribute, so also set `dir` on the document (`<html dir="rtl">`)
27
+ * or on the container: the provider tells the components, and the `dir` attribute tells the browser, which lays out the text.
28
+ * Without a provider each component reads the `dir` of the document.
29
+ *
30
+ * Keyboard and ARIA: none of its own. It changes which arrow key means "next" in right-to-left text.
31
+ *
32
+ * Compared with shadcn/ui: `dir` is the primitives' name and `direction` is accepted as shadcn's.
33
+ *
34
+ * @param props - The direction and the subtree.
35
+ * @returns The subtree.
36
+ * @example
37
+ * ```tsx
38
+ * import { DirectionProvider } from "@/components/ui/direction";
39
+ * import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
40
+ *
41
+ * export function RightToLeftTabs() {
42
+ * return (
43
+ * <DirectionProvider dir="rtl">
44
+ * <Tabs defaultValue="one" dir="rtl">
45
+ * <TabsList>
46
+ * <TabsTrigger value="one">One</TabsTrigger>
47
+ * </TabsList>
48
+ * </Tabs>
49
+ * </DirectionProvider>
50
+ * );
51
+ * }
52
+ * ```
53
+ * @since 1.0.0
54
+ * @see {@link useDirection}
55
+ */
56
+ function DirectionProvider(props: DirectionProviderProps): JSX.Element {
57
+ return (
58
+ <DirectionPrimitive dir={props.dir ?? props.direction ?? "ltr"}>
59
+ {props.children}
60
+ </DirectionPrimitive>
61
+ );
62
+ }
63
+
64
+ /**
65
+ * Reads the reading direction at this point of the tree: the nearest {@link DirectionProvider}, else the `dir` of the document, else
66
+ * `"ltr"`. Call it while a component sets up, like any hook; it returns an accessor, so a change of direction updates what reads it.
67
+ *
68
+ * Keyboard and ARIA: none of its own.
69
+ *
70
+ * @param local - An accessor of a direction the caller was given, which wins over the provider.
71
+ * @returns An accessor of `"ltr"` or `"rtl"`.
72
+ * @example
73
+ * ```tsx
74
+ * import { useDirection } from "@/components/ui/direction";
75
+ *
76
+ * export function Hint() {
77
+ * const dir = useDirection();
78
+ * return <span>{dir() === "rtl" ? "Press the left arrow" : "Press the right arrow"}</span>;
79
+ * }
80
+ * ```
81
+ * @since 1.0.0
82
+ * @see {@link DirectionProvider}
83
+ */
84
+ function useDirection(local?: () => Direction | undefined): Accessor<Direction> {
85
+ return useDirectionPrimitive(local);
86
+ }
87
+
88
+ export { DirectionProvider, useDirection };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Direction",
3
+ "description": "A provider that sets the reading direction (left to right or right to left) for the components below it, and a hook that reads it."
4
+ }