@shadow-js/ui 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +598 -2
  4. package/dist/cli.mjs +2293 -0
  5. package/dist/dev/cli/schema.d.ts +73 -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 +141 -0
  9. package/dist/dev/locales/ar.js +128 -0
  10. package/dist/dev/locales/ar.js.map +1 -0
  11. package/dist/dev/locales/en.d.ts +141 -0
  12. package/dist/dev/locales/en.js +128 -0
  13. package/dist/dev/locales/en.js.map +1 -0
  14. package/dist/dev/registry/utils/utils.d.ts +29 -0
  15. package/dist/dev/registry/utils/utils.js +27 -0
  16. package/dist/dev/registry/utils/utils.js.map +1 -0
  17. package/dist/dev/theme/theme-logic.d.ts +187 -0
  18. package/dist/dev/theme/theme-logic.js +184 -0
  19. package/dist/dev/theme/theme-logic.js.map +1 -0
  20. package/dist/dev/theme/theme-provider.d.ts +90 -0
  21. package/dist/dev/theme/theme-provider.js +150 -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,439 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ createPartContext,
4
+ mergeElementProps,
5
+ Polymorphic,
6
+ type PolymorphicProps,
7
+ type PolymorphicTarget,
8
+ } from "@shadow-js/primitives";
9
+ import type { ComponentProps, JSX } from "@shadow-js/web";
10
+ import { cva, type VariantProps } from "class-variance-authority";
11
+ import { cn } from "@/lib/utils";
12
+
13
+ const itemVariants = cva(
14
+ "group/item flex w-full flex-wrap items-center rounded-md border border-transparent text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-accent/50",
15
+ {
16
+ variants: {
17
+ variant: {
18
+ default: "bg-transparent",
19
+ outline: "border-border",
20
+ muted: "bg-muted/50",
21
+ },
22
+ size: {
23
+ default: "gap-4 p-4",
24
+ sm: "gap-2.5 px-4 py-3",
25
+ },
26
+ },
27
+ defaultVariants: { variant: "default", size: "default" },
28
+ },
29
+ );
30
+
31
+ /** The props as the implementation sees them. */
32
+ type Loose = {
33
+ readonly as?: PolymorphicTarget;
34
+ readonly class?: unknown;
35
+ readonly [prop: string]: unknown;
36
+ };
37
+
38
+ /** Set by {@link ItemGroup} for the items and separators inside its list. @internal */
39
+ const itemGroupContext = createPartContext<boolean>("ItemGroup");
40
+
41
+ /**
42
+ * Own props of {@link Item}: its `variant` (`default`, `outline` or `muted`) and `size` (`default`
43
+ * or `sm`).
44
+ * @since 1.0.0
45
+ * @see {@link Item}
46
+ */
47
+ export type ItemOwnProps = VariantProps<typeof itemVariants>;
48
+
49
+ /**
50
+ * Props of {@link Item}: the own props and the attributes of the chosen element (`div` by default,
51
+ * or `as`).
52
+ * @since 1.0.0
53
+ * @see {@link Item}
54
+ */
55
+ export type ItemProps<T extends PolymorphicTarget = "div"> = PolymorphicProps<
56
+ "div",
57
+ ItemOwnProps,
58
+ T
59
+ >;
60
+
61
+ /**
62
+ * A row of media, content and actions: a file, a person, a setting. Pass `as="a"` to make the whole
63
+ * row a link; the focus ring and the hover state carry over.
64
+ *
65
+ * Keyboard and ARIA: the row handles no keys of its own. A plain row has no role, except inside an
66
+ * {@link ItemGroup}, where it is a `listitem`. A row with `as="a"` is a link, so Enter follows it;
67
+ * inside a group it is wrapped in a `listitem`. The row adds no name of its own; give it an
68
+ * `aria-label` when its content does not say what it is. `data-variant` and `data-size` mirror the
69
+ * props.
70
+ *
71
+ * @param props The row's attributes, plus `variant`, `size`, and `as` (the element to render).
72
+ * @returns The row element, wrapped in a `listitem` `div` when it is a link inside a group.
73
+ * @example
74
+ * import { Button } from "@/components/ui/button";
75
+ * import { InfoIcon } from "@/components/ui/icons";
76
+ * import {
77
+ * Item,
78
+ * ItemActions,
79
+ * ItemContent,
80
+ * ItemDescription,
81
+ * ItemMedia,
82
+ * ItemTitle,
83
+ * } from "@/components/ui/item";
84
+ *
85
+ * <Item variant="outline">
86
+ * <ItemMedia variant="icon">
87
+ * <InfoIcon />
88
+ * </ItemMedia>
89
+ * <ItemContent>
90
+ * <ItemTitle>Report.pdf</ItemTitle>
91
+ * <ItemDescription>2 MB</ItemDescription>
92
+ * </ItemContent>
93
+ * <ItemActions>
94
+ * <Button size="sm" variant="outline">Open</Button>
95
+ * </ItemActions>
96
+ * </Item>
97
+ * @since 1.0.0
98
+ * @see {@link ItemGroup}
99
+ */
100
+ function Item<T extends PolymorphicTarget = "div">(props: ItemProps<T>): JSX.Element {
101
+ const [local, rest] = splitProps(props as unknown as Loose & ItemProps, [
102
+ "as",
103
+ "class",
104
+ "variant",
105
+ "size",
106
+ ]);
107
+ const inList = itemGroupContext.useOptional() === true;
108
+ const element = (local.as as PolymorphicTarget | undefined) ?? "div";
109
+ const merged = mergeElementProps(
110
+ {
111
+ "data-slot": "item",
112
+ // A plain row is the listitem itself. A link row cannot be one, so it is wrapped below.
113
+ role: inList && element === "div" ? "listitem" : undefined,
114
+ get "data-variant"() {
115
+ return (local.variant as string | undefined) ?? "default";
116
+ },
117
+ get "data-size"() {
118
+ return (local.size as string | undefined) ?? "default";
119
+ },
120
+ get class() {
121
+ return cn(
122
+ itemVariants({
123
+ variant: local.variant as "default" | "outline" | "muted" | undefined,
124
+ size: local.size as "default" | "sm" | undefined,
125
+ }),
126
+ local.class as string | undefined,
127
+ );
128
+ },
129
+ },
130
+ rest,
131
+ );
132
+ const row = <Polymorphic as={element} {...merged} />;
133
+ if (!inList || element === "div") return row;
134
+ return (
135
+ <div role="listitem" data-slot="item-row" class="w-full">
136
+ {row}
137
+ </div>
138
+ );
139
+ }
140
+
141
+ /**
142
+ * A list of items: a `div` with `role="list"`. Its items are listitems; a link item is wrapped in
143
+ * one, and a separator inside it is presentational.
144
+ *
145
+ * Keyboard and ARIA: no keys of its own. The list has no name of its own: name it with `aria-label`
146
+ * or `aria-labelledby`, for example `aria-label="Files"`.
147
+ *
148
+ * @param props The `div` attributes. `class` adds to the list's classes.
149
+ * @returns The list `div` with its items inside.
150
+ * @example
151
+ * import { Item, ItemContent, ItemGroup, ItemTitle } from "@/components/ui/item";
152
+ *
153
+ * <ItemGroup aria-label="Files">
154
+ * <Item>
155
+ * <ItemContent>
156
+ * <ItemTitle>Report.pdf</ItemTitle>
157
+ * </ItemContent>
158
+ * </Item>
159
+ * </ItemGroup>
160
+ * @since 1.0.0
161
+ * @see {@link Item}
162
+ */
163
+ function ItemGroup(props: ComponentProps<"div">): JSX.Element {
164
+ const [local, rest] = splitProps(props, ["class"]);
165
+ return (
166
+ <itemGroupContext.Provider value={true}>
167
+ <div
168
+ role="list"
169
+ data-slot="item-group"
170
+ class={cn("group/item-group flex flex-col", local.class)}
171
+ {...rest}
172
+ />
173
+ </itemGroupContext.Provider>
174
+ );
175
+ }
176
+
177
+ /**
178
+ * A line between items of a group, or on its own. Inside an {@link ItemGroup} it is presentational
179
+ * (`role="none"`); outside one it is a horizontal separator.
180
+ *
181
+ * Keyboard and ARIA: no keys, and it is not focusable. Outside a group it has `role="separator"` and
182
+ * `aria-orientation="horizontal"`.
183
+ *
184
+ * @param props The `div` attributes. `class` adds to the classes.
185
+ * @returns The separator `div`.
186
+ * @example
187
+ * import { Item, ItemGroup, ItemSeparator, ItemTitle } from "@/components/ui/item";
188
+ *
189
+ * <ItemGroup aria-label="Files">
190
+ * <Item>
191
+ * <ItemTitle>Report.pdf</ItemTitle>
192
+ * </Item>
193
+ * <ItemSeparator />
194
+ * <Item>
195
+ * <ItemTitle>Notes.txt</ItemTitle>
196
+ * </Item>
197
+ * </ItemGroup>
198
+ * @since 1.0.0
199
+ * @see {@link ItemGroup}
200
+ */
201
+ function ItemSeparator(props: ComponentProps<"div">): JSX.Element {
202
+ const [local, rest] = splitProps(props, ["class"]);
203
+ const inList = itemGroupContext.useOptional() === true;
204
+ const classes = cn("my-0 h-px w-full shrink-0 bg-border", local.class);
205
+ return inList ? (
206
+ <div role="none" data-slot="item-separator" class={classes} {...rest} />
207
+ ) : (
208
+ <div
209
+ role="separator"
210
+ aria-orientation="horizontal"
211
+ data-slot="item-separator"
212
+ class={classes}
213
+ {...rest}
214
+ />
215
+ );
216
+ }
217
+
218
+ /**
219
+ * Props of {@link ItemMedia}: the `div` attributes plus `variant`.
220
+ * @since 1.0.0
221
+ * @see {@link ItemMedia}
222
+ */
223
+ export type ItemMediaProps = ComponentProps<"div"> & {
224
+ /**
225
+ * The kind of media. `icon` puts the child on a small tile; `image` fills a square with the child
226
+ * (an `img`, for instance).
227
+ * @default "default"
228
+ */
229
+ readonly variant?: "default" | "icon" | "image" | undefined;
230
+ };
231
+
232
+ /**
233
+ * The leading media of an item: an icon on a tile (`variant="icon"`) or an image (`variant="image"`).
234
+ *
235
+ * Keyboard and ARIA: no keys and no role. An image inside it needs its own text alternative: use
236
+ * `alt=""` when the title already says the same thing.
237
+ *
238
+ * @param props The `div` attributes plus `variant`. `class` adds to the classes.
239
+ * @returns The media `div`.
240
+ * @example
241
+ * import { Item, ItemMedia } from "@/components/ui/item";
242
+ *
243
+ * <Item>
244
+ * <ItemMedia variant="image">
245
+ * <img src="/avatar.png" alt="" />
246
+ * </ItemMedia>
247
+ * </Item>
248
+ * @since 1.0.0
249
+ * @see {@link Item}
250
+ */
251
+ function ItemMedia(props: ItemMediaProps): JSX.Element {
252
+ const [local, rest] = splitProps(props, ["class", "variant"]);
253
+ const variant = (local.variant as string | undefined) ?? "default";
254
+ return (
255
+ <div
256
+ data-slot="item-media"
257
+ data-variant={variant}
258
+ class={cn(
259
+ "flex shrink-0 items-center justify-center gap-2 [&_svg]:pointer-events-none",
260
+ variant === "icon" &&
261
+ "size-8 rounded-sm border bg-muted [&_svg:not([class*='size-'])]:size-4",
262
+ variant === "image" &&
263
+ "size-10 overflow-hidden rounded-sm [&_img]:size-full [&_img]:object-cover",
264
+ local.class,
265
+ )}
266
+ {...rest}
267
+ />
268
+ );
269
+ }
270
+
271
+ /**
272
+ * The text of an item: its title and description, growing to fill the row.
273
+ *
274
+ * Keyboard and ARIA: a `div` with no role and no keys.
275
+ *
276
+ * @param props The `div` attributes. `class` adds to the classes.
277
+ * @returns The content `div`.
278
+ * @example
279
+ * import { ItemContent, ItemDescription, ItemTitle } from "@/components/ui/item";
280
+ *
281
+ * <ItemContent>
282
+ * <ItemTitle>Name</ItemTitle>
283
+ * <ItemDescription>Shown under the name</ItemDescription>
284
+ * </ItemContent>
285
+ * @since 1.0.0
286
+ * @see {@link Item}
287
+ */
288
+ function ItemContent(props: ComponentProps<"div">): JSX.Element {
289
+ const [local, rest] = splitProps(props, ["class"]);
290
+ return (
291
+ <div
292
+ data-slot="item-content"
293
+ class={cn("flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none", local.class)}
294
+ {...rest}
295
+ />
296
+ );
297
+ }
298
+
299
+ /**
300
+ * The title of an item: the main line of text, in medium weight.
301
+ *
302
+ * Keyboard and ARIA: a `div` with no role and no keys.
303
+ *
304
+ * @param props The `div` attributes. `class` adds to the classes.
305
+ * @returns The title `div`.
306
+ * @example
307
+ * import { ItemTitle } from "@/components/ui/item";
308
+ *
309
+ * <ItemTitle>Report.pdf</ItemTitle>
310
+ * @since 1.0.0
311
+ * @see {@link ItemContent}
312
+ */
313
+ function ItemTitle(props: ComponentProps<"div">): JSX.Element {
314
+ const [local, rest] = splitProps(props, ["class"]);
315
+ return (
316
+ <div
317
+ data-slot="item-title"
318
+ class={cn("flex w-fit items-center gap-2 text-sm leading-snug font-medium", local.class)}
319
+ {...rest}
320
+ />
321
+ );
322
+ }
323
+
324
+ /**
325
+ * The description of an item: a secondary line of text, clamped to two lines.
326
+ *
327
+ * Keyboard and ARIA: a `p` with no role and no keys.
328
+ *
329
+ * @param props The `p` attributes. `class` adds to the classes.
330
+ * @returns The description `p`.
331
+ * @example
332
+ * import { ItemDescription } from "@/components/ui/item";
333
+ *
334
+ * <ItemDescription>2 MB, edited yesterday</ItemDescription>
335
+ * @since 1.0.0
336
+ * @see {@link ItemContent}
337
+ */
338
+ function ItemDescription(props: ComponentProps<"p">): JSX.Element {
339
+ const [local, rest] = splitProps(props, ["class"]);
340
+ return (
341
+ <p
342
+ data-slot="item-description"
343
+ class={cn(
344
+ "line-clamp-2 text-sm leading-normal font-normal text-balance text-muted-foreground",
345
+ local.class,
346
+ )}
347
+ {...rest}
348
+ />
349
+ );
350
+ }
351
+
352
+ /**
353
+ * The actions at the end of an item, usually buttons.
354
+ *
355
+ * Keyboard and ARIA: no keys and no role of its own; the buttons inside keep theirs.
356
+ *
357
+ * @param props The `div` attributes. `class` adds to the classes.
358
+ * @returns The actions `div`.
359
+ * @example
360
+ * import { Button } from "@/components/ui/button";
361
+ * import { ItemActions } from "@/components/ui/item";
362
+ *
363
+ * <ItemActions>
364
+ * <Button size="sm">Open</Button>
365
+ * </ItemActions>
366
+ * @since 1.0.0
367
+ * @see {@link Item}
368
+ */
369
+ function ItemActions(props: ComponentProps<"div">): JSX.Element {
370
+ const [local, rest] = splitProps(props, ["class"]);
371
+ return (
372
+ <div data-slot="item-actions" class={cn("flex items-center gap-2", local.class)} {...rest} />
373
+ );
374
+ }
375
+
376
+ /**
377
+ * A row above the content of an item, such as an image that spans the width.
378
+ *
379
+ * Keyboard and ARIA: no keys and no role of its own.
380
+ *
381
+ * @param props The `div` attributes. `class` adds to the classes.
382
+ * @returns The header `div`.
383
+ * @example
384
+ * import { ItemHeader } from "@/components/ui/item";
385
+ *
386
+ * <ItemHeader>
387
+ * <img src="/cover.png" alt="" />
388
+ * </ItemHeader>
389
+ * @since 1.0.0
390
+ * @see {@link Item}
391
+ */
392
+ function ItemHeader(props: ComponentProps<"div">): JSX.Element {
393
+ const [local, rest] = splitProps(props, ["class"]);
394
+ return (
395
+ <div
396
+ data-slot="item-header"
397
+ class={cn("flex basis-full items-center justify-between gap-2", local.class)}
398
+ {...rest}
399
+ />
400
+ );
401
+ }
402
+
403
+ /**
404
+ * A row below the content of an item, such as a count of comments.
405
+ *
406
+ * Keyboard and ARIA: no keys and no role of its own.
407
+ *
408
+ * @param props The `div` attributes. `class` adds to the classes.
409
+ * @returns The footer `div`.
410
+ * @example
411
+ * import { ItemFooter } from "@/components/ui/item";
412
+ *
413
+ * <ItemFooter>3 comments</ItemFooter>
414
+ * @since 1.0.0
415
+ * @see {@link Item}
416
+ */
417
+ function ItemFooter(props: ComponentProps<"div">): JSX.Element {
418
+ const [local, rest] = splitProps(props, ["class"]);
419
+ return (
420
+ <div
421
+ data-slot="item-footer"
422
+ class={cn("flex basis-full items-center justify-between gap-2", local.class)}
423
+ {...rest}
424
+ />
425
+ );
426
+ }
427
+
428
+ export {
429
+ Item,
430
+ ItemActions,
431
+ ItemContent,
432
+ ItemDescription,
433
+ ItemFooter,
434
+ ItemGroup,
435
+ ItemHeader,
436
+ ItemMedia,
437
+ ItemSeparator,
438
+ ItemTitle,
439
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Kbd",
3
+ "description": "Used to display textual user input from keyboard."
4
+ }
@@ -0,0 +1,65 @@
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 keyboard key or shortcut hint: a `kbd` element whose text is the key's name. Inside a tooltip
7
+ * it adapts its colors.
8
+ *
9
+ * Keyboard and ARIA: a key hint handles no keys and never takes the pointer (`pointer-events` is
10
+ * off). It adds no role and no `aria-*` attribute; screen readers read the text it contains, so
11
+ * write the key names as they should be spoken ("Ctrl", "K").
12
+ *
13
+ * @param props The `kbd` attributes. The children are the key label; `class` adds to the classes.
14
+ * @returns The `kbd` element.
15
+ * @example
16
+ * import { Kbd } from "@/components/ui/kbd";
17
+ *
18
+ * <Kbd>Esc</Kbd>
19
+ * @since 1.0.0
20
+ * @see {@link KbdGroup}
21
+ */
22
+ function Kbd(props: ComponentProps<"kbd">): JSX.Element {
23
+ const [local, rest] = splitProps(props, ["class", "children"]);
24
+ return (
25
+ <kbd
26
+ data-slot="kbd"
27
+ class={cn(
28
+ "pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm bg-muted px-1 font-sans text-xs font-medium text-foreground/60 select-none forced-colors:border [&_svg:not([class*='size-'])]:size-3",
29
+ "[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10",
30
+ local.class,
31
+ )}
32
+ {...rest}
33
+ >
34
+ {local.children}
35
+ </kbd>
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Groups the keys of one shortcut, with a small gap between them: `Ctrl` then `K`.
41
+ *
42
+ * Keyboard and ARIA: no keys and no role of its own; the keys inside are read in order.
43
+ *
44
+ * @param props The `kbd` attributes. The children are the {@link Kbd} keys; `class` adds to the classes.
45
+ * @returns The group element, a `kbd` that holds the keys.
46
+ * @example
47
+ * import { Kbd, KbdGroup } from "@/components/ui/kbd";
48
+ *
49
+ * <KbdGroup>
50
+ * <Kbd>Ctrl</Kbd>
51
+ * <Kbd>K</Kbd>
52
+ * </KbdGroup>
53
+ * @since 1.0.0
54
+ * @see {@link Kbd}
55
+ */
56
+ function KbdGroup(props: ComponentProps<"kbd">): JSX.Element {
57
+ const [local, rest] = splitProps(props, ["class", "children"]);
58
+ return (
59
+ <kbd data-slot="kbd-group" class={cn("inline-flex items-center gap-1", local.class)} {...rest}>
60
+ {local.children}
61
+ </kbd>
62
+ );
63
+ }
64
+
65
+ export { Kbd, KbdGroup };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Label",
3
+ "description": "Renders an accessible label associated with controls."
4
+ }
@@ -0,0 +1,60 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ Label as LabelPrimitive,
4
+ type LabelProps as LabelPrimitiveProps,
5
+ type PolymorphicTarget,
6
+ } from "@shadow-js/primitives";
7
+ import type { JSX } from "@shadow-js/web";
8
+ import { type ClassValue, cn } from "@/lib/utils";
9
+
10
+ /** The props as the implementation sees them. */
11
+ type Loose = {
12
+ readonly class?: ClassValue;
13
+ readonly [prop: string]: unknown;
14
+ };
15
+
16
+ /**
17
+ * Props of {@link Label}: the primitive's props (`htmlFor`, `as`, and every `label` attribute).
18
+ * @since 1.0.0
19
+ * @see {@link Label}
20
+ */
21
+ export type LabelProps<T extends PolymorphicTarget = "label"> = LabelPrimitiveProps<T>;
22
+
23
+ /**
24
+ * The accessible name of a control: `htmlFor` points at the control's `id`, or the control is
25
+ * wrapped in the label. It dims next to a disabled `peer` and inside a `group` marked
26
+ * `data-disabled="true"`, and a repeated press on it does not select its text.
27
+ *
28
+ * Keyboard and ARIA: the label handles no keys and adds no ARIA attribute. It is a native `label`,
29
+ * so a click on it activates the control it names, as the browser does for any label. The control
30
+ * keeps its own keys.
31
+ *
32
+ * @param props The `label` attributes, plus `htmlFor` (the id of the control) and `as`. `class`
33
+ * adds to the classes.
34
+ * @returns The `label` element.
35
+ * @example
36
+ * import { Input } from "@/components/ui/input";
37
+ * import { Label } from "@/components/ui/label";
38
+ *
39
+ * <div>
40
+ * <Label htmlFor="email">Email</Label>
41
+ * <Input id="email" type="email" />
42
+ * </div>
43
+ * @since 1.0.0
44
+ * @see {@link LabelProps}
45
+ */
46
+ function Label<T extends PolymorphicTarget = "label">(props: LabelProps<T>): JSX.Element {
47
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
48
+ return (
49
+ <LabelPrimitive
50
+ data-slot="label"
51
+ class={cn(
52
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
53
+ local.class,
54
+ )}
55
+ {...rest}
56
+ />
57
+ );
58
+ }
59
+
60
+ export { Label };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Marker",
3
+ "description": "An inline note in a conversation: a status update, a system message, a bordered row or a labeled separator."
4
+ }