@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,571 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ type PolymorphicTarget,
4
+ type SelectContentProps as SelectContentPrimitiveProps,
5
+ type SelectGroupProps,
6
+ SelectItemAlignedContent,
7
+ type SelectItemProps as SelectItemPrimitiveProps,
8
+ type SelectLabelProps,
9
+ Select as SelectPrimitive,
10
+ type SelectProps,
11
+ type SelectScrollButtonProps,
12
+ type SelectSeparatorProps,
13
+ type SelectTriggerProps as SelectTriggerPrimitiveProps,
14
+ type SelectValueProps,
15
+ } from "@shadow-js/primitives";
16
+ import type { JSX } from "@shadow-js/web";
17
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "@/components/ui/icons";
18
+ import { type ClassValue, cn } from "@/lib/utils";
19
+
20
+ /** The props as the implementation sees them. */
21
+ type Loose = {
22
+ readonly class?: ClassValue;
23
+ readonly children?: JSX.Element;
24
+ readonly [prop: string]: unknown;
25
+ };
26
+
27
+ /**
28
+ * A control that picks one value from a list shown in a popup, like a native `select` with a styled
29
+ * listbox. Put a {@link SelectTrigger} (with a {@link SelectValue}) and a {@link SelectContent} of
30
+ * {@link SelectItem}s inside it. Control it with `value` and `onValueChange`, and `open` and
31
+ * `onOpenChange`, or start it with `defaultValue` and `defaultOpen`. With a `name` it takes part in
32
+ * native forms. The value `""` means nothing is selected and shows the placeholder, so no item may
33
+ * use `""`.
34
+ *
35
+ * Keyboard and ARIA: the trigger is a combobox that opens the list with Enter, Space, ArrowDown or
36
+ * ArrowUp. Inside the list, the arrow keys, Home and End move between items, Enter and Space select
37
+ * the focused item, Escape closes the list without changing the value, and focus returns to the
38
+ * trigger. Name the trigger with a `Label` (`id` on the trigger, `htmlFor` on the label) or with
39
+ * `aria-label`. See {@link SelectTrigger} and {@link SelectContent}.
40
+ *
41
+ * shadcn/ui differences: `class` replaces `className`. `position` is `"popper"` (the default) or
42
+ * `"item-aligned"`, which puts the selected item over the trigger like a native select.
43
+ *
44
+ * @param props - The props, see {@link SelectProps}.
45
+ * @returns The select context around its parts. It renders no element of its own, except the hidden
46
+ * native `select` when `name` is set.
47
+ * @example
48
+ * ```tsx
49
+ * import {
50
+ * Select,
51
+ * SelectContent,
52
+ * SelectGroup,
53
+ * SelectItem,
54
+ * SelectLabel,
55
+ * SelectTrigger,
56
+ * SelectValue,
57
+ * } from "@/components/ui/select";
58
+ *
59
+ * export function FruitPicker() {
60
+ * return (
61
+ * <Select defaultValue="apple">
62
+ * <SelectTrigger class="w-[180px]" aria-label="Fruit">
63
+ * <SelectValue placeholder="Select a fruit" />
64
+ * </SelectTrigger>
65
+ * <SelectContent>
66
+ * <SelectGroup>
67
+ * <SelectLabel>Fruits</SelectLabel>
68
+ * <SelectItem value="apple">Apple</SelectItem>
69
+ * <SelectItem value="banana">Banana</SelectItem>
70
+ * </SelectGroup>
71
+ * </SelectContent>
72
+ * </Select>
73
+ * );
74
+ * }
75
+ * ```
76
+ * @since 1.0.0
77
+ * @see {@link SelectTrigger}
78
+ */
79
+ function Select(props: SelectProps): JSX.Element {
80
+ return <SelectPrimitive {...props} />;
81
+ }
82
+
83
+ /**
84
+ * A group of related items under a {@link SelectLabel}. It is a `role="group"` named by its label.
85
+ *
86
+ * Keyboard and ARIA: no keys of its own. The group is named by its {@link SelectLabel}.
87
+ *
88
+ * shadcn/ui differences: `class` replaces `className`.
89
+ *
90
+ * @param props - The props: the attributes of the group element.
91
+ * @returns The group element.
92
+ * @example
93
+ * ```tsx
94
+ * import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel } from "@/components/ui/select";
95
+ *
96
+ * export function FruitList() {
97
+ * return (
98
+ * <Select>
99
+ * <SelectContent>
100
+ * <SelectGroup>
101
+ * <SelectLabel>Fruits</SelectLabel>
102
+ * <SelectItem value="apple">Apple</SelectItem>
103
+ * </SelectGroup>
104
+ * </SelectContent>
105
+ * </Select>
106
+ * );
107
+ * }
108
+ * ```
109
+ * @since 1.0.0
110
+ * @see {@link SelectLabel}
111
+ */
112
+ function SelectGroup<T extends PolymorphicTarget = "div">(props: SelectGroupProps<T>): JSX.Element {
113
+ return <SelectPrimitive.Group data-slot="select-group" {...props} />;
114
+ }
115
+
116
+ /**
117
+ * The text of the selected item, shown inside the trigger, or `placeholder` while nothing is
118
+ * selected. The text comes from the item itself, so it is known before the list is ever opened.
119
+ *
120
+ * Keyboard and ARIA: no keys. It is the trigger's visible value, so name the trigger, not this
121
+ * part. Server markup contains the selected text only when you pass it as `children`, which is then
122
+ * shown while a value is selected: `<SelectValue placeholder="Pick">{label}</SelectValue>`.
123
+ *
124
+ * shadcn/ui differences: `class` replaces `className`.
125
+ *
126
+ * @param props - The props: `placeholder`, `children`, and the attributes of the value element.
127
+ * @returns The selected text, or the placeholder.
128
+ * @example
129
+ * ```tsx
130
+ * import { Select, SelectTrigger, SelectValue } from "@/components/ui/select";
131
+ *
132
+ * export function FruitTrigger() {
133
+ * return (
134
+ * <Select>
135
+ * <SelectTrigger aria-label="Fruit">
136
+ * <SelectValue placeholder="Select a fruit" />
137
+ * </SelectTrigger>
138
+ * </Select>
139
+ * );
140
+ * }
141
+ * ```
142
+ * @since 1.0.0
143
+ * @see {@link SelectTrigger}
144
+ */
145
+ function SelectValue<T extends PolymorphicTarget = "span">(
146
+ props: SelectValueProps<T>,
147
+ ): JSX.Element {
148
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />;
149
+ }
150
+
151
+ /**
152
+ * Own props of {@link SelectTrigger}.
153
+ * @since 1.0.0
154
+ * @see {@link SelectTrigger}
155
+ */
156
+ export interface SelectTriggerExtraProps {
157
+ /**
158
+ * `default` (36px high) or `sm` (32px).
159
+ * @default "default"
160
+ */
161
+ readonly size?: "sm" | "default" | undefined;
162
+ }
163
+
164
+ /**
165
+ * Props of {@link SelectTrigger}: the attributes of the rendered `button` and `size`.
166
+ * @since 1.0.0
167
+ * @see {@link SelectTrigger}
168
+ */
169
+ export type SelectTriggerProps<T extends PolymorphicTarget = "button"> =
170
+ SelectTriggerPrimitiveProps<T> & SelectTriggerExtraProps;
171
+
172
+ /**
173
+ * The button that opens the list. It shows the selected text and a chevron. `size` sets its height
174
+ * (`"default"` is 36px, `"sm"` is 32px), and `aria-invalid="true"` turns its border destructive.
175
+ *
176
+ * Keyboard and ARIA: a `button` with `role="combobox"`, `aria-haspopup="listbox"` and
177
+ * `aria-expanded`, and `aria-controls` while the list is open. Enter, Space, ArrowDown and ArrowUp
178
+ * open the list. While the list is closed, typing a letter changes the value to the next matching
179
+ * item without opening it. A mouse press opens the list, so you can press, drag onto an item and
180
+ * release; touch opens it on click. Name the trigger with a `Label` (`id` on the trigger, `htmlFor`
181
+ * on the label) or with `aria-label`.
182
+ *
183
+ * shadcn/ui differences: `class` replaces `className`. The `size` values are the same.
184
+ *
185
+ * @param props - The props: `size`, `class`, and the attributes of the trigger button.
186
+ * @returns The trigger button.
187
+ * @example
188
+ * ```tsx
189
+ * import { Select, SelectTrigger, SelectValue } from "@/components/ui/select";
190
+ *
191
+ * export function SmallFruit() {
192
+ * return (
193
+ * <Select>
194
+ * <SelectTrigger size="sm" id="fruit" aria-label="Fruit">
195
+ * <SelectValue placeholder="Fruit" />
196
+ * </SelectTrigger>
197
+ * </Select>
198
+ * );
199
+ * }
200
+ * ```
201
+ * @since 1.0.0
202
+ * @see {@link SelectContent}
203
+ */
204
+ function SelectTrigger<T extends PolymorphicTarget = "button">(
205
+ props: SelectTriggerProps<T>,
206
+ ): JSX.Element {
207
+ const [local, rest] = splitProps(props as unknown as Loose & SelectTriggerExtraProps, [
208
+ "class",
209
+ "size",
210
+ "children",
211
+ ]);
212
+ return (
213
+ <SelectPrimitive.Trigger
214
+ data-slot="select-trigger"
215
+ data-size={local.size ?? "default"}
216
+ class={cn(
217
+ "flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none 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 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
218
+ local.class,
219
+ )}
220
+ {...rest}
221
+ >
222
+ {local.children}
223
+ <SelectPrimitive.Icon class="flex">
224
+ <ChevronDownIcon class="size-4 opacity-50" />
225
+ </SelectPrimitive.Icon>
226
+ </SelectPrimitive.Trigger>
227
+ );
228
+ }
229
+
230
+ /**
231
+ * Own props of {@link SelectContent}.
232
+ * @since 1.0.0
233
+ * @see {@link SelectContent}
234
+ */
235
+ export interface SelectContentExtraProps {
236
+ /**
237
+ * Where the list sits. `"popper"` is below the trigger, flipping to stay in view. `"item-aligned"`
238
+ * puts the selected item (the first enabled one when nothing is selected) over the trigger's text, as
239
+ * Radix's item-aligned position and a native select do; `side`, `align` and `sideOffset` are then ignored.
240
+ * @default "popper"
241
+ */
242
+ readonly position?: "popper" | "item-aligned" | undefined;
243
+ }
244
+
245
+ /**
246
+ * Props of {@link SelectContent}: the primitive's placement props (`side`, `align`, `sideOffset`,
247
+ * `collisionPadding`...), `position` and the attributes of the rendered `div`.
248
+ * @since 1.0.0
249
+ * @see {@link SelectContent}
250
+ */
251
+ export type SelectContentProps<T extends PolymorphicTarget = "div"> =
252
+ SelectContentPrimitiveProps<T> & SelectContentExtraProps;
253
+
254
+ /**
255
+ * The popup list of items. It is rendered into `document.body`, placed below the trigger by default
256
+ * (flipping to stay in view), at least as wide as the trigger, and it scrolls when the items do not
257
+ * fit, with scroll buttons at both ends. While open it is modal, like a native select.
258
+ *
259
+ * Keyboard and ARIA: the list is a `role="listbox"` with `aria-orientation="vertical"`, and it takes
260
+ * focus on open (the selected item, else the first enabled one). ArrowDown, ArrowUp, Home, End,
261
+ * PageDown and PageUp move focus, without wrapping. Enter and Space select the focused item, and
262
+ * typing jumps to the next item that starts with the typed text. Escape closes the list, and Tab
263
+ * closes it without selecting. Focus returns to the trigger on close.
264
+ *
265
+ * shadcn/ui differences: `class` replaces `className`. `position` is `"popper"` (the default here;
266
+ * Radix's default is item-aligned) or `"item-aligned"`.
267
+ *
268
+ * @param props - The props: `side`, `align`, `sideOffset`, `position`, `class`, and the attributes of
269
+ * the list element.
270
+ * @returns The list in a portal.
271
+ * @example
272
+ * ```tsx
273
+ * import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
274
+ *
275
+ * export function TopList() {
276
+ * return (
277
+ * <Select>
278
+ * <SelectTrigger aria-label="Fruit">
279
+ * <SelectValue placeholder="Pick" />
280
+ * </SelectTrigger>
281
+ * <SelectContent side="top">
282
+ * <SelectItem value="apple">Apple</SelectItem>
283
+ * </SelectContent>
284
+ * </Select>
285
+ * );
286
+ * }
287
+ * ```
288
+ * @since 1.0.0
289
+ * @see {@link SelectItem}
290
+ */
291
+ function SelectContent<T extends PolymorphicTarget = "div">(
292
+ props: SelectContentProps<T>,
293
+ ): JSX.Element {
294
+ const [local, rest] = splitProps(props as unknown as Loose & SelectContentExtraProps, [
295
+ "class",
296
+ "children",
297
+ "position",
298
+ ]);
299
+ // The aligned content is its own export, so a select that never aligns does not carry its code.
300
+ const Part = (
301
+ local.position === "item-aligned" ? SelectItemAlignedContent : SelectPrimitive.Content
302
+ ) as typeof SelectPrimitive.Content;
303
+ return (
304
+ <SelectPrimitive.Portal>
305
+ <Part
306
+ data-slot="select-content"
307
+ style={{ "min-width": "max(8rem, var(--primitives-anchor-width))" }}
308
+ class={cn(
309
+ "relative z-50 max-h-(--primitives-available-height) min-w-[8rem] origin-(--primitives-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
310
+ (local.position ?? "popper") === "popper" &&
311
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
312
+ local.class,
313
+ )}
314
+ {...rest}
315
+ >
316
+ <SelectScrollUpButton />
317
+ <SelectPrimitive.Viewport class="w-full min-w-(--primitives-anchor-width) scroll-my-1 p-1">
318
+ {local.children}
319
+ </SelectPrimitive.Viewport>
320
+ <SelectScrollDownButton />
321
+ </Part>
322
+ </SelectPrimitive.Portal>
323
+ );
324
+ }
325
+
326
+ /**
327
+ * The visible heading of a {@link SelectGroup}. It is not an option: it cannot be focused or
328
+ * selected.
329
+ *
330
+ * Keyboard and ARIA: no keys. The label names its group.
331
+ *
332
+ * shadcn/ui differences: `class` replaces `className`.
333
+ *
334
+ * @param props - The props: the attributes of the label element, and `class`.
335
+ * @returns The label element.
336
+ * @example
337
+ * ```tsx
338
+ * import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel } from "@/components/ui/select";
339
+ *
340
+ * export function Labeled() {
341
+ * return (
342
+ * <Select>
343
+ * <SelectContent>
344
+ * <SelectGroup>
345
+ * <SelectLabel>Fruits</SelectLabel>
346
+ * <SelectItem value="apple">Apple</SelectItem>
347
+ * </SelectGroup>
348
+ * </SelectContent>
349
+ * </Select>
350
+ * );
351
+ * }
352
+ * ```
353
+ * @since 1.0.0
354
+ * @see {@link SelectGroup}
355
+ */
356
+ function SelectLabel<T extends PolymorphicTarget = "div">(props: SelectLabelProps<T>): JSX.Element {
357
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
358
+ return (
359
+ <SelectPrimitive.Label
360
+ data-slot="select-label"
361
+ class={cn("px-2 py-1.5 text-xs text-muted-foreground", local.class)}
362
+ {...rest}
363
+ />
364
+ );
365
+ }
366
+
367
+ /**
368
+ * Props of {@link SelectItem}: the primitive's props (`value`, `disabled`, `textValue`) and the
369
+ * attributes of the rendered `div`.
370
+ * @since 1.0.0
371
+ * @see {@link SelectItem}
372
+ */
373
+ export type SelectItemProps<T extends PolymorphicTarget = "div"> = SelectItemPrimitiveProps<T>;
374
+
375
+ /**
376
+ * One option of a {@link SelectContent}. Its text names the option, is what the trigger shows once
377
+ * it is chosen, and is what typing matches. A check mark marks the selected option.
378
+ *
379
+ * Keyboard and ARIA: `role="option"` with `aria-selected`, named by its text, and `aria-disabled`
380
+ * while disabled. Enter and Space select it. A mouse selects on release, and touch selects on tap.
381
+ * Moving the pointer over an item focuses it. A `disabled` item cannot be picked and is skipped by
382
+ * the arrow keys.
383
+ *
384
+ * shadcn/ui differences: `class` replaces `className`.
385
+ *
386
+ * @param props - The props: `value` (required), `disabled`, `textValue`, and the attributes of the
387
+ * option element.
388
+ * @returns The option element.
389
+ * @example
390
+ * ```tsx
391
+ * import { Select, SelectContent, SelectItem } from "@/components/ui/select";
392
+ *
393
+ * export function Apple() {
394
+ * return (
395
+ * <Select>
396
+ * <SelectContent>
397
+ * <SelectItem value="apple">Apple</SelectItem>
398
+ * </SelectContent>
399
+ * </Select>
400
+ * );
401
+ * }
402
+ * ```
403
+ * @since 1.0.0
404
+ * @see {@link SelectContent}
405
+ */
406
+ function SelectItem<T extends PolymorphicTarget = "div">(props: SelectItemProps<T>): JSX.Element {
407
+ const [local, rest] = splitProps(props as unknown as Loose & { readonly value: string }, [
408
+ "class",
409
+ "children",
410
+ "value",
411
+ ]);
412
+ return (
413
+ <SelectPrimitive.Item
414
+ data-slot="select-item"
415
+ value={local.value}
416
+ class={cn(
417
+ "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 ps-2 pe-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 forced-colors:focus:bg-[Highlight] forced-colors:focus:text-[HighlightText] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
418
+ local.class,
419
+ )}
420
+ {...rest}
421
+ >
422
+ <span class="absolute end-2 flex size-3.5 items-center justify-center">
423
+ <SelectPrimitive.ItemIndicator>
424
+ <CheckIcon class="size-4" />
425
+ </SelectPrimitive.ItemIndicator>
426
+ </span>
427
+ <SelectPrimitive.ItemText>{local.children}</SelectPrimitive.ItemText>
428
+ </SelectPrimitive.Item>
429
+ );
430
+ }
431
+
432
+ /**
433
+ * A divider between items or groups.
434
+ *
435
+ * Keyboard and ARIA: no keys. The divider is hidden from assistive technology (`aria-hidden`).
436
+ *
437
+ * shadcn/ui differences: `class` replaces `className`.
438
+ *
439
+ * @param props - The props: the attributes of the divider element, and `class`.
440
+ * @returns The divider element.
441
+ * @example
442
+ * ```tsx
443
+ * import { Select, SelectContent, SelectItem, SelectSeparator } from "@/components/ui/select";
444
+ *
445
+ * export function Dividers() {
446
+ * return (
447
+ * <Select>
448
+ * <SelectContent>
449
+ * <SelectItem value="apple">Apple</SelectItem>
450
+ * <SelectSeparator />
451
+ * <SelectItem value="banana">Banana</SelectItem>
452
+ * </SelectContent>
453
+ * </Select>
454
+ * );
455
+ * }
456
+ * ```
457
+ * @since 1.0.0
458
+ */
459
+ function SelectSeparator<T extends PolymorphicTarget = "div">(
460
+ props: SelectSeparatorProps<T>,
461
+ ): JSX.Element {
462
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
463
+ return (
464
+ <SelectPrimitive.Separator
465
+ data-slot="select-separator"
466
+ class={cn("pointer-events-none -mx-1 my-1 h-px bg-border", local.class)}
467
+ {...rest}
468
+ />
469
+ );
470
+ }
471
+
472
+ /**
473
+ * The button at the top of the list, shown while the list can scroll up. Hovering or pressing it
474
+ * scrolls the list.
475
+ *
476
+ * Keyboard and ARIA: no keys. The button is hidden from assistive technology (`aria-hidden`);
477
+ * keyboard users scroll with the arrow keys.
478
+ *
479
+ * shadcn/ui differences: `class` replaces `className`.
480
+ *
481
+ * @param props - The props: the attributes of the button element, and `class`.
482
+ * @returns The button, or nothing when the list cannot scroll up.
483
+ * @example
484
+ * ```tsx
485
+ * import { Select, SelectContent, SelectItem, SelectScrollUpButton } from "@/components/ui/select";
486
+ *
487
+ * export function LongList() {
488
+ * return (
489
+ * <Select>
490
+ * <SelectContent>
491
+ * <SelectScrollUpButton />
492
+ * <SelectItem value="apple">Apple</SelectItem>
493
+ * </SelectContent>
494
+ * </Select>
495
+ * );
496
+ * }
497
+ * ```
498
+ * @since 1.0.0
499
+ * @see {@link SelectScrollDownButton}
500
+ */
501
+ function SelectScrollUpButton<T extends PolymorphicTarget = "div">(
502
+ props: SelectScrollButtonProps<T>,
503
+ ): JSX.Element {
504
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
505
+ return (
506
+ <SelectPrimitive.ScrollUpButton
507
+ data-slot="select-scroll-up-button"
508
+ class={cn("flex cursor-default items-center justify-center py-1", local.class)}
509
+ {...rest}
510
+ >
511
+ <ChevronUpIcon class="size-4" />
512
+ </SelectPrimitive.ScrollUpButton>
513
+ );
514
+ }
515
+
516
+ /**
517
+ * The button at the bottom of the list, shown while the list can scroll down. Hovering or pressing
518
+ * it scrolls the list.
519
+ *
520
+ * Keyboard and ARIA: no keys. The button is hidden from assistive technology (`aria-hidden`);
521
+ * keyboard users scroll with the arrow keys.
522
+ *
523
+ * shadcn/ui differences: `class` replaces `className`.
524
+ *
525
+ * @param props - The props: the attributes of the button element, and `class`.
526
+ * @returns The button, or nothing when the list cannot scroll down.
527
+ * @example
528
+ * ```tsx
529
+ * import { Select, SelectContent, SelectItem, SelectScrollDownButton } from "@/components/ui/select";
530
+ *
531
+ * export function LongListBottom() {
532
+ * return (
533
+ * <Select>
534
+ * <SelectContent>
535
+ * <SelectItem value="apple">Apple</SelectItem>
536
+ * <SelectScrollDownButton />
537
+ * </SelectContent>
538
+ * </Select>
539
+ * );
540
+ * }
541
+ * ```
542
+ * @since 1.0.0
543
+ * @see {@link SelectScrollUpButton}
544
+ */
545
+ function SelectScrollDownButton<T extends PolymorphicTarget = "div">(
546
+ props: SelectScrollButtonProps<T>,
547
+ ): JSX.Element {
548
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
549
+ return (
550
+ <SelectPrimitive.ScrollDownButton
551
+ data-slot="select-scroll-down-button"
552
+ class={cn("flex cursor-default items-center justify-center py-1", local.class)}
553
+ {...rest}
554
+ >
555
+ <ChevronDownIcon class="size-4" />
556
+ </SelectPrimitive.ScrollDownButton>
557
+ );
558
+ }
559
+
560
+ export {
561
+ Select,
562
+ SelectContent,
563
+ SelectGroup,
564
+ SelectItem,
565
+ SelectLabel,
566
+ SelectScrollDownButton,
567
+ SelectScrollUpButton,
568
+ SelectSeparator,
569
+ SelectTrigger,
570
+ SelectValue,
571
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Separator",
3
+ "description": "Visually or semantically separates content."
4
+ }
@@ -0,0 +1,52 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import { Separator as SeparatorPrimitive, type SeparatorProps } from "@shadow-js/primitives";
3
+ import type { JSX } from "@shadow-js/web";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ /**
7
+ * A thin line that separates content, horizontal (the default) or vertical. It is decorative unless
8
+ * you say otherwise: a decorative separator is removed from the accessibility tree (`role="none"`).
9
+ * Pass `decorative={false}` to expose it as `role="separator"`, and `aria-orientation="vertical"` is
10
+ * then set when it is vertical.
11
+ *
12
+ * Keyboard and ARIA: the separator is not focusable and has no keys. Decorative: `role="none"`, and
13
+ * no `aria-orientation`. Semantic (`decorative={false}`): `role="separator"`, plus
14
+ * `aria-orientation="vertical"` only when vertical, because horizontal is the default of the role.
15
+ * Both carry `data-orientation` (`"horizontal"` or `"vertical"`).
16
+ *
17
+ * shadcn/ui differences: `class` replaces `className`.
18
+ *
19
+ * @param props - The props: `orientation`, `decorative`, and the attributes of the `div`.
20
+ * @returns The separator element.
21
+ * @example
22
+ * ```tsx
23
+ * import { Separator } from "@/components/ui/separator";
24
+ *
25
+ * export function Toolbar() {
26
+ * return (
27
+ * <div class="flex h-5 items-center gap-4">
28
+ * <span>Home</span>
29
+ * <Separator orientation="vertical" />
30
+ * <span>Docs</span>
31
+ * </div>
32
+ * );
33
+ * }
34
+ * ```
35
+ * @since 1.0.0
36
+ */
37
+ function Separator(props: SeparatorProps): JSX.Element {
38
+ const [local, rest] = splitProps(props, ["class", "decorative"]);
39
+ return (
40
+ <SeparatorPrimitive
41
+ data-slot="separator"
42
+ decorative={local.decorative ?? true}
43
+ class={cn(
44
+ "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px forced-colors:bg-[CanvasText]",
45
+ local.class,
46
+ )}
47
+ {...rest}
48
+ />
49
+ );
50
+ }
51
+
52
+ export { Separator };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Sheet",
3
+ "description": "Extends the Dialog component to display content that complements the main content of the screen."
4
+ }