@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,284 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ type PolymorphicTarget,
4
+ type PopoverAnchorProps,
5
+ type PopoverCloseProps,
6
+ type PopoverContentProps,
7
+ type PopoverDescriptionProps,
8
+ Popover as PopoverPrimitive,
9
+ type PopoverProps,
10
+ type PopoverTitleProps,
11
+ type PopoverTriggerProps,
12
+ } from "@shadow-js/primitives";
13
+ import type { ComponentProps, JSX } from "@shadow-js/web";
14
+ import { cn } from "@/lib/utils";
15
+
16
+ /**
17
+ * A non-modal dialog anchored to its trigger. Controlled with `open` and `onOpenChange`, or
18
+ * uncontrolled with `defaultOpen`. Pass `modal` for the modal form, which traps focus inside the
19
+ * popover and blocks the rest of the page.
20
+ *
21
+ * Keyboard and ARIA: follows the WAI-ARIA dialog pattern. Enter or Space on the trigger toggles it;
22
+ * focus moves into the content when it opens; Escape closes it and returns focus to the trigger; a
23
+ * press outside closes it. Tab moves through the content and then on to the next control after the
24
+ * trigger. The content is a `dialog`; name it with a {@link PopoverTitle} or with `aria-label`.
25
+ *
26
+ * @param props The popover's options: `open`, `defaultOpen`, `onOpenChange` and `modal`.
27
+ * @returns The popover's context around its children; it renders no element of its own.
28
+ * @example
29
+ * import { Button } from "@/components/ui/button";
30
+ * import {
31
+ * Popover,
32
+ * PopoverContent,
33
+ * PopoverDescription,
34
+ * PopoverHeader,
35
+ * PopoverTitle,
36
+ * PopoverTrigger,
37
+ * } from "@/components/ui/popover";
38
+ *
39
+ * <Popover>
40
+ * <PopoverTrigger as={Button} variant="outline">Dimensions</PopoverTrigger>
41
+ * <PopoverContent>
42
+ * <PopoverHeader>
43
+ * <PopoverTitle>Dimensions</PopoverTitle>
44
+ * <PopoverDescription>Set the dimensions for the layer.</PopoverDescription>
45
+ * </PopoverHeader>
46
+ * </PopoverContent>
47
+ * </Popover>
48
+ * @since 1.0.0
49
+ * @see {@link PopoverTrigger}
50
+ */
51
+ function Popover(props: PopoverProps): JSX.Element {
52
+ return <PopoverPrimitive {...props} />;
53
+ }
54
+
55
+ /**
56
+ * The button that toggles the popover. It is also the popover's anchor, unless a
57
+ * {@link PopoverAnchor} is rendered.
58
+ *
59
+ * Keyboard and ARIA: a button with `aria-haspopup="dialog"`, `aria-expanded`, and `aria-controls`
60
+ * while the popover is open. Enter or Space toggles it. Its name is its text.
61
+ *
62
+ * shadcn/ui differences: `as` replaces `asChild`, as in `<PopoverTrigger as={Button}>`.
63
+ *
64
+ * @param props The button's attributes, plus `as` (the element to render in its place).
65
+ * @returns The trigger element.
66
+ * @throws Error when rendered outside a {@link Popover}.
67
+ * @example
68
+ * import { Button } from "@/components/ui/button";
69
+ * import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
70
+ *
71
+ * <Popover>
72
+ * <PopoverTrigger as={Button}>Open</PopoverTrigger>
73
+ * <PopoverContent aria-label="Hello">Hello</PopoverContent>
74
+ * </Popover>
75
+ * @since 1.0.0
76
+ * @see {@link Popover}
77
+ */
78
+ function PopoverTrigger<T extends PolymorphicTarget = "button">(
79
+ props: PopoverTriggerProps<T>,
80
+ ): JSX.Element {
81
+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
82
+ }
83
+
84
+ /**
85
+ * An element to anchor the popover to instead of the trigger, such as a toolbar that the popover
86
+ * should open from while the trigger sits elsewhere.
87
+ *
88
+ * Keyboard and ARIA: no keys and no ARIA attributes; it only sets the position the popover uses.
89
+ *
90
+ * @param props The anchor's attributes, plus `as` (the element to render) and `virtualRef` (an
91
+ * element that is not in the page, to anchor to instead).
92
+ * @returns The anchor element, or nothing when `virtualRef` is given.
93
+ * @throws Error when rendered outside a {@link Popover}.
94
+ * @example
95
+ * import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
96
+ *
97
+ * <Popover>
98
+ * <PopoverAnchor class="toolbar">Toolbar</PopoverAnchor>
99
+ * <PopoverTrigger>Open</PopoverTrigger>
100
+ * <PopoverContent aria-label="Tools">Tool list</PopoverContent>
101
+ * </Popover>
102
+ * @since 1.0.0
103
+ * @see {@link PopoverTrigger}
104
+ */
105
+ function PopoverAnchor<T extends PolymorphicTarget = "div">(
106
+ props: PopoverAnchorProps<T>,
107
+ ): JSX.Element {
108
+ return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
109
+ }
110
+
111
+ /**
112
+ * The popover panel, rendered into `document.body` and placed against the anchor. It defaults to
113
+ * `align="center"` and `sideOffset={4}`.
114
+ *
115
+ * Keyboard and ARIA: `role="dialog"`. It is named by a {@link PopoverTitle} (`aria-labelledby`), or
116
+ * by an `aria-label` you pass when there is no title; give it one or the other. It is described by a
117
+ * {@link PopoverDescription} when one is rendered. Escape closes it, and so does a press outside it.
118
+ *
119
+ * @param props `align` (default `"center"`), `sideOffset` (default `4`) and `class`, which adds to
120
+ * the panel's classes. The rest are the attributes of the panel.
121
+ * @returns The panel, portaled into the page's body.
122
+ * @throws Error when rendered outside a {@link Popover}.
123
+ * @example
124
+ * import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
125
+ *
126
+ * <Popover>
127
+ * <PopoverTrigger>Details</PopoverTrigger>
128
+ * <PopoverContent align="start" class="w-80" aria-label="Details">
129
+ * Details of the order.
130
+ * </PopoverContent>
131
+ * </Popover>
132
+ * @since 1.0.0
133
+ * @see {@link PopoverTitle}
134
+ */
135
+ function PopoverContent(props: PopoverContentProps): JSX.Element {
136
+ const [local, rest] = splitProps(props, ["class", "align", "sideOffset"]);
137
+ return (
138
+ <PopoverPrimitive.Portal>
139
+ <PopoverPrimitive.Content
140
+ data-slot="popover-content"
141
+ align={local.align ?? "center"}
142
+ sideOffset={local.sideOffset ?? 4}
143
+ class={cn(
144
+ "z-50 w-72 origin-(--primitives-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden 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 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",
145
+ local.class,
146
+ )}
147
+ {...rest}
148
+ />
149
+ </PopoverPrimitive.Portal>
150
+ );
151
+ }
152
+
153
+ /**
154
+ * A button that closes the popover. It is `type="button"`, so it never submits a form around it.
155
+ *
156
+ * Keyboard and ARIA: a native button; Enter or Space closes the popover. Its name is its text, so a
157
+ * button with only an icon needs an `aria-label`.
158
+ *
159
+ * shadcn/ui differences: `as` replaces `asChild`.
160
+ *
161
+ * @param props The button's attributes, plus `as` (the element to render in its place).
162
+ * @returns The close element.
163
+ * @throws Error when rendered outside a {@link Popover}.
164
+ * @example
165
+ * import { Button } from "@/components/ui/button";
166
+ * import { Popover, PopoverClose, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
167
+ *
168
+ * <Popover>
169
+ * <PopoverTrigger>Note</PopoverTrigger>
170
+ * <PopoverContent aria-label="Note">
171
+ * <PopoverClose as={Button} variant="ghost">Done</PopoverClose>
172
+ * </PopoverContent>
173
+ * </Popover>
174
+ * @since 1.0.0
175
+ * @see {@link Popover}
176
+ */
177
+ function PopoverClose<T extends PolymorphicTarget = "button">(
178
+ props: PopoverCloseProps<T>,
179
+ ): JSX.Element {
180
+ return <PopoverPrimitive.Close data-slot="popover-close" {...props} />;
181
+ }
182
+
183
+ /**
184
+ * The layout of the title and the description at the top of the popover: a `div` in a column.
185
+ *
186
+ * Keyboard and ARIA: no keys and no role of its own.
187
+ *
188
+ * @param props The `div` attributes. `class` adds to the classes.
189
+ * @returns The header `div`.
190
+ * @example
191
+ * import { PopoverDescription, PopoverHeader, PopoverTitle } from "@/components/ui/popover";
192
+ *
193
+ * <PopoverHeader>
194
+ * <PopoverTitle>Title</PopoverTitle>
195
+ * <PopoverDescription>Description</PopoverDescription>
196
+ * </PopoverHeader>
197
+ * @since 1.0.0
198
+ * @see {@link PopoverTitle}
199
+ */
200
+ function PopoverHeader(props: ComponentProps<"div">): JSX.Element {
201
+ const [local, rest] = splitProps(props, ["class"]);
202
+ return (
203
+ <div
204
+ data-slot="popover-header"
205
+ class={cn("flex flex-col gap-1 text-sm", local.class)}
206
+ {...rest}
207
+ />
208
+ );
209
+ }
210
+
211
+ /**
212
+ * The title of the popover: an `h2` that names the dialog. Render it inside the content, and the
213
+ * dialog's `aria-labelledby` points at it while it is rendered.
214
+ *
215
+ * Keyboard and ARIA: no keys of its own; it provides the dialog's accessible name.
216
+ *
217
+ * @param props The `h2` attributes. `class` adds to the classes.
218
+ * @returns The title `h2`.
219
+ * @throws Error when rendered outside a {@link Popover}.
220
+ * @example
221
+ * import { PopoverContent, PopoverTitle, PopoverTrigger, Popover } from "@/components/ui/popover";
222
+ *
223
+ * <Popover>
224
+ * <PopoverTrigger>Dimensions</PopoverTrigger>
225
+ * <PopoverContent>
226
+ * <PopoverTitle>Dimensions</PopoverTitle>
227
+ * </PopoverContent>
228
+ * </Popover>
229
+ * @since 1.0.0
230
+ * @see {@link PopoverContent}
231
+ */
232
+ function PopoverTitle(props: PopoverTitleProps): JSX.Element {
233
+ const [local, rest] = splitProps(props, ["class"]);
234
+ return (
235
+ <PopoverPrimitive.Title
236
+ data-slot="popover-title"
237
+ class={cn("font-medium", local.class)}
238
+ {...rest}
239
+ />
240
+ );
241
+ }
242
+
243
+ /**
244
+ * The description of the popover: a `p` that describes the dialog. The dialog's `aria-describedby`
245
+ * points at it while it is rendered.
246
+ *
247
+ * Keyboard and ARIA: no keys of its own; it provides the dialog's accessible description.
248
+ *
249
+ * @param props The `p` attributes. `class` adds to the classes.
250
+ * @returns The description `p`.
251
+ * @throws Error when rendered outside a {@link Popover}.
252
+ * @example
253
+ * import { Popover, PopoverContent, PopoverDescription, PopoverTrigger } from "@/components/ui/popover";
254
+ *
255
+ * <Popover>
256
+ * <PopoverTrigger>Dimensions</PopoverTrigger>
257
+ * <PopoverContent aria-label="Dimensions">
258
+ * <PopoverDescription>Set the dimensions for the layer.</PopoverDescription>
259
+ * </PopoverContent>
260
+ * </Popover>
261
+ * @since 1.0.0
262
+ * @see {@link PopoverTitle}
263
+ */
264
+ function PopoverDescription(props: PopoverDescriptionProps): JSX.Element {
265
+ const [local, rest] = splitProps(props, ["class"]);
266
+ return (
267
+ <PopoverPrimitive.Description
268
+ data-slot="popover-description"
269
+ class={cn("text-muted-foreground", local.class)}
270
+ {...rest}
271
+ />
272
+ );
273
+ }
274
+
275
+ export {
276
+ Popover,
277
+ PopoverAnchor,
278
+ PopoverClose,
279
+ PopoverContent,
280
+ PopoverDescription,
281
+ PopoverHeader,
282
+ PopoverTitle,
283
+ PopoverTrigger,
284
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Progress",
3
+ "description": "A compact or composable progress bar with a track, indicator, visible label and value."
4
+ }
@@ -0,0 +1,190 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ type PolymorphicTarget,
4
+ type ProgressIndicatorProps as PrimitiveIndicatorProps,
5
+ type ProgressLabelProps as PrimitiveLabelProps,
6
+ type ProgressValueProps as PrimitiveValueProps,
7
+ Progress as ProgressPrimitive,
8
+ type ProgressProps as ProgressPrimitiveProps,
9
+ progressContext,
10
+ } from "@shadow-js/primitives";
11
+ import type { ComponentProps, JSX } from "@shadow-js/web";
12
+ import { type ClassValue, cn } from "@/lib/utils";
13
+
14
+ /** The props as the implementation sees them. */
15
+ type Loose = {
16
+ readonly class?: ClassValue;
17
+ readonly value?: number | null | undefined;
18
+ readonly max?: number | undefined;
19
+ readonly [prop: string]: unknown;
20
+ };
21
+
22
+ /**
23
+ * Props of {@link Progress}: the primitive's props (`value`, `max`, `getValueLabel`) and the
24
+ * attributes of the rendered `div`.
25
+ * @since 1.0.0
26
+ * @see {@link Progress}
27
+ */
28
+ export type ProgressProps<T extends PolymorphicTarget = "div"> = ProgressPrimitiveProps<T>;
29
+
30
+ /** Shared track styling for the compact and composable forms. */
31
+ const TRACK_CLASS =
32
+ "relative h-2 w-full overflow-hidden rounded-full bg-primary/20 forced-colors:border forced-colors:border-[ButtonText] forced-colors:bg-[Canvas]";
33
+
34
+ /**
35
+ * A bar showing how far a task has got. `value` runs from 0 to `max` (default 100). Without a valid
36
+ * `value` the bar is indeterminate: a 40% segment sweeps back and forth, and it stays still under
37
+ * reduced motion. The bar fills from the start edge in both reading directions.
38
+ * Supplying children composes the parts: `ProgressLabel`, `ProgressValue`, and a `ProgressTrack`
39
+ * containing `ProgressIndicator`. Without a children prop the compact track and fill are supplied.
40
+ *
41
+ * Keyboard and ARIA: the bar is a `role="progressbar"` with `aria-valuemin="0"`, `aria-valuemax`,
42
+ * and `aria-valuenow` and `aria-valuetext`, which are left out while it is indeterminate. It is not
43
+ * focusable and has no keys. Name it with `aria-label` or `aria-labelledby`, because a bar with no
44
+ * name does not say what it measures.
45
+ *
46
+ * @param props `value` (a number, or `null` for indeterminate), `max`, and `getValueLabel`. `class`
47
+ * adds to the classes of the track; the rest are the `div` attributes.
48
+ * @returns The progress bar, with its automatic indicator or the supplied parts.
49
+ * @example
50
+ * import { Progress } from "@/components/ui/progress";
51
+ *
52
+ * <Progress value={33} aria-label="Upload" />
53
+ * @since 1.0.0
54
+ * @see {@link ProgressProps}
55
+ */
56
+ function Progress<T extends PolymorphicTarget = "div">(props: ProgressProps<T>): JSX.Element {
57
+ const [local, rest] = splitProps(props as unknown as Loose, [
58
+ "class",
59
+ "value",
60
+ "max",
61
+ "children",
62
+ ]);
63
+ // Inspect presence without evaluating children before the primitive provides their context.
64
+ const composed = Object.hasOwn(props, "children");
65
+ return (
66
+ <ProgressPrimitive
67
+ data-slot="progress"
68
+ value={local.value}
69
+ max={local.max}
70
+ class={cn(composed ? "grid gap-2" : TRACK_CLASS, local.class)}
71
+ {...rest}
72
+ >
73
+ {composed ? (local["children"] as JSX.Element) : <ProgressIndicator />}
74
+ </ProgressPrimitive>
75
+ );
76
+ }
77
+
78
+ /**
79
+ * The clipped surface of a composable progress bar. Keyboard and ARIA: decorative; the root
80
+ * carries the progressbar role.
81
+ * @param props Div attributes and track classes.
82
+ * @returns The track element.
83
+ * @example
84
+ * import { Progress, ProgressIndicator, ProgressTrack } from "@/components/ui/progress";
85
+ * <Progress value={40} aria-label="Upload"><ProgressTrack><ProgressIndicator /></ProgressTrack></Progress>
86
+ * @see {@link Progress}
87
+ * @since 1.0.0
88
+ */
89
+ function ProgressTrack(props: ComponentProps<"div">): JSX.Element {
90
+ const [local, rest] = splitProps(props, ["class", "children"]);
91
+ return (
92
+ <div data-slot="progress-track" class={cn(TRACK_CLASS, local.class)} {...rest}>
93
+ {local.children}
94
+ </div>
95
+ );
96
+ }
97
+
98
+ /** Props of {@link ProgressIndicator}. @see {@link ProgressIndicator} @since 1.0.0 */
99
+ export type ProgressIndicatorProps<T extends PolymorphicTarget = "div"> =
100
+ PrimitiveIndicatorProps<T>;
101
+
102
+ /**
103
+ * The fill of a progress bar, sized from its root's value and maximum. Indeterminate bars use
104
+ * the sweeping animation; reduced motion disables movement. Keyboard and ARIA: decorative.
105
+ * @param props Primitive indicator attributes and classes.
106
+ * @returns The indicator element.
107
+ * @example
108
+ * import { Progress, ProgressIndicator, ProgressTrack } from "@/components/ui/progress";
109
+ * <Progress value={40} aria-label="Upload"><ProgressTrack><ProgressIndicator /></ProgressTrack></Progress>
110
+ * @see {@link ProgressTrack}
111
+ * @since 1.0.0
112
+ */
113
+ function ProgressIndicator<T extends PolymorphicTarget = "div">(
114
+ props: ProgressIndicatorProps<T>,
115
+ ): JSX.Element {
116
+ const context = progressContext.use("ProgressIndicator");
117
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
118
+ return (
119
+ <ProgressPrimitive.Indicator
120
+ data-slot="progress-indicator"
121
+ class={cn(
122
+ "h-full bg-primary transition-[width] motion-reduce:transition-none data-[state=indeterminate]:w-2/5 data-[state=indeterminate]:animate-progress-indeterminate data-[state=indeterminate]:motion-reduce:animate-none forced-colors:bg-[Highlight]",
123
+ local.class,
124
+ )}
125
+ style={{
126
+ width:
127
+ context.value() === null
128
+ ? undefined
129
+ : `${((context.value() ?? 0) / context.max()) * 100}%`,
130
+ }}
131
+ {...rest}
132
+ />
133
+ );
134
+ }
135
+
136
+ /** Props of {@link ProgressLabel}. @see {@link ProgressLabel} @since 1.0.0 */
137
+ export type ProgressLabelProps<T extends PolymorphicTarget = "span"> = PrimitiveLabelProps<T>;
138
+
139
+ /**
140
+ * A visible label that names the root while mounted. Keyboard: none; ARIA labeling is supplied
141
+ * by the primitive.
142
+ * @param props Primitive label attributes and classes.
143
+ * @returns The label element.
144
+ * @example
145
+ * import { Progress, ProgressLabel, ProgressTrack, ProgressIndicator } from "@/components/ui/progress";
146
+ * <Progress value={40}><ProgressLabel>Upload</ProgressLabel><ProgressTrack><ProgressIndicator /></ProgressTrack></Progress>
147
+ * @see {@link Progress}
148
+ * @since 1.0.0
149
+ */
150
+ function ProgressLabel<T extends PolymorphicTarget = "span">(
151
+ props: ProgressLabelProps<T>,
152
+ ): JSX.Element {
153
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
154
+ return (
155
+ <ProgressPrimitive.Label
156
+ data-slot="progress-label"
157
+ class={cn("text-sm font-medium", local.class)}
158
+ {...rest}
159
+ />
160
+ );
161
+ }
162
+
163
+ /** Props of {@link ProgressValue}. @see {@link ProgressValue} @since 1.0.0 */
164
+ export type ProgressValueProps<T extends PolymorphicTarget = "span"> = PrimitiveValueProps<T>;
165
+
166
+ /**
167
+ * The root's visible percentage or custom value text, empty while indeterminate. Keyboard:
168
+ * none; the root announces the same text through `aria-valuetext`.
169
+ * @param props Primitive value attributes and classes; children override the default text.
170
+ * @returns The value element.
171
+ * @example
172
+ * import { Progress, ProgressLabel, ProgressValue, ProgressTrack, ProgressIndicator } from "@/components/ui/progress";
173
+ * <Progress value={40}><ProgressLabel>Upload</ProgressLabel><ProgressValue /><ProgressTrack><ProgressIndicator /></ProgressTrack></Progress>
174
+ * @see {@link Progress}
175
+ * @since 1.0.0
176
+ */
177
+ function ProgressValue<T extends PolymorphicTarget = "span">(
178
+ props: ProgressValueProps<T>,
179
+ ): JSX.Element {
180
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
181
+ return (
182
+ <ProgressPrimitive.Value
183
+ data-slot="progress-value"
184
+ class={cn("text-sm text-muted-foreground tabular-nums", local.class)}
185
+ {...rest}
186
+ />
187
+ );
188
+ }
189
+
190
+ export { Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Questionnaire",
3
+ "description": "A multi-step form of single-choice, multiple-choice, free-text and skippable questions, one at a time, with progress, validation and keyboard shortcuts."
4
+ }