banhaten 0.1.2 → 0.2.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 (365) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/CONTRIBUTING.md +44 -0
  3. package/README.md +145 -328
  4. package/SECURITY.md +15 -0
  5. package/banhaten.config.example.json +1 -3
  6. package/docs/design-system/README.md +20 -0
  7. package/docs/design-system/adoption-quality-remediation.md +87 -0
  8. package/docs/design-system/agent-usage.md +13 -0
  9. package/docs/design-system/appearance-presets.md +189 -0
  10. package/docs/design-system/appearances/default.md +94 -0
  11. package/docs/design-system/appearances/rounded.md +95 -0
  12. package/docs/design-system/appearances/sharp.md +95 -0
  13. package/docs/design-system/component-docs-pattern.md +81 -0
  14. package/docs/design-system/component-showcase-consistency-report.md +217 -0
  15. package/docs/design-system/component-token-consistency-audit.md +173 -0
  16. package/docs/design-system/components/README.md +75 -0
  17. package/docs/design-system/components/accordion.md +67 -0
  18. package/docs/design-system/components/activity-feed.md +106 -0
  19. package/docs/design-system/components/alert-dialog.md +87 -0
  20. package/docs/design-system/components/alert.md +111 -0
  21. package/docs/design-system/components/aspect-ratio.md +59 -0
  22. package/docs/design-system/components/attribute.md +103 -0
  23. package/docs/design-system/components/autocomplete.md +93 -0
  24. package/docs/design-system/components/avatar.md +70 -0
  25. package/docs/design-system/components/badge.md +72 -0
  26. package/docs/design-system/components/banner.md +99 -0
  27. package/docs/design-system/components/breadcrumbs.md +188 -0
  28. package/docs/design-system/components/button-group.md +117 -0
  29. package/docs/design-system/components/button.md +95 -0
  30. package/docs/design-system/components/card.md +95 -0
  31. package/docs/design-system/components/carousel.md +61 -0
  32. package/docs/design-system/components/catalog-components.md +59 -0
  33. package/docs/design-system/components/chart.md +58 -0
  34. package/docs/design-system/components/checkbox.md +68 -0
  35. package/docs/design-system/components/collapsible.md +63 -0
  36. package/docs/design-system/components/command.md +75 -0
  37. package/docs/design-system/components/context-menu.md +59 -0
  38. package/docs/design-system/components/date-picker.md +97 -0
  39. package/docs/design-system/components/divider.md +117 -0
  40. package/docs/design-system/components/empty-state.md +72 -0
  41. package/docs/design-system/components/field.md +86 -0
  42. package/docs/design-system/components/file-upload.md +199 -0
  43. package/docs/design-system/components/hover-card.md +61 -0
  44. package/docs/design-system/components/icons.md +48 -0
  45. package/docs/design-system/components/input-group.md +73 -0
  46. package/docs/design-system/components/input-otp.md +73 -0
  47. package/docs/design-system/components/input.md +84 -0
  48. package/docs/design-system/components/item.md +72 -0
  49. package/docs/design-system/components/kbd.md +62 -0
  50. package/docs/design-system/components/label.md +64 -0
  51. package/docs/design-system/components/menu.md +76 -0
  52. package/docs/design-system/components/menubar.md +60 -0
  53. package/docs/design-system/components/modal.md +119 -0
  54. package/docs/design-system/components/native-select.md +70 -0
  55. package/docs/design-system/components/navigation-menu.md +63 -0
  56. package/docs/design-system/components/onboarding-step-list-item.md +94 -0
  57. package/docs/design-system/components/page-header.md +98 -0
  58. package/docs/design-system/components/pagination.md +71 -0
  59. package/docs/design-system/components/popover.md +77 -0
  60. package/docs/design-system/components/progress-slider.md +66 -0
  61. package/docs/design-system/components/progress.md +92 -0
  62. package/docs/design-system/components/radio-card.md +65 -0
  63. package/docs/design-system/components/radio-group.md +72 -0
  64. package/docs/design-system/components/resizable.md +57 -0
  65. package/docs/design-system/components/scroll-area.md +60 -0
  66. package/docs/design-system/components/segmented-control.md +71 -0
  67. package/docs/design-system/components/select.md +87 -0
  68. package/docs/design-system/components/sheet.md +82 -0
  69. package/docs/design-system/components/sidebar.md +83 -0
  70. package/docs/design-system/components/skeleton.md +91 -0
  71. package/docs/design-system/components/slideout.md +77 -0
  72. package/docs/design-system/components/slider.md +81 -0
  73. package/docs/design-system/components/social-button.md +65 -0
  74. package/docs/design-system/components/spinner.md +77 -0
  75. package/docs/design-system/components/steps.md +77 -0
  76. package/docs/design-system/components/table.md +411 -0
  77. package/docs/design-system/components/tabs.md +69 -0
  78. package/docs/design-system/components/tag.md +95 -0
  79. package/docs/design-system/components/textarea.md +64 -0
  80. package/docs/design-system/components/timeline.md +95 -0
  81. package/docs/design-system/components/toast.md +73 -0
  82. package/docs/design-system/components/toggle.md +96 -0
  83. package/docs/design-system/components/toolbar.md +105 -0
  84. package/docs/design-system/components/tooltip.md +107 -0
  85. package/docs/design-system/components/typography.md +18 -0
  86. package/docs/design-system/density-and-elevation.md +44 -0
  87. package/docs/design-system/design-system-test-missing-items.md +368 -0
  88. package/docs/design-system/direction-and-localization.md +23 -0
  89. package/docs/design-system/form-recipes.md +73 -0
  90. package/docs/design-system/gap-promotion-policy.md +21 -0
  91. package/docs/design-system/gap-promotion-report.md +166 -0
  92. package/docs/design-system/icons.md +69 -0
  93. package/docs/design-system/installation.md +68 -0
  94. package/docs/design-system/launch-comparison-shadcn.md +98 -0
  95. package/docs/design-system/registry-and-cli.md +64 -0
  96. package/docs/design-system/release-policy.md +30 -0
  97. package/docs/design-system/roadmap.md +14 -0
  98. package/docs/design-system/support-matrix.md +23 -0
  99. package/docs/design-system/tabs.md +53 -0
  100. package/docs/design-system/token-governance.md +60 -0
  101. package/package.json +97 -65
  102. package/registry/component-contract.json +40 -0
  103. package/registry/components/alert-dialog.tsx +297 -0
  104. package/registry/components/alert.tsx +86 -65
  105. package/registry/components/aspect-ratio.tsx +30 -0
  106. package/registry/components/autocomplete.tsx +23 -9
  107. package/registry/components/avatar.tsx +7 -7
  108. package/registry/components/badge.tsx +1 -1
  109. package/registry/components/button-group.tsx +321 -4
  110. package/registry/components/button.tsx +38 -17
  111. package/registry/components/card.tsx +68 -59
  112. package/registry/components/carousel.tsx +282 -0
  113. package/registry/components/chart.tsx +203 -0
  114. package/registry/components/checkbox.tsx +3 -3
  115. package/registry/components/collapsible.tsx +69 -0
  116. package/registry/components/command.tsx +174 -0
  117. package/registry/components/context-menu.tsx +236 -0
  118. package/registry/components/date-picker.tsx +246 -105
  119. package/registry/components/expanded/ActivityFeed.tsx +6 -15
  120. package/registry/components/expanded/Banner.tsx +3 -3
  121. package/registry/components/expanded/Breadcrumbs.tsx +5 -5
  122. package/registry/components/expanded/CatalogComponentsShowcase.tsx +13 -13
  123. package/registry/components/expanded/CatalogDivider.tsx +1 -1
  124. package/registry/components/expanded/EmptyState.tsx +1 -1
  125. package/registry/components/expanded/FileUpload.tsx +9 -2
  126. package/registry/components/expanded/OnboardingStepListItem.tsx +2 -4
  127. package/registry/components/expanded/PageHeader.tsx +14 -14
  128. package/registry/components/expanded/Slideout.tsx +15 -415
  129. package/registry/components/expanded/Steps.tsx +4 -4
  130. package/registry/components/expanded/Table.tsx +116 -69
  131. package/registry/components/expanded/Timeline.tsx +3 -3
  132. package/registry/components/expanded/activityFeed.css +1 -1
  133. package/registry/components/expanded/breadcrumbs.css +139 -139
  134. package/registry/components/expanded/catalogComponentsShowcase.css +83 -83
  135. package/registry/components/expanded/divider.css +1 -1
  136. package/registry/components/expanded/emptyState.css +1 -1
  137. package/registry/components/expanded/fileUpload.css +6 -6
  138. package/registry/components/expanded/slideout.css +9 -685
  139. package/registry/components/expanded/steps.css +274 -274
  140. package/registry/components/expanded/table.css +2 -2
  141. package/registry/components/expanded/timeline.css +264 -264
  142. package/registry/components/field.tsx +230 -0
  143. package/registry/components/hover-card.tsx +48 -0
  144. package/registry/components/input-content.ts +1 -0
  145. package/registry/components/input-group.tsx +130 -0
  146. package/registry/components/input-otp.tsx +13 -3
  147. package/registry/components/input.tsx +70 -45
  148. package/registry/components/item.tsx +189 -0
  149. package/registry/components/kbd.tsx +151 -0
  150. package/registry/components/label.tsx +78 -0
  151. package/registry/components/menu.tsx +34 -246
  152. package/registry/components/menubar.tsx +226 -0
  153. package/registry/components/modal.tsx +216 -143
  154. package/registry/components/native-select.tsx +205 -0
  155. package/registry/components/navigation-menu.tsx +171 -0
  156. package/registry/components/pagination.tsx +80 -58
  157. package/registry/components/popover.tsx +162 -664
  158. package/registry/components/radio-card.tsx +116 -87
  159. package/registry/components/radio-group.tsx +1 -1
  160. package/registry/components/resizable.tsx +74 -0
  161. package/registry/components/scroll-area.tsx +67 -0
  162. package/registry/components/segmented-control.tsx +87 -0
  163. package/registry/components/select-content.tsx +2 -2
  164. package/registry/components/select.tsx +54 -20
  165. package/registry/components/sheet.tsx +305 -0
  166. package/registry/components/sidebar.tsx +352 -0
  167. package/registry/components/skeleton.css +0 -3
  168. package/registry/components/social-button.tsx +116 -30
  169. package/registry/components/spinner.tsx +18 -55
  170. package/registry/components/{expanded/tabs.css → tabs.css} +127 -106
  171. package/registry/components/tabs.tsx +246 -0
  172. package/registry/components/tag.tsx +2 -2
  173. package/registry/components/textarea.tsx +4 -4
  174. package/registry/components/toast.tsx +136 -0
  175. package/registry/components/toggle.tsx +2 -2
  176. package/registry/components/toolbar.tsx +89 -17
  177. package/registry/components/tooltip.tsx +1 -1
  178. package/registry/examples/alert-demo.tsx +25 -5
  179. package/registry/examples/alert-dialog-demo.tsx +42 -0
  180. package/registry/examples/aspect-ratio-demo.tsx +11 -0
  181. package/registry/examples/attribute-demo.tsx +1 -1
  182. package/registry/examples/autocomplete-demo.tsx +3 -3
  183. package/registry/examples/avatar-demo.tsx +7 -7
  184. package/registry/examples/badge-demo.tsx +1 -1
  185. package/registry/examples/button-demo.tsx +6 -6
  186. package/registry/examples/button-group-demo.tsx +23 -2
  187. package/registry/examples/card-demo.tsx +46 -8
  188. package/registry/examples/carousel-demo.tsx +43 -0
  189. package/registry/examples/chart-demo.tsx +33 -0
  190. package/registry/examples/checkbox-demo.tsx +5 -5
  191. package/registry/examples/collapsible-demo.tsx +16 -0
  192. package/registry/examples/collection-filter-bar-demo.tsx +135 -0
  193. package/registry/examples/command-demo.tsx +42 -0
  194. package/registry/examples/{expanded/command-bar-demo.tsx → command-palette-demo.tsx} +5 -15
  195. package/registry/examples/context-menu-demo.tsx +29 -0
  196. package/registry/examples/control-density-contract-demo.tsx +61 -0
  197. package/registry/examples/date-picker-demo.tsx +12 -12
  198. package/registry/examples/divider-demo.tsx +3 -3
  199. package/registry/examples/expanded/banner-demo.tsx +1 -1
  200. package/registry/examples/expanded/breadcrumbs-demo.tsx +1 -1
  201. package/registry/examples/expanded/empty-state-demo.tsx +1 -1
  202. package/registry/examples/expanded/file-upload-demo.tsx +1 -1
  203. package/registry/examples/expanded/onboarding-step-demo.tsx +1 -1
  204. package/registry/examples/expanded/slideout-demo.tsx +2 -2
  205. package/registry/examples/expanded/steps-demo.tsx +1 -1
  206. package/registry/examples/expanded/tabs-demo.tsx +2 -2
  207. package/registry/examples/expanded/timeline-demo.tsx +1 -1
  208. package/registry/examples/field-demo.tsx +51 -0
  209. package/registry/examples/hover-card-demo.tsx +23 -0
  210. package/registry/examples/input-demo.tsx +6 -6
  211. package/registry/examples/input-group-demo.tsx +16 -0
  212. package/registry/examples/input-otp-demo.tsx +4 -4
  213. package/registry/examples/item-demo.tsx +46 -0
  214. package/registry/examples/kbd-demo.tsx +11 -0
  215. package/registry/examples/label-demo.tsx +20 -0
  216. package/registry/examples/menu-demo.tsx +1 -1
  217. package/registry/examples/menubar-demo.tsx +34 -0
  218. package/registry/examples/modal-demo.tsx +67 -9
  219. package/registry/examples/native-select-demo.tsx +16 -0
  220. package/registry/examples/navigation-menu-demo.tsx +29 -0
  221. package/registry/examples/pagination-demo.tsx +6 -3
  222. package/registry/examples/popover-demo.tsx +19 -19
  223. package/registry/examples/progress-demo.tsx +1 -1
  224. package/registry/examples/progress-slider-demo.tsx +1 -1
  225. package/registry/examples/radio-card-demo.tsx +4 -4
  226. package/registry/examples/radio-group-demo.tsx +2 -2
  227. package/registry/examples/resizable-demo.tsx +22 -0
  228. package/registry/examples/results-toolbar-demo.tsx +44 -0
  229. package/registry/examples/scroll-area-demo.tsx +15 -0
  230. package/registry/examples/segmented-control-demo.tsx +39 -0
  231. package/registry/examples/select-demo.tsx +1 -1
  232. package/registry/examples/sheet-demo.tsx +47 -0
  233. package/registry/examples/sidebar-demo.tsx +55 -0
  234. package/registry/examples/slider-demo.tsx +3 -3
  235. package/registry/examples/social-button-demo.tsx +18 -8
  236. package/registry/examples/spinner-demo.tsx +13 -13
  237. package/registry/examples/tabs-demo.tsx +13 -0
  238. package/registry/examples/tag-demo.tsx +1 -1
  239. package/registry/examples/textarea-demo.tsx +4 -4
  240. package/registry/examples/toast-demo.tsx +35 -0
  241. package/registry/examples/toggle-demo.tsx +6 -6
  242. package/registry/examples/tooltip-demo.tsx +4 -4
  243. package/registry/guidance/USAGE.md +27 -0
  244. package/registry/hooks/use-direction.ts +193 -13
  245. package/registry/index.json +6010 -432
  246. package/registry/styles/globals.css +3112 -4690
  247. package/registry.json +1582 -0
  248. package/schema/config.schema.json +51 -0
  249. package/schema/registry.schema.json +190 -0
  250. package/schema/tokens.schema.json +163 -0
  251. package/src/cli/command-line.js +194 -0
  252. package/src/cli/config-editors.js +9 -0
  253. package/src/cli/doctor.js +399 -0
  254. package/src/cli/file-transaction.js +295 -0
  255. package/src/cli/global-css.js +153 -0
  256. package/src/cli/index.js +639 -1391
  257. package/src/cli/install-plan.js +592 -0
  258. package/src/cli/install-receipt.js +171 -0
  259. package/src/cli/jsonc.js +203 -0
  260. package/src/cli/package-install-state.js +254 -0
  261. package/src/cli/package-manager.js +238 -0
  262. package/src/cli/package-plan.js +185 -0
  263. package/src/cli/package-version.js +97 -0
  264. package/src/cli/registry-query.js +207 -0
  265. package/src/cli/reporting.js +55 -0
  266. package/src/cli/schema-validation.js +19 -0
  267. package/src/cli/source-scanner.js +686 -0
  268. package/src/cli/vite-config.js +478 -0
  269. package/tokens/banhaten.tokens.json +4313 -310
  270. package/registry/assets/avatars/avatar-02.jpg +0 -0
  271. package/registry/assets/avatars/avatar-03.jpg +0 -0
  272. package/registry/assets/avatars/avatar-04.jpg +0 -0
  273. package/registry/assets/avatars/avatar-05.jpg +0 -0
  274. package/registry/assets/avatars/avatar-06.jpg +0 -0
  275. package/registry/assets/avatars/avatar-07.jpg +0 -0
  276. package/registry/assets/avatars/avatar-08.jpg +0 -0
  277. package/registry/assets/avatars/avatar-09.jpg +0 -0
  278. package/registry/assets/avatars/avatar-10.jpg +0 -0
  279. package/registry/assets/avatars/avatar-11.jpg +0 -0
  280. package/registry/assets/avatars/avatar-12.jpg +0 -0
  281. package/registry/assets/avatars/avatar-13.jpg +0 -0
  282. package/registry/assets/avatars/avatar-14.jpg +0 -0
  283. package/registry/assets/avatars/avatar-15.jpg +0 -0
  284. package/registry/assets/avatars/avatar-16.jpg +0 -0
  285. package/registry/assets/avatars/avatar-17.jpg +0 -0
  286. package/registry/assets/avatars/avatar-18.jpg +0 -0
  287. package/registry/assets/avatars/avatar-19.jpg +0 -0
  288. package/registry/assets/avatars/avatar-20.jpg +0 -0
  289. package/registry/assets/avatars/avatar-21.jpg +0 -0
  290. package/registry/assets/avatars/avatar-22.jpg +0 -0
  291. package/registry/assets/avatars/avatar-23.jpg +0 -0
  292. package/registry/assets/avatars/avatar-24.jpg +0 -0
  293. package/registry/assets/avatars/avatar-25.jpg +0 -0
  294. package/registry/assets/avatars/avatar-26.jpg +0 -0
  295. package/registry/assets/avatars/avatar-27.jpg +0 -0
  296. package/registry/assets/avatars/avatar-28.jpg +0 -0
  297. package/registry/assets/avatars/avatar-29.jpg +0 -0
  298. package/registry/assets/avatars/avatar-30.jpg +0 -0
  299. package/registry/assets/avatars/avatar-31.jpg +0 -0
  300. package/registry/assets/avatars/avatar-32.jpg +0 -0
  301. package/registry/assets/avatars/avatar-33.jpg +0 -0
  302. package/registry/assets/avatars/avatar-34.jpg +0 -0
  303. package/registry/assets/avatars/avatar-35.jpg +0 -0
  304. package/registry/assets/image-assets.json +0 -744
  305. package/registry/assets/images/art-02.jpg +0 -0
  306. package/registry/assets/images/art-03.jpg +0 -0
  307. package/registry/assets/images/art-04.jpg +0 -0
  308. package/registry/assets/images/art-05.jpg +0 -0
  309. package/registry/assets/images/art-06.jpg +0 -0
  310. package/registry/assets/images/art-07.jpg +0 -0
  311. package/registry/assets/images/art-08.jpg +0 -0
  312. package/registry/assets/images/art-09.jpg +0 -0
  313. package/registry/assets/images/art-10.jpg +0 -0
  314. package/registry/assets/images/art-11.jpg +0 -0
  315. package/registry/assets/images/art-12.jpg +0 -0
  316. package/registry/assets/images/art-13.jpg +0 -0
  317. package/registry/assets/images/art-14.jpg +0 -0
  318. package/registry/assets/images/art-15.jpg +0 -0
  319. package/registry/assets/images/art-16.jpg +0 -0
  320. package/registry/assets/images/art-17.jpg +0 -0
  321. package/registry/assets/images/art-18.jpg +0 -0
  322. package/registry/assets/images/art-19.jpg +0 -0
  323. package/registry/assets/images/art-20.jpg +0 -0
  324. package/registry/assets/images/art-21.jpg +0 -0
  325. package/registry/assets/images/art-22.jpg +0 -0
  326. package/registry/assets/images/art-23.jpg +0 -0
  327. package/registry/assets/images/art-24.jpg +0 -0
  328. package/registry/assets/images/art-25.jpg +0 -0
  329. package/registry/assets/images/art-26.jpg +0 -0
  330. package/registry/assets/images/art-27.jpg +0 -0
  331. package/registry/assets/images/nature-01.jpg +0 -0
  332. package/registry/assets/images/nature-02.jpg +0 -0
  333. package/registry/assets/images/nature-03.jpg +0 -0
  334. package/registry/assets/images/nature-04.jpg +0 -0
  335. package/registry/assets/images/nature-05.jpg +0 -0
  336. package/registry/assets/images/nature-06.jpg +0 -0
  337. package/registry/assets/images/nature-07.jpg +0 -0
  338. package/registry/assets/images/nature-08.jpg +0 -0
  339. package/registry/assets/images/nature-09.jpg +0 -0
  340. package/registry/assets/images/nature-10.jpg +0 -0
  341. package/registry/assets/images/nature-11.jpg +0 -0
  342. package/registry/assets/images/nature-12.jpg +0 -0
  343. package/registry/assets/images/nature-13.jpg +0 -0
  344. package/registry/assets/images/nature-14.jpg +0 -0
  345. package/registry/assets/images/nature-15.jpg +0 -0
  346. package/registry/assets/images/nature-16.jpg +0 -0
  347. package/registry/assets/images/nature-17.jpg +0 -0
  348. package/registry/assets/images/nature-18.jpg +0 -0
  349. package/registry/assets/images/nature-19.jpg +0 -0
  350. package/registry/assets/images/nature-20.jpg +0 -0
  351. package/registry/components/expanded/BannerBoard.tsx +0 -225
  352. package/registry/components/expanded/CatalogTag.tsx +0 -85
  353. package/registry/components/expanded/CommandBar.tsx +0 -386
  354. package/registry/components/expanded/IconExplorer.tsx +0 -612
  355. package/registry/components/expanded/Tabs.tsx +0 -86
  356. package/registry/components/expanded/TimelineShowcase.tsx +0 -158
  357. package/registry/components/expanded/commandBar.css +0 -470
  358. package/registry/components/expanded/iconExplorer.css +0 -764
  359. package/registry/components/expanded/iconPacks.ts +0 -866
  360. package/registry/components/expanded/tag.css +0 -148
  361. package/registry/examples/expanded/icons-demo.tsx +0 -5
  362. package/tokens/Color mode.zip +0 -0
  363. package/tokens/Numbers.zip +0 -0
  364. package/tokens/Radius.zip +0 -0
  365. package/tokens/Theme.zip +0 -0
@@ -0,0 +1,78 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as LabelPrimitive from "@radix-ui/react-label"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const labelVariants = cva(
10
+ [
11
+ "inline-flex max-w-full items-center gap-[var(--bh-space-xs-4)] text-start",
12
+ "text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)]",
13
+ "leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)]",
14
+ "text-[var(--bh-content-default)]",
15
+ "has-[[aria-disabled=true]]:text-[var(--bh-content-disabled)]",
16
+ "peer-disabled:cursor-not-allowed peer-disabled:text-[var(--bh-content-disabled)]",
17
+ ],
18
+ {
19
+ variants: {
20
+ tone: {
21
+ default: "",
22
+ subtle: "text-[var(--bh-content-subtle)]",
23
+ danger: "text-[var(--bh-content-danger-default)]",
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ tone: "default",
28
+ },
29
+ }
30
+ )
31
+
32
+ type LabelProps = React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
33
+ VariantProps<typeof labelVariants> & {
34
+ optionalText?: React.ReactNode
35
+ required?: boolean
36
+ }
37
+
38
+ const Label = React.forwardRef<
39
+ React.ElementRef<typeof LabelPrimitive.Root>,
40
+ LabelProps
41
+ >(function Label(
42
+ { children, className, optionalText, required = false, tone, ...props },
43
+ ref
44
+ ) {
45
+ return (
46
+ <LabelPrimitive.Root
47
+ data-slot="label"
48
+ ref={ref}
49
+ className={cn(labelVariants({ tone, className }))}
50
+ {...props}
51
+ >
52
+ <span data-slot="label-text" dir="auto" className="min-w-0">
53
+ {children}
54
+ </span>
55
+ {required ? (
56
+ <span
57
+ aria-hidden="true"
58
+ data-slot="label-required"
59
+ className="shrink-0 text-[var(--bh-content-danger-default)]"
60
+ >
61
+ *
62
+ </span>
63
+ ) : null}
64
+ {optionalText ? (
65
+ <span
66
+ data-slot="label-optional"
67
+ dir="auto"
68
+ className="min-w-0 text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
69
+ >
70
+ {optionalText}
71
+ </span>
72
+ ) : null}
73
+ </LabelPrimitive.Root>
74
+ )
75
+ })
76
+
77
+ export { Label, labelVariants }
78
+ export type { LabelProps }
@@ -4,37 +4,21 @@ import { Slot } from "@radix-ui/react-slot"
4
4
  import { cva, type VariantProps } from "class-variance-authority"
5
5
 
6
6
  import { cn } from "@/lib/utils"
7
+ import { useResolvedDirection } from "@/hooks/use-direction"
7
8
 
8
9
  type MenuWidth = "menu" | "default" | "multiline" | "action" | "auto"
9
10
  type MenuItemKind = "default" | "multiline" | "action" | "progress" | "button"
10
11
  type MenuItemState = "default" | "disabled"
11
12
  type MenuItemIconPosition = "leading" | "trailing"
12
13
  type MenuItemAvatarSize = "sm" | "lg"
13
- type MenuCollisionShift = {
14
- x: number
15
- y: number
16
- }
17
14
  type MenuCollisionPadding = NonNullable<
18
15
  React.ComponentProps<typeof DropdownMenuPrimitive.Content>["collisionPadding"]
19
16
  >
20
- type MenuCollisionPaddingSides = {
21
- bottom: number
22
- left: number
23
- right: number
24
- top: number
25
- }
26
- type MenuResizeObserverConstructor = new (
27
- callback: () => void
28
- ) => {
29
- disconnect: () => void
30
- observe: (target: Element) => void
31
- }
32
17
 
33
18
  // Default Radix collisionPadding mirrors --bh-space-md-8.
34
19
  const MENU_COLLISION_PADDING_PX = 8
35
- const zeroMenuCollisionShift: MenuCollisionShift = { x: 0, y: 0 }
36
- const useSafeLayoutEffect =
37
- typeof window === "undefined" ? React.useEffect : React.useLayoutEffect
20
+ // Radix sideOffset is numeric; this mirrors --bh-space-xs-4.
21
+ const MENU_SUBCONTENT_SIDE_OFFSET_PX = 4
38
22
 
39
23
  const menuVariants = cva(
40
24
  "flex flex-col items-start gap-[var(--bh-menu-gap)] overflow-hidden rounded-[var(--bh-menu-radius)] bg-[var(--bh-menu-bg)] py-[var(--bh-menu-padding-y)] shadow-[var(--shadow-menu)]",
@@ -117,14 +101,7 @@ const menuItemIconVariants = cva(
117
101
  }
118
102
  )
119
103
 
120
- type MenuProps = React.ComponentProps<"div"> &
121
- VariantProps<typeof menuVariants> & {
122
- asChild?: boolean
123
- defaultOpen?: boolean
124
- modal?: boolean
125
- onOpenChange?: (open: boolean) => void
126
- open?: boolean
127
- }
104
+ type MenuProps = React.ComponentProps<typeof DropdownMenuPrimitive.Root>
128
105
 
129
106
  type MenuContentProps = React.ComponentProps<typeof DropdownMenuPrimitive.Content> &
130
107
  VariantProps<typeof menuVariants> & {
@@ -141,147 +118,38 @@ const MenuSub = DropdownMenuPrimitive.Sub
141
118
  const MenuTrigger = DropdownMenuPrimitive.Trigger
142
119
  const MenuStaticContext = React.createContext(false)
143
120
 
121
+ function Menu({ dir, ...props }: MenuProps) {
122
+ const directionBoundaryRef = React.useRef<HTMLSpanElement | null>(null)
123
+ const resolvedDirection = useResolvedDirection(dir, directionBoundaryRef)
124
+
125
+ return (
126
+ <span className="contents" data-slot="menu-root-boundary" ref={directionBoundaryRef}>
127
+ <MenuRoot dir={resolvedDirection} {...props} />
128
+ </span>
129
+ )
130
+ }
131
+
144
132
  type MenuSurfaceProps = React.ComponentProps<"div"> &
145
133
  VariantProps<typeof menuVariants> & {
146
134
  asChild?: boolean
147
- avoidViewportNudge?: boolean
148
- collisionPadding?: MenuCollisionPadding
149
135
  }
150
136
 
151
137
  const MenuSurface = React.forwardRef<HTMLDivElement, MenuSurfaceProps>(function MenuSurface({
152
138
  asChild = false,
153
- avoidViewportNudge = true,
154
139
  children,
155
140
  className,
156
- collisionPadding = MENU_COLLISION_PADDING_PX,
157
141
  role = "menu",
158
- style,
159
142
  width,
160
143
  ...props
161
144
  }, ref) {
162
145
  const Comp = asChild ? Slot : "div"
163
- const surfaceRef = React.useRef<HTMLDivElement | null>(null)
164
- const collisionShiftRef = React.useRef(zeroMenuCollisionShift)
165
- const [collisionShift, setCollisionShift] = React.useState(
166
- zeroMenuCollisionShift
167
- )
168
-
169
- function updateCollisionShift(nextShift: MenuCollisionShift) {
170
- collisionShiftRef.current = nextShift
171
- setCollisionShift((currentShift) =>
172
- isSameMenuCollisionShift(currentShift, nextShift)
173
- ? currentShift
174
- : nextShift
175
- )
176
- }
177
-
178
- function setSurfaceRef(node: HTMLDivElement | null) {
179
- surfaceRef.current = node
180
-
181
- if (typeof ref === "function") {
182
- ref(node)
183
- } else if (ref) {
184
- ref.current = node
185
- }
186
- }
187
-
188
- useSafeLayoutEffect(() => {
189
- if (!avoidViewportNudge) {
190
- updateCollisionShift(zeroMenuCollisionShift)
191
- return
192
- }
193
-
194
- const node = surfaceRef.current
195
- if (!node) return
196
- const surfaceNode: HTMLDivElement = node
197
-
198
- const ownerWindow = surfaceNode.ownerDocument.defaultView
199
- if (!ownerWindow) return
200
- const surfaceWindow: Window = ownerWindow
201
-
202
- let animationFrame = 0
203
- const resolvedCollisionPadding = resolveMenuCollisionPadding(collisionPadding)
204
- const requestFrame =
205
- typeof surfaceWindow.requestAnimationFrame === "function"
206
- ? surfaceWindow.requestAnimationFrame.bind(surfaceWindow)
207
- : (callback: FrameRequestCallback) =>
208
- surfaceWindow.setTimeout(
209
- () => callback(surfaceWindow.performance.now()),
210
- 0
211
- )
212
- const cancelFrame =
213
- typeof surfaceWindow.cancelAnimationFrame === "function"
214
- ? surfaceWindow.cancelAnimationFrame.bind(surfaceWindow)
215
- : surfaceWindow.clearTimeout.bind(surfaceWindow)
216
-
217
- function measureCollisionShift() {
218
- animationFrame = 0
219
-
220
- const rect = surfaceNode.getBoundingClientRect()
221
- const currentShift = collisionShiftRef.current
222
- const unshiftedRect = {
223
- bottom: rect.bottom - currentShift.y,
224
- left: rect.left - currentShift.x,
225
- right: rect.right - currentShift.x,
226
- top: rect.top - currentShift.y,
227
- }
228
-
229
- updateCollisionShift(
230
- getMenuCollisionShift({
231
- collisionPadding: resolvedCollisionPadding,
232
- rect: unshiftedRect,
233
- viewportHeight: surfaceWindow.innerHeight,
234
- viewportWidth: surfaceWindow.innerWidth,
235
- })
236
- )
237
- }
238
-
239
- function queueCollisionShiftMeasure() {
240
- if (animationFrame) {
241
- cancelFrame(animationFrame)
242
- }
243
-
244
- animationFrame = requestFrame(measureCollisionShift)
245
- }
246
-
247
- queueCollisionShiftMeasure()
248
- surfaceWindow.addEventListener("resize", queueCollisionShiftMeasure)
249
- surfaceWindow.addEventListener("scroll", queueCollisionShiftMeasure, true)
250
-
251
- const ResizeObserver = (
252
- surfaceWindow as Window & {
253
- ResizeObserver?: MenuResizeObserverConstructor
254
- }
255
- ).ResizeObserver
256
- const resizeObserver = ResizeObserver
257
- ? new ResizeObserver(queueCollisionShiftMeasure)
258
- : undefined
259
- resizeObserver?.observe(surfaceNode)
260
-
261
- return () => {
262
- if (animationFrame) {
263
- cancelFrame(animationFrame)
264
- }
265
-
266
- surfaceWindow.removeEventListener("resize", queueCollisionShiftMeasure)
267
- surfaceWindow.removeEventListener("scroll", queueCollisionShiftMeasure, true)
268
- resizeObserver?.disconnect()
269
- }
270
- }, [avoidViewportNudge, collisionPadding, width])
271
146
 
272
147
  return (
273
148
  <Comp
274
149
  data-slot="menu"
275
150
  data-width={width ?? "menu"}
276
- ref={setSurfaceRef}
151
+ ref={ref}
277
152
  role={role}
278
- style={{
279
- ...style,
280
- transform: mergeMenuTransforms(
281
- getMenuCollisionTransform(collisionShift),
282
- style?.transform
283
- ),
284
- }}
285
153
  className={cn(
286
154
  menuVariants({ width }),
287
155
  "max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto",
@@ -312,7 +180,6 @@ const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>(function
312
180
  <MenuSurface
313
181
  asChild={asChild}
314
182
  className={className}
315
- collisionPadding={collisionPadding}
316
183
  ref={ref}
317
184
  role={role}
318
185
  width={width}
@@ -327,7 +194,7 @@ const MenuSubContent = React.forwardRef<HTMLDivElement, MenuSubContentProps>(fun
327
194
  children,
328
195
  className,
329
196
  collisionPadding = MENU_COLLISION_PADDING_PX,
330
- sideOffset = 4,
197
+ sideOffset = MENU_SUBCONTENT_SIDE_OFFSET_PX,
331
198
  width,
332
199
  asChild = false,
333
200
  role = "menu",
@@ -343,7 +210,6 @@ const MenuSubContent = React.forwardRef<HTMLDivElement, MenuSubContentProps>(fun
343
210
  <MenuSurface
344
211
  asChild={asChild}
345
212
  className={className}
346
- collisionPadding={collisionPadding}
347
213
  ref={ref}
348
214
  role={role}
349
215
  width={width}
@@ -354,37 +220,30 @@ const MenuSubContent = React.forwardRef<HTMLDivElement, MenuSubContentProps>(fun
354
220
  )
355
221
  })
356
222
 
357
- function Menu({
223
+ type MenuPreviewProps = React.ComponentProps<"div"> &
224
+ VariantProps<typeof menuVariants> & {
225
+ asChild?: boolean
226
+ }
227
+
228
+ function MenuPreview({
358
229
  children,
359
230
  className,
360
- defaultOpen = true,
361
- modal = false,
362
- onOpenChange,
363
- open,
364
231
  role = "menu",
365
232
  width,
366
233
  asChild = false,
367
234
  ...props
368
- }: MenuProps) {
369
- const Comp = asChild ? Slot : "div"
370
- const isOpen = open ?? defaultOpen
371
-
372
- void modal
373
- void onOpenChange
374
-
375
- if (!isOpen) return null
376
-
235
+ }: MenuPreviewProps) {
377
236
  return (
378
237
  <MenuStaticContext.Provider value>
379
- <Comp
380
- data-slot="menu"
381
- data-width={width ?? "menu"}
238
+ <MenuSurface
239
+ asChild={asChild}
240
+ className={className}
382
241
  role={role}
383
- className={cn(menuVariants({ width, className }))}
242
+ width={width}
384
243
  {...props}
385
244
  >
386
245
  {children}
387
- </Comp>
246
+ </MenuSurface>
388
247
  </MenuStaticContext.Provider>
389
248
  )
390
249
  }
@@ -905,7 +764,7 @@ function MenuProgressSpinner({ className }: { className?: string }) {
905
764
  fill="none"
906
765
  stroke="currentColor"
907
766
  strokeLinecap="round"
908
- strokeWidth="3"
767
+ strokeWidth="var(--bh-icon-stroke-300)"
909
768
  />
910
769
  </svg>
911
770
  )
@@ -1023,81 +882,6 @@ function MenuSeparator({
1023
882
  return <DropdownMenuPrimitive.Separator asChild>{separator}</DropdownMenuPrimitive.Separator>
1024
883
  }
1025
884
 
1026
- function getMenuCollisionShift({
1027
- collisionPadding,
1028
- rect,
1029
- viewportHeight,
1030
- viewportWidth,
1031
- }: {
1032
- collisionPadding: MenuCollisionPaddingSides
1033
- rect: Pick<DOMRect, "bottom" | "left" | "right" | "top">
1034
- viewportHeight: number
1035
- viewportWidth: number
1036
- }): MenuCollisionShift {
1037
- let x = 0
1038
- let y = 0
1039
- const minX = collisionPadding.left
1040
- const minY = collisionPadding.top
1041
- const maxX = viewportWidth - collisionPadding.right
1042
- const maxY = viewportHeight - collisionPadding.bottom
1043
-
1044
- if (rect.left < minX) {
1045
- x = minX - rect.left
1046
- } else if (rect.right > maxX) {
1047
- x = maxX - rect.right
1048
- }
1049
-
1050
- if (rect.top < minY) {
1051
- y = minY - rect.top
1052
- } else if (rect.bottom > maxY) {
1053
- y = maxY - rect.bottom
1054
- }
1055
-
1056
- return { x: Math.round(x), y: Math.round(y) }
1057
- }
1058
-
1059
- function resolveMenuCollisionPadding(
1060
- collisionPadding: MenuCollisionPadding
1061
- ): MenuCollisionPaddingSides {
1062
- if (typeof collisionPadding === "number") {
1063
- const padding = Math.max(0, collisionPadding)
1064
-
1065
- return {
1066
- bottom: padding,
1067
- left: padding,
1068
- right: padding,
1069
- top: padding,
1070
- }
1071
- }
1072
-
1073
- return {
1074
- bottom: Math.max(0, collisionPadding.bottom ?? 0),
1075
- left: Math.max(0, collisionPadding.left ?? 0),
1076
- right: Math.max(0, collisionPadding.right ?? 0),
1077
- top: Math.max(0, collisionPadding.top ?? 0),
1078
- }
1079
- }
1080
-
1081
- function getMenuCollisionTransform(shift: MenuCollisionShift) {
1082
- return `translate(${Math.round(shift.x)}px, ${Math.round(shift.y)}px)`
1083
- }
1084
-
1085
- function mergeMenuTransforms(
1086
- collisionTransform: string,
1087
- customTransform: string | undefined
1088
- ) {
1089
- return customTransform
1090
- ? `${collisionTransform} ${customTransform}`
1091
- : collisionTransform
1092
- }
1093
-
1094
- function isSameMenuCollisionShift(
1095
- currentShift: MenuCollisionShift,
1096
- nextShift: MenuCollisionShift
1097
- ) {
1098
- return currentShift.x === nextShift.x && currentShift.y === nextShift.y
1099
- }
1100
-
1101
885
  export {
1102
886
  Menu,
1103
887
  MenuCaption,
@@ -1115,12 +899,14 @@ export {
1115
899
  MenuItemTitle,
1116
900
  MenuLabel,
1117
901
  MenuProgress,
902
+ MenuPreview,
1118
903
  MenuPortal,
1119
904
  MenuRoot,
1120
905
  MenuSeparator,
1121
906
  MenuSub,
1122
907
  MenuSubContent,
1123
908
  MenuSubTrigger,
909
+ MenuSurface,
1124
910
  MenuTrigger,
1125
911
  menuItemContentVariants,
1126
912
  menuItemIconVariants,
@@ -1144,5 +930,7 @@ export type {
1144
930
  MenuLabelProps,
1145
931
  MenuProgressProps,
1146
932
  MenuProps,
933
+ MenuPreviewProps,
934
+ MenuSurfaceProps,
1147
935
  MenuWidth,
1148
936
  }
@@ -0,0 +1,226 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as MenubarPrimitive from "@radix-ui/react-menubar"
5
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ type MenubarProps = React.ComponentProps<typeof MenubarPrimitive.Root>
10
+ type MenubarTriggerProps = React.ComponentPropsWithoutRef<
11
+ typeof MenubarPrimitive.Trigger
12
+ >
13
+ type MenubarContentProps = React.ComponentPropsWithoutRef<
14
+ typeof MenubarPrimitive.Content
15
+ >
16
+ type MenubarItemProps = React.ComponentPropsWithoutRef<
17
+ typeof MenubarPrimitive.Item
18
+ > & {
19
+ inset?: boolean
20
+ }
21
+ type MenubarCheckboxItemProps = React.ComponentPropsWithoutRef<
22
+ typeof MenubarPrimitive.CheckboxItem
23
+ >
24
+ type MenubarRadioItemProps = React.ComponentPropsWithoutRef<
25
+ typeof MenubarPrimitive.RadioItem
26
+ >
27
+ type MenubarLabelProps = React.ComponentPropsWithoutRef<
28
+ typeof MenubarPrimitive.Label
29
+ > & {
30
+ inset?: boolean
31
+ }
32
+ type MenubarSeparatorProps = React.ComponentPropsWithoutRef<
33
+ typeof MenubarPrimitive.Separator
34
+ >
35
+ type MenubarShortcutProps = React.ComponentProps<"span">
36
+
37
+ const MenubarMenu = MenubarPrimitive.Menu
38
+ const MenubarGroup = MenubarPrimitive.Group
39
+ const MenubarPortal = MenubarPrimitive.Portal
40
+ const MenubarSub = MenubarPrimitive.Sub
41
+ const MenubarRadioGroup = MenubarPrimitive.RadioGroup
42
+
43
+ const Menubar = React.forwardRef<
44
+ React.ElementRef<typeof MenubarPrimitive.Root>,
45
+ MenubarProps
46
+ >(function Menubar({ className, ...props }, ref) {
47
+ return (
48
+ <MenubarPrimitive.Root
49
+ data-slot="menubar"
50
+ ref={ref}
51
+ className={cn(
52
+ "flex h-[var(--bh-space-7xl-40)] items-center gap-[var(--bh-space-xs-4)] rounded-[var(--bh-radius-lg-8)] border border-[var(--bh-border-subtle)] bg-[var(--bh-bg-raised)] p-[var(--bh-space-xs-4)] shadow-[var(--shadow-component-default)]",
53
+ className
54
+ )}
55
+ {...props}
56
+ />
57
+ )
58
+ })
59
+
60
+ const MenubarTrigger = React.forwardRef<
61
+ React.ElementRef<typeof MenubarPrimitive.Trigger>,
62
+ MenubarTriggerProps
63
+ >(function MenubarTrigger({ className, ...props }, ref) {
64
+ return (
65
+ <MenubarPrimitive.Trigger
66
+ data-slot="menubar-trigger"
67
+ ref={ref}
68
+ className={cn(
69
+ "flex h-[var(--bh-space-6xl-32)] items-center rounded-[var(--bh-radius-md-6)] px-[var(--bh-space-md-8)] text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)] text-[var(--bh-content-default)] outline-none data-[highlighted]:bg-[var(--bh-interactive-ghost-hover)] data-[state=open]:bg-[var(--bh-interactive-soft-default)]",
70
+ className
71
+ )}
72
+ {...props}
73
+ />
74
+ )
75
+ })
76
+
77
+ const menuContentClassName =
78
+ "z-[var(--bh-z-popover)] min-w-[var(--bh-menu-width)] overflow-hidden rounded-[var(--bh-radius-xl-10)] bg-[var(--bh-bg-raised)] p-[var(--bh-space-xs-4)] text-[var(--bh-content-default)] shadow-[var(--shadow-menu)]"
79
+
80
+ const menuItemClassName =
81
+ "relative flex min-h-[var(--bh-menu-item-default-height)] cursor-default select-none items-center gap-[var(--bh-space-md-8)] rounded-[var(--bh-radius-md-6)] px-[var(--bh-space-md-8)] py-[var(--bh-space-sm-6)] text-start text-[length:var(--bh-text-body-sm-regular-font-size)] font-[var(--bh-text-body-sm-regular-font-weight)] leading-[var(--bh-text-body-sm-regular-line-height)] tracking-[var(--bh-text-body-sm-regular-letter-spacing)] text-[var(--bh-content-default)] outline-none data-[disabled]:pointer-events-none data-[disabled]:text-[var(--bh-content-disabled)] data-[highlighted]:bg-[var(--bh-menu-item-hover-bg)]"
82
+
83
+ const MenubarContent = React.forwardRef<
84
+ React.ElementRef<typeof MenubarPrimitive.Content>,
85
+ MenubarContentProps
86
+ >(function MenubarContent({ align = "start", className, ...props }, ref) {
87
+ return (
88
+ <MenubarPortal>
89
+ <MenubarPrimitive.Content
90
+ align={align}
91
+ data-slot="menubar-content"
92
+ ref={ref}
93
+ className={cn(menuContentClassName, className)}
94
+ {...props}
95
+ />
96
+ </MenubarPortal>
97
+ )
98
+ })
99
+
100
+ const MenubarItem = React.forwardRef<
101
+ React.ElementRef<typeof MenubarPrimitive.Item>,
102
+ MenubarItemProps
103
+ >(function MenubarItem({ className, inset, ...props }, ref) {
104
+ return (
105
+ <MenubarPrimitive.Item
106
+ data-inset={inset ? "true" : undefined}
107
+ data-slot="menubar-item"
108
+ ref={ref}
109
+ className={cn(menuItemClassName, inset && "ps-[var(--bh-space-8xl-48)]", className)}
110
+ {...props}
111
+ />
112
+ )
113
+ })
114
+
115
+ const MenubarCheckboxItem = React.forwardRef<
116
+ React.ElementRef<typeof MenubarPrimitive.CheckboxItem>,
117
+ MenubarCheckboxItemProps
118
+ >(function MenubarCheckboxItem({ children, className, checked, ...props }, ref) {
119
+ return (
120
+ <MenubarPrimitive.CheckboxItem
121
+ checked={checked}
122
+ data-slot="menubar-checkbox-item"
123
+ ref={ref}
124
+ className={cn(menuItemClassName, "ps-[var(--bh-space-8xl-48)]", className)}
125
+ {...props}
126
+ >
127
+ <span className="absolute start-[var(--bh-space-md-8)] inline-flex size-[var(--bh-space-5xl-24)] items-center justify-center">
128
+ <MenubarPrimitive.ItemIndicator>
129
+ <CheckIcon className="size-[var(--bh-space-3xl-16)]" />
130
+ </MenubarPrimitive.ItemIndicator>
131
+ </span>
132
+ {children}
133
+ </MenubarPrimitive.CheckboxItem>
134
+ )
135
+ })
136
+
137
+ const MenubarRadioItem = React.forwardRef<
138
+ React.ElementRef<typeof MenubarPrimitive.RadioItem>,
139
+ MenubarRadioItemProps
140
+ >(function MenubarRadioItem({ children, className, ...props }, ref) {
141
+ return (
142
+ <MenubarPrimitive.RadioItem
143
+ data-slot="menubar-radio-item"
144
+ ref={ref}
145
+ className={cn(menuItemClassName, "ps-[var(--bh-space-8xl-48)]", className)}
146
+ {...props}
147
+ >
148
+ <span className="absolute start-[var(--bh-space-md-8)] inline-flex size-[var(--bh-space-5xl-24)] items-center justify-center">
149
+ <MenubarPrimitive.ItemIndicator>
150
+ <CircleIcon className="size-[var(--bh-space-md-8)] fill-current" />
151
+ </MenubarPrimitive.ItemIndicator>
152
+ </span>
153
+ {children}
154
+ </MenubarPrimitive.RadioItem>
155
+ )
156
+ })
157
+
158
+ const MenubarLabel = React.forwardRef<
159
+ React.ElementRef<typeof MenubarPrimitive.Label>,
160
+ MenubarLabelProps
161
+ >(function MenubarLabel({ className, inset, ...props }, ref) {
162
+ return (
163
+ <MenubarPrimitive.Label
164
+ data-slot="menubar-label"
165
+ ref={ref}
166
+ className={cn(
167
+ "px-[var(--bh-space-md-8)] py-[var(--bh-space-sm-6)] text-[length:var(--bh-text-body-xs-medium-font-size)] font-[var(--bh-text-body-xs-medium-font-weight)] leading-[var(--bh-text-body-xs-medium-line-height)] tracking-[var(--bh-text-body-xs-medium-letter-spacing)] text-[var(--bh-content-muted)]",
168
+ inset && "ps-[var(--bh-space-8xl-48)]",
169
+ className
170
+ )}
171
+ {...props}
172
+ />
173
+ )
174
+ })
175
+
176
+ const MenubarSeparator = React.forwardRef<
177
+ React.ElementRef<typeof MenubarPrimitive.Separator>,
178
+ MenubarSeparatorProps
179
+ >(function MenubarSeparator({ className, ...props }, ref) {
180
+ return (
181
+ <MenubarPrimitive.Separator
182
+ data-slot="menubar-separator"
183
+ ref={ref}
184
+ className={cn("my-[var(--bh-space-xs-4)] h-[var(--bh-border-width-default)] bg-[var(--bh-border-subtle)]", className)}
185
+ {...props}
186
+ />
187
+ )
188
+ })
189
+
190
+ function MenubarShortcut({ className, ...props }: MenubarShortcutProps) {
191
+ return (
192
+ <span
193
+ data-slot="menubar-shortcut"
194
+ className={cn("ms-auto text-[length:var(--bh-text-body-xs-medium-font-size)] font-[var(--bh-text-body-xs-medium-font-weight)] text-[var(--bh-content-muted)]", className)}
195
+ {...props}
196
+ />
197
+ )
198
+ }
199
+
200
+ export {
201
+ Menubar,
202
+ MenubarCheckboxItem,
203
+ MenubarContent,
204
+ MenubarGroup,
205
+ MenubarItem,
206
+ MenubarLabel,
207
+ MenubarMenu,
208
+ MenubarPortal,
209
+ MenubarRadioGroup,
210
+ MenubarRadioItem,
211
+ MenubarSeparator,
212
+ MenubarShortcut,
213
+ MenubarSub,
214
+ MenubarTrigger,
215
+ }
216
+ export type {
217
+ MenubarCheckboxItemProps,
218
+ MenubarContentProps,
219
+ MenubarItemProps,
220
+ MenubarLabelProps,
221
+ MenubarProps,
222
+ MenubarRadioItemProps,
223
+ MenubarSeparatorProps,
224
+ MenubarShortcutProps,
225
+ MenubarTriggerProps,
226
+ }