@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,694 @@
1
+ import { splitProps } from "@shadow-js/core";
2
+ import {
3
+ type MenubarContentProps,
4
+ type MenubarMenuProps,
5
+ Menubar as MenubarPrimitive,
6
+ type MenubarProps,
7
+ type MenubarTriggerProps,
8
+ type MenuCheckboxItemProps,
9
+ type MenuGroupProps,
10
+ type MenuItemOwnProps,
11
+ type MenuLabelProps,
12
+ type MenuPortalProps,
13
+ type MenuRadioGroupProps,
14
+ type MenuRadioItemProps,
15
+ type MenuSeparatorProps,
16
+ type MenuSubContentProps,
17
+ type MenuSubProps,
18
+ type MenuSubTriggerProps,
19
+ type PolymorphicProps,
20
+ type PolymorphicTarget,
21
+ } from "@shadow-js/primitives";
22
+ import type { ComponentProps, JSX } from "@shadow-js/web";
23
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "@/components/ui/icons";
24
+ import { type ClassValue, cn } from "@/lib/utils";
25
+
26
+ /** The props as the implementation sees them: the typed own props plus whatever the target takes. */
27
+ type Loose = {
28
+ readonly as?: PolymorphicTarget;
29
+ readonly class?: ClassValue;
30
+ readonly inset?: boolean | undefined;
31
+ readonly variant?: "default" | "destructive" | undefined;
32
+ readonly children?: JSX.Element | undefined;
33
+ readonly [prop: string]: unknown;
34
+ };
35
+
36
+ /** The placement and entrance animation shared by the menu panels. */
37
+ const PANEL =
38
+ "z-50 min-w-[8rem] origin-(--primitives-transform-origin) rounded-md border bg-popover p-1 text-popover-foreground 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";
39
+
40
+ /** What every selectable row shares. */
41
+ const ROW =
42
+ "relative flex cursor-default items-center gap-2 rounded-sm 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] forced-colors:data-[disabled]:text-[GrayText] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
43
+
44
+ /**
45
+ * A horizontal bar of menus (`role="menubar"`). The triggers share one tab stop and move with
46
+ * ArrowLeft and ArrowRight (swapped in right-to-left layouts). While a menu is open, hovering another
47
+ * trigger, or pressing an arrow key, switches to that menu.
48
+ *
49
+ * Keyboard and ARIA: the bar is a `menubar`, and its triggers are `menuitem` buttons. Home and End go
50
+ * to the first and last trigger, and disabled triggers are skipped. Name the bar with `aria-label`,
51
+ * because it has no name of its own. Control the open menu with `value` and `onValueChange` (the
52
+ * value of the open menu), or start uncontrolled with `defaultValue`.
53
+ *
54
+ * shadcn/ui differences: `as` replaces `asChild` on the triggers, items and sub-triggers.
55
+ *
56
+ * @param props The bar's `div` attributes plus `value`, `defaultValue`, `onValueChange`, `dir` and
57
+ * `loop`. `class` adds to the bar's classes.
58
+ * @returns The bar `div` with its menus inside.
59
+ * @example
60
+ * import {
61
+ * Menubar,
62
+ * MenubarContent,
63
+ * MenubarItem,
64
+ * MenubarMenu,
65
+ * MenubarShortcut,
66
+ * MenubarTrigger,
67
+ * } from "@/components/ui/menubar";
68
+ *
69
+ * <Menubar aria-label="Application">
70
+ * <MenubarMenu value="file">
71
+ * <MenubarTrigger>File</MenubarTrigger>
72
+ * <MenubarContent>
73
+ * <MenubarItem>
74
+ * New Tab <MenubarShortcut>Ctrl+T</MenubarShortcut>
75
+ * </MenubarItem>
76
+ * </MenubarContent>
77
+ * </MenubarMenu>
78
+ * </Menubar>
79
+ * @since 1.0.0
80
+ * @see {@link MenubarMenu}
81
+ */
82
+ function Menubar<T extends PolymorphicTarget = "div">(props: MenubarProps<T>): JSX.Element {
83
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
84
+ return (
85
+ <MenubarPrimitive
86
+ data-slot="menubar"
87
+ class={cn(
88
+ "flex h-9 items-center gap-1 rounded-md border bg-background p-1 shadow-xs",
89
+ local.class,
90
+ )}
91
+ {...rest}
92
+ />
93
+ );
94
+ }
95
+
96
+ /**
97
+ * One menu of the bar. It pairs a {@link MenubarTrigger} with its {@link MenubarContent}, and it is
98
+ * open while the bar's `value` equals its `value`. It renders no element and handles no keys; the
99
+ * trigger and the content carry the behavior.
100
+ *
101
+ * @param props `value` names the menu (generated when omitted), and `children` are the trigger, the
102
+ * content and anything else in the menu.
103
+ * @returns The menu's children, with no element of its own.
104
+ * @throws Error when rendered outside a {@link Menubar}.
105
+ * @example
106
+ * import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarTrigger } from "@/components/ui/menubar";
107
+ *
108
+ * <Menubar>
109
+ * <MenubarMenu value="edit">
110
+ * <MenubarTrigger>Edit</MenubarTrigger>
111
+ * <MenubarContent>
112
+ * <MenubarItem>Undo</MenubarItem>
113
+ * </MenubarContent>
114
+ * </MenubarMenu>
115
+ * </Menubar>
116
+ * @since 1.0.0
117
+ * @see {@link MenubarTrigger}
118
+ */
119
+ function MenubarMenu(props: MenubarMenuProps): JSX.Element {
120
+ return <MenubarPrimitive.Menu {...props} />;
121
+ }
122
+
123
+ /**
124
+ * Renders its children into `document.body` (or into `container`) while the menu is open. It is
125
+ * client only: nothing renders on the server. {@link MenubarContent} already renders its panel this
126
+ * way, so use this portal for other content that belongs in the menu's layer.
127
+ *
128
+ * @param props `container` (an element, `null`, or a function that returns one; the default is
129
+ * `document.body`) and `forceMount` (keep the children mounted while the menu is closed).
130
+ * @returns The portal, or nothing while the menu is closed.
131
+ * @throws Error when rendered outside a {@link MenubarMenu}.
132
+ * @example
133
+ * import { Menubar, MenubarMenu, MenubarPortal, MenubarTrigger } from "@/components/ui/menubar";
134
+ *
135
+ * <Menubar>
136
+ * <MenubarMenu value="help">
137
+ * <MenubarTrigger>Help</MenubarTrigger>
138
+ * <MenubarPortal container={() => document.body}>
139
+ * <p>Shown in the body while the menu is open.</p>
140
+ * </MenubarPortal>
141
+ * </MenubarMenu>
142
+ * </Menubar>
143
+ * @since 1.0.0
144
+ * @see {@link MenubarContent}
145
+ */
146
+ function MenubarPortal(props: MenuPortalProps): JSX.Element {
147
+ return <MenubarPrimitive.Portal {...props} />;
148
+ }
149
+
150
+ /**
151
+ * The button of the bar that opens its menu. Its `data-state` is `open` or `closed`.
152
+ *
153
+ * Keyboard and ARIA: a `menuitem` button with `aria-haspopup="menu"`, `aria-expanded`, and
154
+ * `aria-controls` while its menu is open. ArrowLeft and ArrowRight move to the neighboring trigger.
155
+ * ArrowDown, Enter and Space open the menu and focus its first item; ArrowUp opens it and focuses the
156
+ * last item. A pointer press toggles the menu. Its name is its text.
157
+ *
158
+ * @param props The button's attributes, plus `as` (the element to render in its place).
159
+ * @returns The trigger element.
160
+ * @throws Error when rendered outside a {@link Menubar}.
161
+ * @example
162
+ * import { Menubar, MenubarMenu, MenubarTrigger } from "@/components/ui/menubar";
163
+ *
164
+ * <Menubar>
165
+ * <MenubarMenu value="edit">
166
+ * <MenubarTrigger>Edit</MenubarTrigger>
167
+ * </MenubarMenu>
168
+ * </Menubar>
169
+ * @since 1.0.0
170
+ * @see {@link MenubarContent}
171
+ */
172
+ function MenubarTrigger<T extends PolymorphicTarget = "button">(
173
+ props: MenubarTriggerProps<T>,
174
+ ): JSX.Element {
175
+ const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
176
+ return (
177
+ <MenubarPrimitive.Trigger
178
+ data-slot="menubar-trigger"
179
+ class={cn(
180
+ "flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground forced-colors:focus:bg-[Highlight] forced-colors:focus:text-[HighlightText] forced-colors:data-[state=open]:bg-[Highlight] forced-colors:data-[state=open]:text-[HighlightText]",
181
+ local.class,
182
+ )}
183
+ {...rest}
184
+ />
185
+ );
186
+ }
187
+
188
+ /**
189
+ * The menu panel, rendered into `document.body` and placed under its trigger. It is open while its
190
+ * menu is open.
191
+ *
192
+ * Keyboard and ARIA: `role="menu"` with `aria-orientation="vertical"`, named by its trigger
193
+ * (`aria-labelledby`). ArrowDown and ArrowUp move between the items; ArrowLeft and ArrowRight move to
194
+ * the neighboring menu (swapped in right-to-left layouts); Escape and Tab close it, and focus returns
195
+ * to the trigger. Pressing another trigger of the bar does not close it.
196
+ *
197
+ * @param props The panel's attributes. The placement defaults are `align="start"`, `alignOffset={-4}`
198
+ * and `sideOffset={8}`; `class` adds to the panel's classes.
199
+ * @returns The panel element, or nothing while the menu is closed.
200
+ * @throws Error when rendered outside a {@link Menubar}.
201
+ * @example
202
+ * import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarTrigger } from "@/components/ui/menubar";
203
+ *
204
+ * <Menubar>
205
+ * <MenubarMenu value="file">
206
+ * <MenubarTrigger>File</MenubarTrigger>
207
+ * <MenubarContent class="w-64">
208
+ * <MenubarItem>New window</MenubarItem>
209
+ * </MenubarContent>
210
+ * </MenubarMenu>
211
+ * </Menubar>
212
+ * @since 1.0.0
213
+ * @see {@link MenubarTrigger}
214
+ */
215
+ function MenubarContent(props: MenubarContentProps): JSX.Element {
216
+ const [local, rest] = splitProps(props, ["class", "align", "alignOffset", "sideOffset"]);
217
+ return (
218
+ <MenubarPortal>
219
+ <MenubarPrimitive.Content
220
+ data-slot="menubar-content"
221
+ align={local.align ?? "start"}
222
+ alignOffset={local.alignOffset ?? -4}
223
+ sideOffset={local.sideOffset ?? 8}
224
+ class={cn(
225
+ PANEL,
226
+ "min-w-[12rem] max-h-(--primitives-available-height) overflow-x-hidden overflow-y-auto shadow-md",
227
+ local.class,
228
+ )}
229
+ {...rest}
230
+ />
231
+ </MenubarPortal>
232
+ );
233
+ }
234
+
235
+ /**
236
+ * Groups related items. It has no keys and no ARIA attributes of its own; the items keep theirs.
237
+ *
238
+ * @param props The group's attributes.
239
+ * @returns The group element.
240
+ * @throws Error when rendered outside a menu of a {@link Menubar}.
241
+ * @example
242
+ * import { Menubar, MenubarContent, MenubarGroup, MenubarItem, MenubarMenu, MenubarTrigger } from "@/components/ui/menubar";
243
+ *
244
+ * <Menubar>
245
+ * <MenubarMenu value="account">
246
+ * <MenubarTrigger>Account</MenubarTrigger>
247
+ * <MenubarContent>
248
+ * <MenubarGroup>
249
+ * <MenubarItem>Profile</MenubarItem>
250
+ * </MenubarGroup>
251
+ * </MenubarContent>
252
+ * </MenubarMenu>
253
+ * </Menubar>
254
+ * @since 1.0.0
255
+ * @see {@link MenubarContent}
256
+ */
257
+ function MenubarGroup(props: MenuGroupProps): JSX.Element {
258
+ return <MenubarPrimitive.Group data-slot="menubar-group" {...props} />;
259
+ }
260
+
261
+ /**
262
+ * The extra props of {@link MenubarItem}: `inset` and `variant`.
263
+ * @since 1.0.0
264
+ * @see {@link MenubarItem}
265
+ */
266
+ export interface MenubarItemExtraProps {
267
+ /**
268
+ * Indents the item so it lines up with items that have an indicator.
269
+ * @default false
270
+ */
271
+ readonly inset?: boolean | undefined;
272
+ /**
273
+ * `destructive` colors the item for dangerous actions.
274
+ * @default "default"
275
+ */
276
+ readonly variant?: "default" | "destructive" | undefined;
277
+ }
278
+
279
+ /**
280
+ * Props of {@link MenubarItem}: the attributes of the chosen element (`div` by default), plus `inset`
281
+ * and `variant`.
282
+ * @since 1.0.0
283
+ * @see {@link MenubarItem}
284
+ */
285
+ export type MenubarItemProps<T extends PolymorphicTarget = "div"> = PolymorphicProps<
286
+ "div",
287
+ MenuItemOwnProps & MenubarItemExtraProps,
288
+ T
289
+ >;
290
+
291
+ /**
292
+ * An action in a menu of the bar. Selecting it (by click, Enter or Space) closes the menu, unless
293
+ * `onSelect` calls `event.preventDefault()`.
294
+ *
295
+ * Keyboard and ARIA: `role="menuitem"` with `tabindex="-1"`; the arrow keys move between the items.
296
+ * A disabled item gets `aria-disabled="true"` and is skipped. Its name is its text.
297
+ *
298
+ * shadcn/ui differences: `as` replaces `asChild`, as in `<MenubarItem as="a" href="/settings">`.
299
+ *
300
+ * @param props The item's attributes, plus `inset`, `variant` and `as`. `class` adds to the classes.
301
+ * @returns The item element.
302
+ * @throws Error when rendered outside a menu of a {@link Menubar}.
303
+ * @example
304
+ * import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarTrigger } from "@/components/ui/menubar";
305
+ *
306
+ * const remove = (): void => console.info("remove");
307
+ *
308
+ * <Menubar>
309
+ * <MenubarMenu value="edit">
310
+ * <MenubarTrigger>Edit</MenubarTrigger>
311
+ * <MenubarContent>
312
+ * <MenubarItem variant="destructive" onSelect={remove}>Delete</MenubarItem>
313
+ * </MenubarContent>
314
+ * </MenubarMenu>
315
+ * </Menubar>
316
+ * @since 1.0.0
317
+ * @see {@link MenubarContent}
318
+ */
319
+ function MenubarItem<T extends PolymorphicTarget = "div">(props: MenubarItemProps<T>): JSX.Element {
320
+ const [local, rest] = splitProps(props as unknown as Loose, ["class", "inset", "variant"]);
321
+ return (
322
+ <MenubarPrimitive.Item
323
+ data-slot="menubar-item"
324
+ data-inset={local.inset === true ? "" : undefined}
325
+ data-variant={local.variant ?? "default"}
326
+ class={cn(
327
+ ROW,
328
+ "px-2 py-1.5 data-[inset]:ps-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:*:[svg]:text-destructive! [&_svg:not([class*='text-'])]:text-muted-foreground",
329
+ local.class,
330
+ )}
331
+ {...rest}
332
+ />
333
+ );
334
+ }
335
+
336
+ /**
337
+ * An item that toggles a boolean, with a check mark while it is checked.
338
+ *
339
+ * Keyboard and ARIA: `role="menuitemcheckbox"` with `aria-checked` (`"mixed"` while indeterminate).
340
+ * Enter, Space and a click toggle it, and the state changes even when `onSelect` keeps the menu open.
341
+ * Its name is its text.
342
+ *
343
+ * @param props The item's props: `checked`, `defaultChecked`, `onCheckedChange`, and the label as
344
+ * `children`. `class` adds to the classes.
345
+ * @returns The checkbox item element.
346
+ * @throws Error when rendered outside a menu of a {@link Menubar}.
347
+ * @example
348
+ * import { useSignal } from "@shadow-js/core";
349
+ * import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarMenu, MenubarTrigger } from "@/components/ui/menubar";
350
+ *
351
+ * export function ViewMenu() {
352
+ * const [bookmarks, setBookmarks] = useSignal(true);
353
+ * return (
354
+ * <Menubar>
355
+ * <MenubarMenu value="view">
356
+ * <MenubarTrigger>View</MenubarTrigger>
357
+ * <MenubarContent>
358
+ * <MenubarCheckboxItem checked={bookmarks()} onCheckedChange={setBookmarks}>
359
+ * Show bookmarks
360
+ * </MenubarCheckboxItem>
361
+ * </MenubarContent>
362
+ * </MenubarMenu>
363
+ * </Menubar>
364
+ * );
365
+ * }
366
+ * @since 1.0.0
367
+ * @see {@link MenubarItem}
368
+ */
369
+ function MenubarCheckboxItem(props: MenuCheckboxItemProps): JSX.Element {
370
+ const [local, rest] = splitProps(props, ["class", "children"]);
371
+ return (
372
+ <MenubarPrimitive.CheckboxItem
373
+ data-slot="menubar-checkbox-item"
374
+ class={cn(ROW, "py-1.5 ps-8 pe-2", local.class)}
375
+ {...rest}
376
+ >
377
+ <span class="pointer-events-none absolute start-2 flex size-3.5 items-center justify-center">
378
+ <MenubarPrimitive.ItemIndicator>
379
+ <CheckIcon class="size-4" />
380
+ </MenubarPrimitive.ItemIndicator>
381
+ </span>
382
+ {local.children}
383
+ </MenubarPrimitive.CheckboxItem>
384
+ );
385
+ }
386
+
387
+ /**
388
+ * A group of mutually exclusive {@link MenubarRadioItem}s. It handles no keys of its own; the items
389
+ * do.
390
+ *
391
+ * Keyboard and ARIA: a `role="group"`, with no name of its own; pass `aria-label` when the group needs one.
392
+ *
393
+ * @param props The group's props: `value`, `defaultValue`, `onValueChange`, and the `div` attributes.
394
+ * @returns The group element.
395
+ * @example
396
+ * import { Menubar, MenubarContent, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarTrigger } from "@/components/ui/menubar";
397
+ *
398
+ * <Menubar>
399
+ * <MenubarMenu value="view">
400
+ * <MenubarTrigger>View</MenubarTrigger>
401
+ * <MenubarContent>
402
+ * <MenubarRadioGroup defaultValue="list">
403
+ * <MenubarRadioItem value="list">List</MenubarRadioItem>
404
+ * <MenubarRadioItem value="grid">Grid</MenubarRadioItem>
405
+ * </MenubarRadioGroup>
406
+ * </MenubarContent>
407
+ * </MenubarMenu>
408
+ * </Menubar>
409
+ * @since 1.0.0
410
+ * @see {@link MenubarRadioItem}
411
+ */
412
+ function MenubarRadioGroup(props: MenuRadioGroupProps): JSX.Element {
413
+ return <MenubarPrimitive.RadioGroup data-slot="menubar-radio-group" {...props} />;
414
+ }
415
+
416
+ /**
417
+ * An item of a {@link MenubarRadioGroup}, with a dot while it is the selected one.
418
+ *
419
+ * Keyboard and ARIA: `role="menuitemradio"` with `aria-checked`. Enter, Space and a click select it.
420
+ * Its name is its text.
421
+ *
422
+ * @param props The item's props: `value` (required), and the label as `children`. `class` adds to the
423
+ * classes.
424
+ * @returns The radio item element.
425
+ * @throws Error when rendered outside a menu of a {@link Menubar}.
426
+ * @example
427
+ * import { Menubar, MenubarContent, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarTrigger } from "@/components/ui/menubar";
428
+ *
429
+ * <Menubar>
430
+ * <MenubarMenu value="view">
431
+ * <MenubarTrigger>View</MenubarTrigger>
432
+ * <MenubarContent>
433
+ * <MenubarRadioGroup defaultValue="list">
434
+ * <MenubarRadioItem value="list">List</MenubarRadioItem>
435
+ * </MenubarRadioGroup>
436
+ * </MenubarContent>
437
+ * </MenubarMenu>
438
+ * </Menubar>
439
+ * @since 1.0.0
440
+ * @see {@link MenubarRadioGroup}
441
+ */
442
+ function MenubarRadioItem(props: MenuRadioItemProps): JSX.Element {
443
+ const [local, rest] = splitProps(props, ["class", "children"]);
444
+ return (
445
+ <MenubarPrimitive.RadioItem
446
+ data-slot="menubar-radio-item"
447
+ class={cn(ROW, "py-1.5 ps-8 pe-2", local.class)}
448
+ {...rest}
449
+ >
450
+ <span class="pointer-events-none absolute start-2 flex size-3.5 items-center justify-center">
451
+ <MenubarPrimitive.ItemIndicator>
452
+ <CircleIcon class="size-2 fill-current" />
453
+ </MenubarPrimitive.ItemIndicator>
454
+ </span>
455
+ {local.children}
456
+ </MenubarPrimitive.RadioItem>
457
+ );
458
+ }
459
+
460
+ /**
461
+ * Props of {@link MenubarLabel}: the primitive's label props plus `inset`.
462
+ * @since 1.0.0
463
+ * @see {@link MenubarLabel}
464
+ */
465
+ export type MenubarLabelProps = MenuLabelProps & {
466
+ /**
467
+ * Indents the label so it lines up with items that have an indicator.
468
+ * @default false
469
+ */
470
+ readonly inset?: boolean | undefined;
471
+ };
472
+
473
+ /**
474
+ * A heading for a group of items. It has no role, it is not focusable, and the arrow keys skip it.
475
+ *
476
+ * @param props The label's attributes, plus `inset`. `class` adds to the classes.
477
+ * @returns The label element.
478
+ * @throws Error when rendered outside a menu of a {@link Menubar}.
479
+ * @example
480
+ * import { Menubar, MenubarContent, MenubarLabel, MenubarMenu, MenubarTrigger } from "@/components/ui/menubar";
481
+ *
482
+ * <Menubar>
483
+ * <MenubarMenu value="account">
484
+ * <MenubarTrigger>Account</MenubarTrigger>
485
+ * <MenubarContent>
486
+ * <MenubarLabel>My Account</MenubarLabel>
487
+ * </MenubarContent>
488
+ * </MenubarMenu>
489
+ * </Menubar>
490
+ * @since 1.0.0
491
+ * @see {@link MenubarGroup}
492
+ */
493
+ function MenubarLabel(props: MenubarLabelProps): JSX.Element {
494
+ const [local, rest] = splitProps(props, ["class", "inset"]);
495
+ return (
496
+ <MenubarPrimitive.Label
497
+ data-slot="menubar-label"
498
+ data-inset={local.inset === true ? "" : undefined}
499
+ class={cn("px-2 py-1.5 text-sm font-medium data-[inset]:ps-8", local.class)}
500
+ {...rest}
501
+ />
502
+ );
503
+ }
504
+
505
+ /**
506
+ * A divider between groups of items.
507
+ *
508
+ * Keyboard and ARIA: a horizontal `role="separator"` with `aria-orientation="horizontal"`. The arrow
509
+ * keys skip it.
510
+ *
511
+ * @param props The separator's attributes. `class` adds to the classes.
512
+ * @returns The separator element.
513
+ * @throws Error when rendered outside a menu of a {@link Menubar}.
514
+ * @example
515
+ * import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarTrigger } from "@/components/ui/menubar";
516
+ *
517
+ * <Menubar>
518
+ * <MenubarMenu value="file">
519
+ * <MenubarTrigger>File</MenubarTrigger>
520
+ * <MenubarContent>
521
+ * <MenubarItem>New window</MenubarItem>
522
+ * <MenubarSeparator />
523
+ * <MenubarItem>Close</MenubarItem>
524
+ * </MenubarContent>
525
+ * </MenubarMenu>
526
+ * </Menubar>
527
+ * @since 1.0.0
528
+ * @see {@link MenubarContent}
529
+ */
530
+ function MenubarSeparator(props: MenuSeparatorProps): JSX.Element {
531
+ const [local, rest] = splitProps(props, ["class"]);
532
+ return (
533
+ <MenubarPrimitive.Separator
534
+ data-slot="menubar-separator"
535
+ class={cn("-mx-1 my-1 h-px bg-border", local.class)}
536
+ {...rest}
537
+ />
538
+ );
539
+ }
540
+
541
+ /**
542
+ * A keyboard shortcut hint at the end of an item. It is text only: it shows the shortcut and does not
543
+ * bind the key, so wire the key yourself.
544
+ *
545
+ * Keyboard and ARIA: a `span` with no role and no keys.
546
+ *
547
+ * @param props The `span` attributes. `class` adds to the classes.
548
+ * @returns The shortcut `span`.
549
+ * @example
550
+ * import { MenubarItem, MenubarShortcut } from "@/components/ui/menubar";
551
+ *
552
+ * <MenubarItem>
553
+ * Save <MenubarShortcut>Ctrl+S</MenubarShortcut>
554
+ * </MenubarItem>
555
+ * @since 1.0.0
556
+ * @see {@link MenubarItem}
557
+ */
558
+ function MenubarShortcut(props: ComponentProps<"span">): JSX.Element {
559
+ const [local, rest] = splitProps(props, ["class"]);
560
+ return (
561
+ <span
562
+ data-slot="menubar-shortcut"
563
+ class={cn("ms-auto text-xs tracking-widest text-muted-foreground", local.class)}
564
+ {...rest}
565
+ />
566
+ );
567
+ }
568
+
569
+ /**
570
+ * The root of a submenu. Place a {@link MenubarSubTrigger} and a {@link MenubarSubContent} in it. It
571
+ * renders no element and handles no keys.
572
+ *
573
+ * @param props The submenu's options: `open`, `defaultOpen` and `onOpenChange`.
574
+ * @returns The submenu's context around its children.
575
+ * @throws Error when rendered outside a menu of a {@link Menubar}.
576
+ * @example
577
+ * import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger } from "@/components/ui/menubar";
578
+ *
579
+ * <Menubar>
580
+ * <MenubarMenu value="file">
581
+ * <MenubarTrigger>File</MenubarTrigger>
582
+ * <MenubarContent>
583
+ * <MenubarSub>
584
+ * <MenubarSubTrigger>Share</MenubarSubTrigger>
585
+ * <MenubarSubContent>
586
+ * <MenubarItem>Email</MenubarItem>
587
+ * </MenubarSubContent>
588
+ * </MenubarSub>
589
+ * </MenubarContent>
590
+ * </MenubarMenu>
591
+ * </Menubar>
592
+ * @since 1.0.0
593
+ * @see {@link MenubarSubTrigger}
594
+ */
595
+ function MenubarSub(props: MenuSubProps): JSX.Element {
596
+ return <MenubarPrimitive.Sub {...props} />;
597
+ }
598
+
599
+ /**
600
+ * The item that opens a submenu, with a chevron that flips in right-to-left layouts.
601
+ *
602
+ * Keyboard and ARIA: `role="menuitem"` with `aria-haspopup="menu"`, `aria-expanded`, and
603
+ * `aria-controls` while the submenu is open. ArrowRight (ArrowLeft in right-to-left layouts), Enter
604
+ * and Space open the submenu and focus its first item. Resting the pointer on it for a moment opens it
605
+ * too.
606
+ *
607
+ * @param props The trigger's attributes, plus `inset` and `as`. `class` adds to the classes.
608
+ * @returns The trigger element.
609
+ * @throws Error when rendered outside a menu of a {@link Menubar}.
610
+ * @example
611
+ * import { MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarItem } from "@/components/ui/menubar";
612
+ *
613
+ * <MenubarSub>
614
+ * <MenubarSubTrigger>Invite users</MenubarSubTrigger>
615
+ * <MenubarSubContent>
616
+ * <MenubarItem>Email</MenubarItem>
617
+ * </MenubarSubContent>
618
+ * </MenubarSub>
619
+ * @since 1.0.0
620
+ * @see {@link MenubarSubContent}
621
+ */
622
+ function MenubarSubTrigger<T extends PolymorphicTarget = "div">(
623
+ props: MenuSubTriggerProps<T> & { readonly inset?: boolean | undefined },
624
+ ): JSX.Element {
625
+ const [local, rest] = splitProps(props as unknown as Loose, ["class", "inset", "children"]);
626
+ return (
627
+ <MenubarPrimitive.SubTrigger
628
+ data-slot="menubar-sub-trigger"
629
+ data-inset={local.inset === true ? "" : undefined}
630
+ class={cn(
631
+ ROW,
632
+ "px-2 py-1.5 data-[inset]:ps-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground forced-colors:data-[state=open]:bg-[Highlight] forced-colors:data-[state=open]:text-[HighlightText] [&_svg:not([class*='text-'])]:text-muted-foreground",
633
+ local.class,
634
+ )}
635
+ {...rest}
636
+ >
637
+ {local.children}
638
+ <ChevronRightIcon class="ms-auto size-4 rtl:rotate-180" />
639
+ </MenubarPrimitive.SubTrigger>
640
+ );
641
+ }
642
+
643
+ /**
644
+ * The submenu panel, placed beside its trigger.
645
+ *
646
+ * Keyboard and ARIA: `role="menu"` with `aria-orientation="vertical"`, named by its trigger. ArrowLeft
647
+ * (ArrowRight in right-to-left layouts) and Escape close it and return focus to the trigger. The arrow
648
+ * keys, Home and End move between the items.
649
+ *
650
+ * @param props The panel's attributes. `class` adds to the classes.
651
+ * @returns The submenu element, or nothing while it is closed.
652
+ * @throws Error when rendered outside a menu of a {@link Menubar}.
653
+ * @example
654
+ * import { MenubarItem, MenubarSubContent } from "@/components/ui/menubar";
655
+ *
656
+ * <MenubarSubContent>
657
+ * <MenubarItem>Email</MenubarItem>
658
+ * </MenubarSubContent>
659
+ * @since 1.0.0
660
+ * @see {@link MenubarSubTrigger}
661
+ */
662
+ function MenubarSubContent(props: MenuSubContentProps): JSX.Element {
663
+ const [local, rest] = splitProps(props, ["class"]);
664
+ return (
665
+ <MenubarPrimitive.SubContent
666
+ data-slot="menubar-sub-content"
667
+ class={cn(
668
+ PANEL,
669
+ "max-h-(--primitives-available-height) overflow-x-hidden overflow-y-auto shadow-lg",
670
+ local.class,
671
+ )}
672
+ {...rest}
673
+ />
674
+ );
675
+ }
676
+
677
+ export {
678
+ Menubar,
679
+ MenubarCheckboxItem,
680
+ MenubarContent,
681
+ MenubarGroup,
682
+ MenubarItem,
683
+ MenubarLabel,
684
+ MenubarMenu,
685
+ MenubarPortal,
686
+ MenubarRadioGroup,
687
+ MenubarRadioItem,
688
+ MenubarSeparator,
689
+ MenubarShortcut,
690
+ MenubarSub,
691
+ MenubarSubContent,
692
+ MenubarSubTrigger,
693
+ MenubarTrigger,
694
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Message",
3
+ "description": "One message in a conversation: the avatar, the alignment, a header, the content and a footer of actions."
4
+ }