@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,373 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import type { ComponentProps, JSX } from "@shadow-js/web";
3
+ import { cn } from "@/lib/utils";
4
+
5
+ /** The attributes that make a scroll container a named region. @internal */
6
+ interface RegionAttributes {
7
+ readonly role?: "region" | undefined;
8
+ readonly "aria-label"?: string | null | undefined;
9
+ readonly "aria-labelledby"?: string | null | undefined;
10
+ }
11
+
12
+ /**
13
+ * A responsive table. The `table` sits in a container that scrolls horizontally when the table is
14
+ * wider than the space it has. `class` goes on the `table` element.
15
+ *
16
+ * Keyboard and ARIA: the container takes keyboard focus (`tabindex="0"`), so keyboard users can
17
+ * scroll a wide table. The component adds no keys of its own. When the table has an accessible name
18
+ * (`aria-label` or `aria-labelledby`), the container is a `role="region"` with the same name, so the
19
+ * scrolling area is announced by name. Name the table with a {@link TableCaption} or an
20
+ * `aria-label`.
21
+ *
22
+ * shadcn/ui differences: the scroll container is focusable and, for a named table, a labeled
23
+ * region. shadcn/ui sets neither. `class` replaces `className`.
24
+ *
25
+ * @param props - The props: the attributes of the `table`, and `class`.
26
+ * @returns The scroll container, with the table inside.
27
+ * @example
28
+ * ```tsx
29
+ * import {
30
+ * Table,
31
+ * TableBody,
32
+ * TableCaption,
33
+ * TableCell,
34
+ * TableHead,
35
+ * TableHeader,
36
+ * TableRow,
37
+ * } from "@/components/ui/table";
38
+ *
39
+ * export function Invoices() {
40
+ * return (
41
+ * <Table>
42
+ * <TableCaption>Recent invoices</TableCaption>
43
+ * <TableHeader>
44
+ * <TableRow>
45
+ * <TableHead scope="col">Invoice</TableHead>
46
+ * <TableHead scope="col">Amount</TableHead>
47
+ * </TableRow>
48
+ * </TableHeader>
49
+ * <TableBody>
50
+ * <TableRow>
51
+ * <TableCell>INV001</TableCell>
52
+ * <TableCell>$250.00</TableCell>
53
+ * </TableRow>
54
+ * </TableBody>
55
+ * </Table>
56
+ * );
57
+ * }
58
+ * ```
59
+ * @since 1.0.0
60
+ * @see {@link TableCaption}
61
+ */
62
+ function Table(props: ComponentProps<"table">): JSX.Element {
63
+ const [local, rest] = splitProps(props, ["class", "children"]);
64
+ // A named table names its scroll region too; an unnamed one is not a region at all.
65
+ const region = (): RegionAttributes =>
66
+ props["aria-label"] === undefined && props["aria-labelledby"] === undefined
67
+ ? {}
68
+ : {
69
+ role: "region",
70
+ "aria-label": props["aria-label"],
71
+ "aria-labelledby": props["aria-labelledby"],
72
+ };
73
+ return (
74
+ <div
75
+ data-slot="table-container"
76
+ class="relative w-full overflow-x-auto"
77
+ tabindex={0}
78
+ {...region()}
79
+ >
80
+ <table data-slot="table" class={cn("w-full caption-bottom text-sm", local.class)} {...rest}>
81
+ {local.children}
82
+ </table>
83
+ </div>
84
+ );
85
+ }
86
+
87
+ /**
88
+ * The `thead` of a table. Its rows get a bottom border.
89
+ *
90
+ * Keyboard and ARIA: no keys, and no role of its own (it is a row group). Its header cells are
91
+ * column headers (see {@link TableHead}).
92
+ *
93
+ * shadcn/ui differences: `class` replaces `className`.
94
+ *
95
+ * @param props - The props: the attributes of the `thead`, and `class`.
96
+ * @returns The `thead` element.
97
+ * @example
98
+ * ```tsx
99
+ * import { Table, TableHead, TableHeader, TableRow } from "@/components/ui/table";
100
+ *
101
+ * export function Columns() {
102
+ * return (
103
+ * <Table>
104
+ * <TableHeader>
105
+ * <TableRow>
106
+ * <TableHead scope="col">Invoice</TableHead>
107
+ * </TableRow>
108
+ * </TableHeader>
109
+ * </Table>
110
+ * );
111
+ * }
112
+ * ```
113
+ * @since 1.0.0
114
+ */
115
+ function TableHeader(props: ComponentProps<"thead">): JSX.Element {
116
+ const [local, rest] = splitProps(props, ["class", "children"]);
117
+ return (
118
+ <thead data-slot="table-header" class={cn("[&_tr]:border-b", local.class)} {...rest}>
119
+ {local.children}
120
+ </thead>
121
+ );
122
+ }
123
+
124
+ /**
125
+ * The `tbody` of a table. Its last row has no bottom border.
126
+ *
127
+ * Keyboard and ARIA: no keys, and no role of its own (it is a row group).
128
+ *
129
+ * shadcn/ui differences: `class` replaces `className`.
130
+ *
131
+ * @param props - The props: the attributes of the `tbody`, and `class`.
132
+ * @returns The `tbody` element.
133
+ * @example
134
+ * ```tsx
135
+ * import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
136
+ *
137
+ * export function Rows() {
138
+ * return (
139
+ * <Table>
140
+ * <TableBody>
141
+ * <TableRow>
142
+ * <TableCell>INV001</TableCell>
143
+ * </TableRow>
144
+ * </TableBody>
145
+ * </Table>
146
+ * );
147
+ * }
148
+ * ```
149
+ * @since 1.0.0
150
+ */
151
+ function TableBody(props: ComponentProps<"tbody">): JSX.Element {
152
+ const [local, rest] = splitProps(props, ["class", "children"]);
153
+ return (
154
+ <tbody data-slot="table-body" class={cn("[&_tr:last-child]:border-0", local.class)} {...rest}>
155
+ {local.children}
156
+ </tbody>
157
+ );
158
+ }
159
+
160
+ /**
161
+ * The `tfoot` of a table, for totals. It has a top border and a muted background, and its last row
162
+ * has no bottom border.
163
+ *
164
+ * Keyboard and ARIA: no keys, and no role of its own (it is a row group).
165
+ *
166
+ * shadcn/ui differences: `class` replaces `className`.
167
+ *
168
+ * @param props - The props: the attributes of the `tfoot`, and `class`.
169
+ * @returns The `tfoot` element.
170
+ * @example
171
+ * ```tsx
172
+ * import { Table, TableCell, TableFooter, TableRow } from "@/components/ui/table";
173
+ *
174
+ * export function Total() {
175
+ * return (
176
+ * <Table>
177
+ * <TableFooter>
178
+ * <TableRow>
179
+ * <TableCell>Total</TableCell>
180
+ * <TableCell>$250.00</TableCell>
181
+ * </TableRow>
182
+ * </TableFooter>
183
+ * </Table>
184
+ * );
185
+ * }
186
+ * ```
187
+ * @since 1.0.0
188
+ */
189
+ function TableFooter(props: ComponentProps<"tfoot">): JSX.Element {
190
+ const [local, rest] = splitProps(props, ["class", "children"]);
191
+ return (
192
+ <tfoot
193
+ data-slot="table-footer"
194
+ class={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", local.class)}
195
+ {...rest}
196
+ >
197
+ {local.children}
198
+ </tfoot>
199
+ );
200
+ }
201
+
202
+ /**
203
+ * A row of cells. It highlights on hover, and with `data-state="selected"` it takes the selected
204
+ * background.
205
+ *
206
+ * Keyboard and ARIA: no keys; the row has the `row` role. Set `data-state="selected"` for the
207
+ * highlight only; it is not announced, so mark a selected row with `aria-selected` if assistive
208
+ * technology must know.
209
+ *
210
+ * shadcn/ui differences: `class` replaces `className`.
211
+ *
212
+ * @param props - The props: the attributes of the `tr`, and `class`.
213
+ * @returns The `tr` element.
214
+ * @example
215
+ * ```tsx
216
+ * import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
217
+ *
218
+ * export function SelectedRow() {
219
+ * return (
220
+ * <Table>
221
+ * <TableBody>
222
+ * <TableRow data-state="selected" aria-selected="true">
223
+ * <TableCell>INV001</TableCell>
224
+ * </TableRow>
225
+ * </TableBody>
226
+ * </Table>
227
+ * );
228
+ * }
229
+ * ```
230
+ * @since 1.0.0
231
+ */
232
+ function TableRow(props: ComponentProps<"tr">): JSX.Element {
233
+ const [local, rest] = splitProps(props, ["class", "children"]);
234
+ return (
235
+ <tr
236
+ data-slot="table-row"
237
+ class={cn(
238
+ "border-b transition-colors hover:bg-muted/50 motion-reduce:transition-none data-[state=selected]:bg-muted forced-colors:data-[state=selected]:bg-[Highlight] forced-colors:data-[state=selected]:text-[HighlightText]",
239
+ local.class,
240
+ )}
241
+ {...rest}
242
+ >
243
+ {local.children}
244
+ </tr>
245
+ );
246
+ }
247
+
248
+ /**
249
+ * A header cell (`th`). Give it `scope="col"` (a column header) or `scope="row"` (a row header), so
250
+ * assistive technology knows what the cell heads.
251
+ *
252
+ * Keyboard and ARIA: no keys. In a header row it is a `columnheader`; `scope="row"` makes it a
253
+ * row header.
254
+ *
255
+ * shadcn/ui differences: `class` replaces `className`.
256
+ *
257
+ * @param props - The props: the attributes of the `th`, and `class`.
258
+ * @returns The `th` element.
259
+ * @example
260
+ * ```tsx
261
+ * import { Table, TableHead, TableHeader, TableRow } from "@/components/ui/table";
262
+ *
263
+ * export function AmountHead() {
264
+ * return (
265
+ * <Table>
266
+ * <TableHeader>
267
+ * <TableRow>
268
+ * <TableHead scope="col">Amount</TableHead>
269
+ * </TableRow>
270
+ * </TableHeader>
271
+ * </Table>
272
+ * );
273
+ * }
274
+ * ```
275
+ * @since 1.0.0
276
+ */
277
+ function TableHead(props: ComponentProps<"th">): JSX.Element {
278
+ const [local, rest] = splitProps(props, ["class", "children"]);
279
+ return (
280
+ <th
281
+ data-slot="table-head"
282
+ class={cn(
283
+ "h-10 px-2 text-start align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pe-0 [&>[role=checkbox]]:translate-y-[2px]",
284
+ local.class,
285
+ )}
286
+ {...rest}
287
+ >
288
+ {local.children}
289
+ </th>
290
+ );
291
+ }
292
+
293
+ /**
294
+ * A data cell (`td`).
295
+ *
296
+ * Keyboard and ARIA: no keys; the cell has the `cell` role.
297
+ *
298
+ * shadcn/ui differences: `class` replaces `className`.
299
+ *
300
+ * @param props - The props: the attributes of the `td`, and `class`.
301
+ * @returns The `td` element.
302
+ * @example
303
+ * ```tsx
304
+ * import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
305
+ *
306
+ * export function Amount() {
307
+ * return (
308
+ * <Table>
309
+ * <TableBody>
310
+ * <TableRow>
311
+ * <TableCell>$250.00</TableCell>
312
+ * </TableRow>
313
+ * </TableBody>
314
+ * </Table>
315
+ * );
316
+ * }
317
+ * ```
318
+ * @since 1.0.0
319
+ */
320
+ function TableCell(props: ComponentProps<"td">): JSX.Element {
321
+ const [local, rest] = splitProps(props, ["class", "children"]);
322
+ return (
323
+ <td
324
+ data-slot="table-cell"
325
+ class={cn(
326
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pe-0 [&>[role=checkbox]]:translate-y-[2px]",
327
+ local.class,
328
+ )}
329
+ {...rest}
330
+ >
331
+ {local.children}
332
+ </td>
333
+ );
334
+ }
335
+
336
+ /**
337
+ * The caption of a table. It is shown below the table and is the table's accessible name.
338
+ *
339
+ * Keyboard and ARIA: no keys. The caption names the table, so a table needs a caption or an
340
+ * `aria-label` for its name.
341
+ *
342
+ * shadcn/ui differences: `class` replaces `className`.
343
+ *
344
+ * @param props - The props: the attributes of the `caption`, and `class`.
345
+ * @returns The `caption` element.
346
+ * @example
347
+ * ```tsx
348
+ * import { Table, TableCaption } from "@/components/ui/table";
349
+ *
350
+ * export function Captioned() {
351
+ * return (
352
+ * <Table>
353
+ * <TableCaption>A list of your recent invoices.</TableCaption>
354
+ * </Table>
355
+ * );
356
+ * }
357
+ * ```
358
+ * @since 1.0.0
359
+ */
360
+ function TableCaption(props: ComponentProps<"caption">): JSX.Element {
361
+ const [local, rest] = splitProps(props, ["class", "children"]);
362
+ return (
363
+ <caption
364
+ data-slot="table-caption"
365
+ class={cn("mt-4 text-sm text-muted-foreground", local.class)}
366
+ {...rest}
367
+ >
368
+ {local.children}
369
+ </caption>
370
+ );
371
+ }
372
+
373
+ export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Tabs",
3
+ "description": "A set of layered sections of content, known as tab panels, that are displayed one at a time."
4
+ }
@@ -0,0 +1,209 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ type TabsContentProps,
4
+ type TabsListProps,
5
+ Tabs as TabsPrimitive,
6
+ type TabsProps,
7
+ type TabsTriggerProps,
8
+ } from "@shadow-js/primitives";
9
+ import type { JSX } from "@shadow-js/web";
10
+ import { cn } from "@/lib/utils";
11
+
12
+ /**
13
+ * A set of sections, one visible at a time, switched with tabs. Control the selected tab with
14
+ * `value` and `onValueChange`, or start it with `defaultValue`. `orientation` (`"horizontal"` or
15
+ * `"vertical"`), `dir` and `activationMode` are passed to the primitive.
16
+ *
17
+ * Keyboard and ARIA: the selected tab is the one tab stop of the list. Arrow keys move between
18
+ * enabled tabs (Up and Down when vertical, Left and Right when horizontal, swapped in a
19
+ * right-to-left layout), Home and End go to the first and last tabs, and the arrows wrap around.
20
+ * With `activationMode="automatic"` (the default) moving focus selects the tab; with `"manual"` only
21
+ * Enter, Space or a click selects it. A click always selects. The list is `role="tablist"` and each
22
+ * tab is `role="tab"` with `aria-selected`. The panel is `role="tabpanel"`, named by its tab, and
23
+ * focusable so its content can be reached. Name the list with a `TabsList` `aria-label`.
24
+ *
25
+ * shadcn/ui differences: `class` replaces `className`.
26
+ *
27
+ * @param props - The props, see {@link TabsProps}.
28
+ * @returns The root `div` with the list and the panels inside.
29
+ * @example
30
+ * ```tsx
31
+ * import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
32
+ *
33
+ * export function SettingsTabs() {
34
+ * return (
35
+ * <Tabs defaultValue="account">
36
+ * <TabsList aria-label="Settings">
37
+ * <TabsTrigger value="account">Account</TabsTrigger>
38
+ * <TabsTrigger value="password">Password</TabsTrigger>
39
+ * </TabsList>
40
+ * <TabsContent value="account">Account settings</TabsContent>
41
+ * <TabsContent value="password">Password settings</TabsContent>
42
+ * </Tabs>
43
+ * );
44
+ * }
45
+ * ```
46
+ * @since 1.0.0
47
+ * @see {@link TabsList}
48
+ */
49
+ function Tabs(props: TabsProps): JSX.Element {
50
+ const [local, rest] = splitProps(props, ["class"]);
51
+ return (
52
+ <TabsPrimitive
53
+ data-slot="tabs"
54
+ class={cn("group/tabs flex flex-col gap-2 data-[orientation=vertical]:flex-row", local.class)}
55
+ {...rest}
56
+ />
57
+ );
58
+ }
59
+
60
+ /**
61
+ * The own props of {@link TabsList}, on top of the primitive's list props.
62
+ * @since 1.0.0
63
+ * @see {@link TabsList}
64
+ */
65
+ export interface TabsListOwnProps {
66
+ /**
67
+ * The look of the list. `"default"` is a muted pill; `"line"` is a transparent list whose selected tab
68
+ * has an underline.
69
+ * @default "default"
70
+ */
71
+ readonly variant?: "default" | "line" | undefined;
72
+ }
73
+
74
+ /**
75
+ * The row (or column, when vertical) of tabs. It is a `role="tablist"` and owns the arrow-key
76
+ * movement between its tabs. Name it with `aria-label` or `aria-labelledby`.
77
+ *
78
+ * Keyboard and ARIA: see {@link Tabs}. The list sets `aria-orientation="vertical"` only when the
79
+ * tabs are stacked. Pass `loop={false}` to stop the arrows at the ends instead of wrapping.
80
+ *
81
+ * `variant` is `"default"` (a muted pill behind the tabs) or `"line"` (a transparent list, with an
82
+ * underline under the selected tab). It is styled by the list and the trigger together, so the line
83
+ * variant works through the triggers' own classes.
84
+ *
85
+ * shadcn/ui differences: `class` replaces `className`.
86
+ *
87
+ * @param props - The props: `loop`, `variant` and the attributes of the list `div`.
88
+ * @returns The `div` with the tabs inside.
89
+ * @default variant "default"
90
+ * @example
91
+ * ```tsx
92
+ * import { TabsList, TabsTrigger } from "@/components/ui/tabs";
93
+ *
94
+ * export function Sections() {
95
+ * return (
96
+ * <TabsList aria-label="Sections">
97
+ * <TabsTrigger value="overview">Overview</TabsTrigger>
98
+ * <TabsTrigger value="activity">Activity</TabsTrigger>
99
+ * </TabsList>
100
+ * );
101
+ * }
102
+ * ```
103
+ * @since 1.0.0
104
+ */
105
+ function TabsList(props: TabsListProps & TabsListOwnProps): JSX.Element {
106
+ const [local, rest] = splitProps(props, ["class", "variant"]);
107
+ const variant = (): "default" | "line" => local.variant ?? "default";
108
+ return (
109
+ <TabsPrimitive.List
110
+ data-slot="tabs-list"
111
+ data-variant={variant()}
112
+ class={cn(
113
+ "group/tabs-list inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground data-[orientation=vertical]:h-fit data-[orientation=vertical]:flex-col",
114
+ variant() === "line" ? "gap-1 bg-transparent" : "bg-muted",
115
+ local.class,
116
+ )}
117
+ {...rest}
118
+ />
119
+ );
120
+ }
121
+
122
+ /**
123
+ * One tab of a {@link Tabs}: the button that selects the panel with the same `value`. A disabled
124
+ * tab cannot be selected and the arrow keys skip it.
125
+ *
126
+ * Keyboard and ARIA: a `button` with `role="tab"`, `aria-selected` (`"true"` or `"false"`),
127
+ * `aria-controls` pointing at its panel, and `data-state` (`"active"` or `"inactive"`). It is in the
128
+ * tab order only when selected. Enter and Space select it, and so does a click. Arrow keys and
129
+ * Home and End are handled by the list (see {@link Tabs}).
130
+ *
131
+ * shadcn/ui differences: `class` replaces `className`.
132
+ *
133
+ * @param props - The props: `value` (required), `disabled`, and the attributes of the `button`.
134
+ * @returns The tab `button`.
135
+ * @example
136
+ * ```tsx
137
+ * import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
138
+ *
139
+ * export function Billing() {
140
+ * return (
141
+ * <Tabs defaultValue="plans">
142
+ * <TabsList aria-label="Billing">
143
+ * <TabsTrigger value="plans">Plans</TabsTrigger>
144
+ * <TabsTrigger value="invoices" disabled>
145
+ * Invoices
146
+ * </TabsTrigger>
147
+ * </TabsList>
148
+ * <TabsContent value="plans">Choose a plan.</TabsContent>
149
+ * </Tabs>
150
+ * );
151
+ * }
152
+ * ```
153
+ * @since 1.0.0
154
+ */
155
+ function TabsTrigger(props: TabsTriggerProps): JSX.Element {
156
+ const [local, rest] = splitProps(props, ["class"]);
157
+ return (
158
+ <TabsPrimitive.Trigger
159
+ data-slot="tabs-trigger"
160
+ class={cn(
161
+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground transition-[color,box-shadow] group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-end-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring motion-reduce:transition-none disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:shadow-sm dark:text-muted-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground forced-colors:disabled:opacity-100 forced-colors:data-[state=active]:bg-[Highlight] forced-colors:data-[state=active]:text-[HighlightText] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 outline-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
162
+ local.class,
163
+ )}
164
+ {...rest}
165
+ />
166
+ );
167
+ }
168
+
169
+ /**
170
+ * The panel of the tab with the same `value`. Only the selected panel is rendered; the others are
171
+ * removed until they are selected again.
172
+ *
173
+ * Keyboard and ARIA: a `div` with `role="tabpanel"`, named by its tab (`aria-labelledby`), with
174
+ * `tabindex="0"` so keyboard users can reach its content, and `data-state` (`"active"` or
175
+ * `"inactive"`). The component adds no key handling.
176
+ *
177
+ * shadcn/ui differences: `class` replaces `className`.
178
+ *
179
+ * @param props - The props: `value` (required), `forceMount`, and the attributes of the `div`.
180
+ * @returns The panel `div`.
181
+ * @example
182
+ * ```tsx
183
+ * import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
184
+ *
185
+ * export function Profile() {
186
+ * return (
187
+ * <Tabs defaultValue="general">
188
+ * <TabsList aria-label="Profile">
189
+ * <TabsTrigger value="general">General</TabsTrigger>
190
+ * </TabsList>
191
+ * <TabsContent value="general">Name and email.</TabsContent>
192
+ * </Tabs>
193
+ * );
194
+ * }
195
+ * ```
196
+ * @since 1.0.0
197
+ */
198
+ function TabsContent(props: TabsContentProps): JSX.Element {
199
+ const [local, rest] = splitProps(props, ["class"]);
200
+ return (
201
+ <TabsPrimitive.Content
202
+ data-slot="tabs-content"
203
+ class={cn("flex-1 outline-none", local.class)}
204
+ {...rest}
205
+ />
206
+ );
207
+ }
208
+
209
+ export { Tabs, TabsContent, TabsList, TabsTrigger };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Textarea",
3
+ "description": "A multi-line text field that grows with its content."
4
+ }
@@ -0,0 +1,51 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import type { ComponentProps, JSX } from "@shadow-js/web";
3
+ import { cn } from "@/lib/utils";
4
+
5
+ /**
6
+ * A multi-line text field that grows with its content (`field-sizing: content`). It is a native
7
+ * `textarea`, so every `textarea` attribute passes through (`value`, `rows`, `disabled`, `name`,
8
+ * `onInput`, and so on). `aria-invalid="true"` turns the border and the focus ring destructive.
9
+ *
10
+ * Keyboard and ARIA: the browser's own keys apply. Tab moves focus in and out, Enter inserts a line
11
+ * break, and the arrow keys move the caret. It has the `textbox` role. Name it with a
12
+ * {@link Label} (`id` on the textarea, `htmlFor` on the label) or with `aria-label`. Set
13
+ * `aria-invalid="true"` and `aria-describedby` on the textarea to show an error.
14
+ *
15
+ * shadcn/ui differences: `class` replaces `className`.
16
+ *
17
+ * @param props - The props: the attributes of the native `textarea`. `class` is merged with the base
18
+ * classes.
19
+ * @returns The `textarea` element.
20
+ * @example
21
+ * ```tsx
22
+ * import { Label } from "@/components/ui/label";
23
+ * import { Textarea } from "@/components/ui/textarea";
24
+ *
25
+ * export function Feedback() {
26
+ * return (
27
+ * <div class="grid gap-2">
28
+ * <Label htmlFor="feedback">Feedback</Label>
29
+ * <Textarea id="feedback" placeholder="Type your message here." rows={3} />
30
+ * </div>
31
+ * );
32
+ * }
33
+ * ```
34
+ * @since 1.0.0
35
+ * @see {@link Label}
36
+ */
37
+ function Textarea(props: ComponentProps<"textarea">): JSX.Element {
38
+ const [local, rest] = splitProps(props, ["class"]);
39
+ return (
40
+ <textarea
41
+ data-slot="textarea"
42
+ class={cn(
43
+ "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 motion-reduce:transition-none md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
44
+ local.class,
45
+ )}
46
+ {...rest}
47
+ />
48
+ );
49
+ }
50
+
51
+ export { Textarea };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Toast",
3
+ "description": "Toast stores (useToast in components, createToaster in module code) and the Toaster that shows their notifications, after Sonner."
4
+ }