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
@@ -121,15 +121,15 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
121
121
  moreButtonLabel = "Open card menu",
122
122
  onMoreClick,
123
123
  orientation = "vertical",
124
- primaryActionLabel = "Button",
124
+ primaryActionLabel,
125
125
  primaryActionProps,
126
- secondaryActionLabel = "Button",
126
+ secondaryActionLabel,
127
127
  secondaryActionProps,
128
- showDescription = true,
129
- showFooter = true,
130
- showImage = true,
131
- showMoreButton = true,
132
- showSubtitle = true,
128
+ showDescription,
129
+ showFooter,
130
+ showImage,
131
+ showMoreButton,
132
+ showSubtitle,
133
133
  subtitle,
134
134
  title,
135
135
  type = "leading-image",
@@ -142,13 +142,15 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
142
142
  ? "leading-image"
143
143
  : type ?? "leading-image"
144
144
  const fullImage = cardOrientation === "vertical" && cardType === "full-image"
145
- const renderedTitle = title ?? "Card Title"
146
- const renderedSubtitle = subtitle ?? "Card Subtitle"
147
- const renderedDescription =
148
- description ??
149
- "Journey through time as we explore the mysteries of the Great Pyramids and the Sphinx."
145
+ const shouldShowDescription = showDescription ?? description !== undefined
146
+ const shouldShowFooter =
147
+ showFooter ??
148
+ Boolean(footer || actions || primaryActionLabel || secondaryActionLabel)
149
+ const shouldShowImage = showImage ?? Boolean(imageSrc)
150
+ const shouldShowMoreButton = showMoreButton ?? Boolean(onMoreClick)
151
+ const shouldShowSubtitle = showSubtitle ?? subtitle !== undefined
150
152
 
151
- const media = showImage ? (
153
+ const media = shouldShowImage ? (
152
154
  <CardMedia
153
155
  imageAlt={imageAlt}
154
156
  imageSrc={imageSrc}
@@ -160,7 +162,7 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
160
162
  <CardBody
161
163
  actions={actions}
162
164
  breakpoint={cardBreakpoint}
163
- description={renderedDescription}
165
+ description={description}
164
166
  footer={footer}
165
167
  moreButtonLabel={moreButtonLabel}
166
168
  onMoreClick={onMoreClick}
@@ -169,12 +171,12 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
169
171
  primaryActionProps={primaryActionProps}
170
172
  secondaryActionLabel={secondaryActionLabel}
171
173
  secondaryActionProps={secondaryActionProps}
172
- showDescription={showDescription}
173
- showFooter={showFooter && !fullImage}
174
- showMoreButton={showMoreButton}
175
- showSubtitle={showSubtitle}
176
- subtitle={renderedSubtitle}
177
- title={renderedTitle}
174
+ showDescription={shouldShowDescription}
175
+ showFooter={shouldShowFooter && !fullImage}
176
+ showMoreButton={shouldShowMoreButton}
177
+ showSubtitle={shouldShowSubtitle}
178
+ subtitle={subtitle}
179
+ title={title}
178
180
  />
179
181
  )
180
182
 
@@ -206,10 +208,10 @@ const Card = React.forwardRef<HTMLElement, CardProps>(function Card({
206
208
  breakpoint={cardBreakpoint}
207
209
  moreButtonLabel={moreButtonLabel}
208
210
  onMoreClick={onMoreClick}
209
- showMoreButton={showMoreButton}
210
- showSubtitle={showSubtitle}
211
- subtitle={renderedSubtitle}
212
- title={renderedTitle}
211
+ showMoreButton={shouldShowMoreButton}
212
+ showSubtitle={shouldShowSubtitle}
213
+ subtitle={subtitle}
214
+ title={title}
213
215
  />
214
216
  </>
215
217
  ) : cardOrientation === "horizontal" &&
@@ -275,9 +277,9 @@ const CardActions = React.forwardRef<HTMLDivElement, CardActionsProps>(
275
277
  function CardActions({
276
278
  children,
277
279
  className,
278
- primaryLabel = "Button",
280
+ primaryLabel,
279
281
  primaryProps,
280
- secondaryLabel = "Button",
282
+ secondaryLabel,
281
283
  secondaryProps,
282
284
  ...props
283
285
  }, ref) {
@@ -286,23 +288,28 @@ const CardActions = React.forwardRef<HTMLDivElement, CardActionsProps>(
286
288
  data-slot="card-actions"
287
289
  ref={ref}
288
290
  className={cn(
289
- "grid w-full max-w-[var(--bh-card-actions-max-width)] grid-cols-2 gap-[var(--bh-space-xl-12)]",
291
+ "grid w-full max-w-[var(--bh-card-actions-max-width)] gap-[var(--bh-space-xl-12)]",
292
+ primaryLabel && secondaryLabel ? "grid-cols-2" : "grid-cols-1",
290
293
  className
291
294
  )}
292
295
  {...props}
293
296
  >
294
297
  {children ?? (
295
298
  <>
296
- <Button {...primaryProps} className={cn("min-w-0", primaryProps?.className)}>
297
- {primaryLabel}
298
- </Button>
299
- <Button
300
- variant="soft"
301
- {...secondaryProps}
302
- className={cn("min-w-0", secondaryProps?.className)}
303
- >
304
- {secondaryLabel}
305
- </Button>
299
+ {primaryLabel ? (
300
+ <Button {...primaryProps} className={cn("min-w-0", primaryProps?.className)}>
301
+ {primaryLabel}
302
+ </Button>
303
+ ) : null}
304
+ {secondaryLabel ? (
305
+ <Button
306
+ variant="soft"
307
+ {...secondaryProps}
308
+ className={cn("min-w-0", secondaryProps?.className)}
309
+ >
310
+ {secondaryLabel}
311
+ </Button>
312
+ ) : null}
306
313
  </>
307
314
  )}
308
315
  </div>
@@ -331,21 +338,21 @@ function CardBody({
331
338
  }: {
332
339
  actions?: React.ReactNode
333
340
  breakpoint: CardBreakpoint
334
- description: React.ReactNode
341
+ description?: React.ReactNode
335
342
  footer?: React.ReactNode
336
343
  moreButtonLabel: string
337
344
  onMoreClick?: React.MouseEventHandler<HTMLButtonElement>
338
345
  orientation: CardOrientation
339
- primaryActionLabel: React.ReactNode
346
+ primaryActionLabel?: React.ReactNode
340
347
  primaryActionProps?: ButtonProps
341
- secondaryActionLabel: React.ReactNode
348
+ secondaryActionLabel?: React.ReactNode
342
349
  secondaryActionProps?: ButtonProps
343
350
  showDescription: boolean
344
351
  showFooter: boolean
345
352
  showMoreButton: boolean
346
353
  showSubtitle: boolean
347
- subtitle: React.ReactNode
348
- title: React.ReactNode
354
+ subtitle?: React.ReactNode
355
+ title?: React.ReactNode
349
356
  }) {
350
357
  const horizontal = orientation === "horizontal"
351
358
 
@@ -378,7 +385,7 @@ function CardBody({
378
385
  title={title}
379
386
  subtitleClassName={getSubtitleClassName(breakpoint)}
380
387
  />
381
- {showDescription ? (
388
+ {showDescription && description !== undefined ? (
382
389
  <p
383
390
  data-slot="card-description"
384
391
  dir="auto"
@@ -423,8 +430,8 @@ function CardFullImageContent({
423
430
  onMoreClick?: React.MouseEventHandler<HTMLButtonElement>
424
431
  showMoreButton: boolean
425
432
  showSubtitle: boolean
426
- subtitle: React.ReactNode
427
- title: React.ReactNode
433
+ subtitle?: React.ReactNode
434
+ title?: React.ReactNode
428
435
  }) {
429
436
  return (
430
437
  <div
@@ -475,20 +482,22 @@ function CardHeader({
475
482
  className="flex min-w-0 items-start gap-[var(--bh-space-3xl-16)]"
476
483
  >
477
484
  <div className="flex min-w-0 flex-1 flex-col gap-[var(--bh-space-xxs-2)]">
478
- <h3
479
- data-slot="card-title"
480
- dir="auto"
481
- className={cn(
482
- "min-w-0 break-words text-[length:var(--bh-text-heading-sm-semibold-font-size)]",
483
- "font-[var(--bh-text-heading-sm-semibold-font-weight)]",
484
- "leading-[var(--bh-text-heading-sm-semibold-line-height)]",
485
- "tracking-[var(--bh-text-heading-sm-semibold-letter-spacing)]",
486
- onImage ? "text-[var(--bh-content-on-color)]" : "text-[var(--bh-content-default)]"
487
- )}
488
- >
489
- {title}
490
- </h3>
491
- {showSubtitle ? (
485
+ {title !== undefined ? (
486
+ <h3
487
+ data-slot="card-title"
488
+ dir="auto"
489
+ className={cn(
490
+ "min-w-0 break-words text-[length:var(--bh-text-heading-sm-semibold-font-size)]",
491
+ "font-[var(--bh-text-heading-sm-semibold-font-weight)]",
492
+ "leading-[var(--bh-text-heading-sm-semibold-line-height)]",
493
+ "tracking-[var(--bh-text-heading-sm-semibold-letter-spacing)]",
494
+ onImage ? "text-[var(--bh-content-on-color)]" : "text-[var(--bh-content-default)]"
495
+ )}
496
+ >
497
+ {title}
498
+ </h3>
499
+ ) : null}
500
+ {showSubtitle && subtitle !== undefined ? (
492
501
  <p
493
502
  data-slot="card-subtitle"
494
503
  dir="auto"
@@ -0,0 +1,282 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import useEmblaCarousel, {
5
+ type UseEmblaCarouselType,
6
+ } from "embla-carousel-react"
7
+ import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"
8
+
9
+ import { Button } from "@/components/ui/button"
10
+ import { cn } from "@/lib/utils"
11
+ import { useResolvedDirection, type Direction } from "@/hooks/use-direction"
12
+
13
+ type CarouselApi = UseEmblaCarouselType[1]
14
+ type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
15
+ type CarouselOptions = UseCarouselParameters[0]
16
+ type CarouselPlugin = UseCarouselParameters[1]
17
+ type CarouselOrientation = "horizontal" | "vertical"
18
+
19
+ type CarouselMessages = {
20
+ nextSlide: string
21
+ previousSlide: string
22
+ }
23
+
24
+ const defaultCarouselMessages: CarouselMessages = {
25
+ nextSlide: "Next slide",
26
+ previousSlide: "Previous slide",
27
+ }
28
+
29
+ const arabicCarouselMessages: CarouselMessages = {
30
+ nextSlide: "\u0627\u0644\u0634\u0631\u064a\u062d\u0629 \u0627\u0644\u062a\u0627\u0644\u064a\u0629",
31
+ previousSlide: "\u0627\u0644\u0634\u0631\u064a\u062d\u0629 \u0627\u0644\u0633\u0627\u0628\u0642\u0629",
32
+ }
33
+
34
+ type CarouselProps = React.ComponentProps<"div"> & {
35
+ messages?: Partial<CarouselMessages>
36
+ opts?: CarouselOptions
37
+ orientation?: CarouselOrientation
38
+ plugins?: CarouselPlugin
39
+ setApi?: (api: CarouselApi) => void
40
+ }
41
+ type CarouselContentProps = React.ComponentProps<"div">
42
+ type CarouselItemProps = React.ComponentProps<"div">
43
+ type CarouselButtonProps = React.ComponentProps<typeof Button>
44
+
45
+ type CarouselContextValue = {
46
+ api: CarouselApi
47
+ canScrollNext: boolean
48
+ canScrollPrev: boolean
49
+ direction: Direction
50
+ messages: CarouselMessages
51
+ orientation: CarouselOrientation
52
+ scrollNext: () => void
53
+ scrollPrev: () => void
54
+ }
55
+
56
+ const CarouselContext = React.createContext<CarouselContextValue | null>(null)
57
+
58
+ const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
59
+ function Carousel(
60
+ {
61
+ children,
62
+ className,
63
+ dir,
64
+ messages: messageOverrides,
65
+ opts,
66
+ orientation = "horizontal",
67
+ plugins,
68
+ setApi,
69
+ ...props
70
+ },
71
+ ref
72
+ ) {
73
+ const rootRef = React.useRef<HTMLDivElement | null>(null)
74
+ const direction = useResolvedDirection(
75
+ dir === "ltr" || dir === "rtl" ? dir : undefined,
76
+ rootRef
77
+ )
78
+ const setRootRef = React.useCallback(
79
+ (node: HTMLDivElement | null) => {
80
+ rootRef.current = node
81
+ if (typeof ref === "function") ref(node)
82
+ else if (ref) ref.current = node
83
+ },
84
+ [ref]
85
+ )
86
+ const [carouselRef, api] = useEmblaCarousel(
87
+ {
88
+ ...opts,
89
+ axis: orientation === "horizontal" ? "x" : "y",
90
+ direction,
91
+ },
92
+ plugins
93
+ )
94
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
95
+ const [canScrollNext, setCanScrollNext] = React.useState(false)
96
+ const messages = React.useMemo(
97
+ () => ({ ...defaultCarouselMessages, ...messageOverrides }),
98
+ [messageOverrides]
99
+ )
100
+
101
+ const onSelect = React.useCallback((nextApi: CarouselApi) => {
102
+ if (!nextApi) return
103
+ setCanScrollPrev(nextApi.canScrollPrev())
104
+ setCanScrollNext(nextApi.canScrollNext())
105
+ }, [])
106
+
107
+ const scrollPrev = React.useCallback(() => api?.scrollPrev(), [api])
108
+ const scrollNext = React.useCallback(() => api?.scrollNext(), [api])
109
+
110
+ React.useEffect(() => {
111
+ if (!api) return
112
+ setApi?.(api)
113
+ onSelect(api)
114
+ api.on("reInit", onSelect)
115
+ api.on("select", onSelect)
116
+
117
+ return () => {
118
+ api.off("select", onSelect)
119
+ }
120
+ }, [api, onSelect, setApi])
121
+
122
+ return (
123
+ <CarouselContext.Provider
124
+ value={{
125
+ api,
126
+ canScrollNext,
127
+ canScrollPrev,
128
+ direction,
129
+ messages,
130
+ orientation,
131
+ scrollNext,
132
+ scrollPrev,
133
+ }}
134
+ >
135
+ <div
136
+ data-orientation={orientation}
137
+ data-slot="carousel"
138
+ dir={direction}
139
+ ref={setRootRef}
140
+ role="region"
141
+ className={cn("relative", className)}
142
+ {...props}
143
+ >
144
+ <div ref={carouselRef} className="overflow-hidden">
145
+ {children}
146
+ </div>
147
+ </div>
148
+ </CarouselContext.Provider>
149
+ )
150
+ }
151
+ )
152
+
153
+ const CarouselContent = React.forwardRef<HTMLDivElement, CarouselContentProps>(
154
+ function CarouselContent({ className, ...props }, ref) {
155
+ const { orientation } = useCarousel()
156
+
157
+ return (
158
+ <div
159
+ data-slot="carousel-content"
160
+ ref={ref}
161
+ className={cn(
162
+ "flex",
163
+ orientation === "horizontal"
164
+ ? "-ms-[var(--bh-space-3xl-16)]"
165
+ : "-mt-[var(--bh-space-3xl-16)] flex-col",
166
+ className
167
+ )}
168
+ {...props}
169
+ />
170
+ )
171
+ }
172
+ )
173
+
174
+ const CarouselItem = React.forwardRef<HTMLDivElement, CarouselItemProps>(
175
+ function CarouselItem({ className, ...props }, ref) {
176
+ const { orientation } = useCarousel()
177
+
178
+ return (
179
+ <div
180
+ data-slot="carousel-item"
181
+ ref={ref}
182
+ role="group"
183
+ className={cn(
184
+ "min-w-0 shrink-0 grow-0 basis-full",
185
+ orientation === "horizontal"
186
+ ? "ps-[var(--bh-space-3xl-16)]"
187
+ : "pt-[var(--bh-space-3xl-16)]",
188
+ className
189
+ )}
190
+ {...props}
191
+ />
192
+ )
193
+ }
194
+ )
195
+
196
+ const CarouselPrevious = React.forwardRef<HTMLButtonElement, CarouselButtonProps>(
197
+ function CarouselPrevious({ className, variant = "outline", size = "icon", ...props }, ref) {
198
+ const { canScrollPrev, messages, orientation, scrollPrev } = useCarousel()
199
+
200
+ return (
201
+ <Button
202
+ aria-label={
203
+ props["aria-label"] ?? messages.previousSlide
204
+ }
205
+ className={cn(
206
+ "absolute size-[var(--bh-space-7xl-40)] rounded-[var(--bh-radius-full)]",
207
+ orientation === "horizontal"
208
+ ? "start-[calc(var(--bh-space-8xl-48)*-1)] top-1/2 -translate-y-1/2"
209
+ : "start-1/2 top-[calc(var(--bh-space-8xl-48)*-1)] -translate-x-1/2 rotate-90",
210
+ className
211
+ )}
212
+ disabled={!canScrollPrev}
213
+ onClick={scrollPrev}
214
+ ref={ref}
215
+ size={size}
216
+ type="button"
217
+ variant={variant}
218
+ {...props}
219
+ >
220
+ <ArrowLeftIcon className="size-[var(--bh-space-3xl-16)] rtl:-scale-x-100" />
221
+ </Button>
222
+ )
223
+ }
224
+ )
225
+
226
+ const CarouselNext = React.forwardRef<HTMLButtonElement, CarouselButtonProps>(
227
+ function CarouselNext({ className, variant = "outline", size = "icon", ...props }, ref) {
228
+ const { canScrollNext, messages, orientation, scrollNext } = useCarousel()
229
+
230
+ return (
231
+ <Button
232
+ aria-label={
233
+ props["aria-label"] ?? messages.nextSlide
234
+ }
235
+ className={cn(
236
+ "absolute size-[var(--bh-space-7xl-40)] rounded-[var(--bh-radius-full)]",
237
+ orientation === "horizontal"
238
+ ? "end-[calc(var(--bh-space-8xl-48)*-1)] top-1/2 -translate-y-1/2"
239
+ : "bottom-[calc(var(--bh-space-8xl-48)*-1)] start-1/2 -translate-x-1/2 rotate-90",
240
+ className
241
+ )}
242
+ disabled={!canScrollNext}
243
+ onClick={scrollNext}
244
+ ref={ref}
245
+ size={size}
246
+ type="button"
247
+ variant={variant}
248
+ {...props}
249
+ >
250
+ <ArrowRightIcon className="size-[var(--bh-space-3xl-16)] rtl:-scale-x-100" />
251
+ </Button>
252
+ )
253
+ }
254
+ )
255
+
256
+ function useCarousel() {
257
+ const context = React.useContext(CarouselContext)
258
+ if (!context) {
259
+ throw new Error("Carousel parts must be used within Carousel")
260
+ }
261
+ return context
262
+ }
263
+
264
+ export {
265
+ arabicCarouselMessages,
266
+ Carousel,
267
+ CarouselContent,
268
+ CarouselItem,
269
+ CarouselNext,
270
+ CarouselPrevious,
271
+ }
272
+ export type {
273
+ CarouselApi,
274
+ CarouselButtonProps,
275
+ CarouselContentProps,
276
+ CarouselItemProps,
277
+ CarouselMessages,
278
+ CarouselOptions,
279
+ CarouselOrientation,
280
+ CarouselPlugin,
281
+ CarouselProps,
282
+ }