velvetkit 0.1.0.beta

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 (481) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +16 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +197 -0
  5. data/lib/generators/velvetkit/add/add_generator.rb +111 -0
  6. data/lib/generators/velvetkit/ai/ai_generator.rb +78 -0
  7. data/lib/generators/velvetkit/helpers.rb +64 -0
  8. data/lib/generators/velvetkit/install/install_generator.rb +108 -0
  9. data/lib/velvetkit/namespace.rb +57 -0
  10. data/lib/velvetkit/reference.rb +108 -0
  11. data/lib/velvetkit/registry.rb +162 -0
  12. data/lib/velvetkit/version.rb +5 -0
  13. data/lib/velvetkit.rb +28 -0
  14. data/registry/app/assets/tailwind/velvetkit.css +342 -0
  15. data/registry/app/components/ui/accordion/content.html.erb +5 -0
  16. data/registry/app/components/ui/accordion/content.rb +34 -0
  17. data/registry/app/components/ui/accordion/item.html.erb +1 -0
  18. data/registry/app/components/ui/accordion/item.rb +40 -0
  19. data/registry/app/components/ui/accordion/trigger.html.erb +1 -0
  20. data/registry/app/components/ui/accordion/trigger.rb +37 -0
  21. data/registry/app/components/ui/accordion.html.erb +7 -0
  22. data/registry/app/components/ui/accordion.rb +34 -0
  23. data/registry/app/components/ui/alert/description.html.erb +1 -0
  24. data/registry/app/components/ui/alert/description.rb +12 -0
  25. data/registry/app/components/ui/alert/title.html.erb +1 -0
  26. data/registry/app/components/ui/alert/title.rb +12 -0
  27. data/registry/app/components/ui/alert.html.erb +16 -0
  28. data/registry/app/components/ui/alert.rb +55 -0
  29. data/registry/app/components/ui/avatar/fallback.html.erb +1 -0
  30. data/registry/app/components/ui/avatar/fallback.rb +12 -0
  31. data/registry/app/components/ui/avatar/image.rb +20 -0
  32. data/registry/app/components/ui/avatar.html.erb +1 -0
  33. data/registry/app/components/ui/avatar.rb +56 -0
  34. data/registry/app/components/ui/avatar_group.html.erb +1 -0
  35. data/registry/app/components/ui/avatar_group.rb +35 -0
  36. data/registry/app/components/ui/badge.html.erb +1 -0
  37. data/registry/app/components/ui/badge.rb +38 -0
  38. data/registry/app/components/ui/banner.html.erb +10 -0
  39. data/registry/app/components/ui/banner.rb +47 -0
  40. data/registry/app/components/ui/base.rb +53 -0
  41. data/registry/app/components/ui/breadcrumb/auto_ellipsis.html.erb +23 -0
  42. data/registry/app/components/ui/breadcrumb/auto_ellipsis.rb +12 -0
  43. data/registry/app/components/ui/breadcrumb/ellipsis.html.erb +18 -0
  44. data/registry/app/components/ui/breadcrumb/ellipsis.rb +8 -0
  45. data/registry/app/components/ui/breadcrumb/item.html.erb +1 -0
  46. data/registry/app/components/ui/breadcrumb/item.rb +12 -0
  47. data/registry/app/components/ui/breadcrumb/item_slot.html.erb +10 -0
  48. data/registry/app/components/ui/breadcrumb/item_slot.rb +47 -0
  49. data/registry/app/components/ui/breadcrumb/list.html.erb +1 -0
  50. data/registry/app/components/ui/breadcrumb/list.rb +12 -0
  51. data/registry/app/components/ui/breadcrumb/separator.html.erb +1 -0
  52. data/registry/app/components/ui/breadcrumb/separator.rb +12 -0
  53. data/registry/app/components/ui/breadcrumb.html.erb +30 -0
  54. data/registry/app/components/ui/breadcrumb.rb +83 -0
  55. data/registry/app/components/ui/button.html.erb +1 -0
  56. data/registry/app/components/ui/button.rb +81 -0
  57. data/registry/app/components/ui/button_to.html.erb +1 -0
  58. data/registry/app/components/ui/button_to.rb +33 -0
  59. data/registry/app/components/ui/card/content.html.erb +1 -0
  60. data/registry/app/components/ui/card/content.rb +12 -0
  61. data/registry/app/components/ui/card/description.html.erb +1 -0
  62. data/registry/app/components/ui/card/description.rb +12 -0
  63. data/registry/app/components/ui/card/footer.html.erb +1 -0
  64. data/registry/app/components/ui/card/footer.rb +12 -0
  65. data/registry/app/components/ui/card/header.html.erb +1 -0
  66. data/registry/app/components/ui/card/header.rb +15 -0
  67. data/registry/app/components/ui/card/title.html.erb +1 -0
  68. data/registry/app/components/ui/card/title.rb +12 -0
  69. data/registry/app/components/ui/card.html.erb +1 -0
  70. data/registry/app/components/ui/card.rb +18 -0
  71. data/registry/app/components/ui/checkbox.html.erb +18 -0
  72. data/registry/app/components/ui/checkbox.rb +133 -0
  73. data/registry/app/components/ui/data_grid.html.erb +2 -0
  74. data/registry/app/components/ui/data_grid.rb +84 -0
  75. data/registry/app/components/ui/date_picker.html.erb +70 -0
  76. data/registry/app/components/ui/date_picker.rb +111 -0
  77. data/registry/app/components/ui/dialog/close.html.erb +7 -0
  78. data/registry/app/components/ui/dialog/close.rb +53 -0
  79. data/registry/app/components/ui/dialog/content.html.erb +26 -0
  80. data/registry/app/components/ui/dialog/content.rb +102 -0
  81. data/registry/app/components/ui/dialog/description.html.erb +1 -0
  82. data/registry/app/components/ui/dialog/description.rb +16 -0
  83. data/registry/app/components/ui/dialog/footer.html.erb +1 -0
  84. data/registry/app/components/ui/dialog/footer.rb +14 -0
  85. data/registry/app/components/ui/dialog/header.html.erb +1 -0
  86. data/registry/app/components/ui/dialog/header.rb +16 -0
  87. data/registry/app/components/ui/dialog/title.html.erb +1 -0
  88. data/registry/app/components/ui/dialog/title.rb +16 -0
  89. data/registry/app/components/ui/dialog/trigger.html.erb +5 -0
  90. data/registry/app/components/ui/dialog/trigger.rb +75 -0
  91. data/registry/app/components/ui/dialog.html.erb +3 -0
  92. data/registry/app/components/ui/dialog.rb +41 -0
  93. data/registry/app/components/ui/divider.html.erb +9 -0
  94. data/registry/app/components/ui/divider.rb +41 -0
  95. data/registry/app/components/ui/icon.html.erb +1 -0
  96. data/registry/app/components/ui/icon.rb +41 -0
  97. data/registry/app/components/ui/image.html.erb +1 -0
  98. data/registry/app/components/ui/image.rb +74 -0
  99. data/registry/app/components/ui/input.html.erb +9 -0
  100. data/registry/app/components/ui/input.rb +127 -0
  101. data/registry/app/components/ui/input_group/addon.html.erb +6 -0
  102. data/registry/app/components/ui/input_group/addon.rb +51 -0
  103. data/registry/app/components/ui/input_group/field.rb +26 -0
  104. data/registry/app/components/ui/input_group.html.erb +5 -0
  105. data/registry/app/components/ui/input_group.rb +55 -0
  106. data/registry/app/components/ui/label.html.erb +2 -0
  107. data/registry/app/components/ui/label.rb +48 -0
  108. data/registry/app/components/ui/link.html.erb +1 -0
  109. data/registry/app/components/ui/link.rb +64 -0
  110. data/registry/app/components/ui/menu/content.html.erb +5 -0
  111. data/registry/app/components/ui/menu/content.rb +106 -0
  112. data/registry/app/components/ui/menu/group.html.erb +1 -0
  113. data/registry/app/components/ui/menu/group.rb +19 -0
  114. data/registry/app/components/ui/menu/item.html.erb +1 -0
  115. data/registry/app/components/ui/menu/item.rb +68 -0
  116. data/registry/app/components/ui/menu/label.html.erb +1 -0
  117. data/registry/app/components/ui/menu/label.rb +33 -0
  118. data/registry/app/components/ui/menu/separator.html.erb +1 -0
  119. data/registry/app/components/ui/menu/separator.rb +20 -0
  120. data/registry/app/components/ui/menu/trigger.html.erb +1 -0
  121. data/registry/app/components/ui/menu/trigger.rb +29 -0
  122. data/registry/app/components/ui/menu.html.erb +3 -0
  123. data/registry/app/components/ui/menu.rb +23 -0
  124. data/registry/app/components/ui/native_select.html.erb +16 -0
  125. data/registry/app/components/ui/native_select.rb +109 -0
  126. data/registry/app/components/ui/pagination.html.erb +34 -0
  127. data/registry/app/components/ui/pagination.rb +62 -0
  128. data/registry/app/components/ui/password_input.html.erb +20 -0
  129. data/registry/app/components/ui/password_input.rb +94 -0
  130. data/registry/app/components/ui/popover/content.html.erb +12 -0
  131. data/registry/app/components/ui/popover/content.rb +83 -0
  132. data/registry/app/components/ui/popover/trigger.html.erb +1 -0
  133. data/registry/app/components/ui/popover/trigger.rb +29 -0
  134. data/registry/app/components/ui/popover.html.erb +3 -0
  135. data/registry/app/components/ui/popover.rb +24 -0
  136. data/registry/app/components/ui/radio_group/item.html.erb +18 -0
  137. data/registry/app/components/ui/radio_group/item.rb +97 -0
  138. data/registry/app/components/ui/radio_group.html.erb +1 -0
  139. data/registry/app/components/ui/radio_group.rb +35 -0
  140. data/registry/app/components/ui/select.html.erb +60 -0
  141. data/registry/app/components/ui/select.rb +181 -0
  142. data/registry/app/components/ui/sheet/close.rb +4 -0
  143. data/registry/app/components/ui/sheet/content.html.erb +24 -0
  144. data/registry/app/components/ui/sheet/content.rb +97 -0
  145. data/registry/app/components/ui/sheet/trigger.rb +4 -0
  146. data/registry/app/components/ui/sheet.html.erb +3 -0
  147. data/registry/app/components/ui/sheet.rb +37 -0
  148. data/registry/app/components/ui/sidebar/content.html.erb +1 -0
  149. data/registry/app/components/ui/sidebar/content.rb +18 -0
  150. data/registry/app/components/ui/sidebar/footer.html.erb +3 -0
  151. data/registry/app/components/ui/sidebar/footer.rb +16 -0
  152. data/registry/app/components/ui/sidebar/group.html.erb +1 -0
  153. data/registry/app/components/ui/sidebar/group.rb +19 -0
  154. data/registry/app/components/ui/sidebar/group_label.html.erb +1 -0
  155. data/registry/app/components/ui/sidebar/group_label.rb +18 -0
  156. data/registry/app/components/ui/sidebar/header.html.erb +1 -0
  157. data/registry/app/components/ui/sidebar/header.rb +16 -0
  158. data/registry/app/components/ui/sidebar/inset.html.erb +1 -0
  159. data/registry/app/components/ui/sidebar/inset.rb +17 -0
  160. data/registry/app/components/ui/sidebar/menu.html.erb +1 -0
  161. data/registry/app/components/ui/sidebar/menu.rb +18 -0
  162. data/registry/app/components/ui/sidebar/menu_button.html.erb +1 -0
  163. data/registry/app/components/ui/sidebar/menu_button.rb +30 -0
  164. data/registry/app/components/ui/sidebar/menu_item.html.erb +1 -0
  165. data/registry/app/components/ui/sidebar/menu_item.rb +23 -0
  166. data/registry/app/components/ui/sidebar/trigger.html.erb +1 -0
  167. data/registry/app/components/ui/sidebar/trigger.rb +24 -0
  168. data/registry/app/components/ui/sidebar/wrapper.html.erb +1 -0
  169. data/registry/app/components/ui/sidebar/wrapper.rb +40 -0
  170. data/registry/app/components/ui/sidebar.html.erb +11 -0
  171. data/registry/app/components/ui/sidebar.rb +29 -0
  172. data/registry/app/components/ui/spinner.html.erb +1 -0
  173. data/registry/app/components/ui/spinner.rb +20 -0
  174. data/registry/app/components/ui/switch.html.erb +12 -0
  175. data/registry/app/components/ui/switch.rb +109 -0
  176. data/registry/app/components/ui/table/cell.html.erb +1 -0
  177. data/registry/app/components/ui/table/cell.rb +16 -0
  178. data/registry/app/components/ui/table/column.html.erb +1 -0
  179. data/registry/app/components/ui/table/column.rb +18 -0
  180. data/registry/app/components/ui/table/head.html.erb +1 -0
  181. data/registry/app/components/ui/table/head.rb +13 -0
  182. data/registry/app/components/ui/table/row.html.erb +1 -0
  183. data/registry/app/components/ui/table/row.rb +26 -0
  184. data/registry/app/components/ui/table.html.erb +4 -0
  185. data/registry/app/components/ui/table.rb +45 -0
  186. data/registry/app/components/ui/tabs/content.html.erb +1 -0
  187. data/registry/app/components/ui/tabs/content.rb +43 -0
  188. data/registry/app/components/ui/tabs/list.html.erb +1 -0
  189. data/registry/app/components/ui/tabs/list.rb +28 -0
  190. data/registry/app/components/ui/tabs/trigger.html.erb +1 -0
  191. data/registry/app/components/ui/tabs/trigger.rb +91 -0
  192. data/registry/app/components/ui/tabs.html.erb +3 -0
  193. data/registry/app/components/ui/tabs.rb +27 -0
  194. data/registry/app/components/ui/text.html.erb +1 -0
  195. data/registry/app/components/ui/text.rb +48 -0
  196. data/registry/app/components/ui/theme_toggle.html.erb +9 -0
  197. data/registry/app/components/ui/theme_toggle.rb +63 -0
  198. data/registry/app/components/ui/tooltip/content.html.erb +1 -0
  199. data/registry/app/components/ui/tooltip/content.rb +65 -0
  200. data/registry/app/components/ui/tooltip/trigger.html.erb +1 -0
  201. data/registry/app/components/ui/tooltip/trigger.rb +33 -0
  202. data/registry/app/components/ui/tooltip.html.erb +3 -0
  203. data/registry/app/components/ui/tooltip.rb +24 -0
  204. data/registry/app/components/ui/typography/blockquote.html.erb +1 -0
  205. data/registry/app/components/ui/typography/blockquote.rb +15 -0
  206. data/registry/app/components/ui/typography/code.html.erb +1 -0
  207. data/registry/app/components/ui/typography/code.rb +25 -0
  208. data/registry/app/components/ui/typography/h1.rb +7 -0
  209. data/registry/app/components/ui/typography/h2.rb +7 -0
  210. data/registry/app/components/ui/typography/h3.rb +7 -0
  211. data/registry/app/components/ui/typography/h4.rb +7 -0
  212. data/registry/app/components/ui/typography/large.rb +7 -0
  213. data/registry/app/components/ui/typography/lead.rb +7 -0
  214. data/registry/app/components/ui/typography/muted.rb +7 -0
  215. data/registry/app/components/ui/typography/small.rb +7 -0
  216. data/registry/app/javascript/ag_grid.ts +26 -0
  217. data/registry/app/javascript/cally.ts +8 -0
  218. data/registry/app/javascript/controllers/ui/_internal/use_dismiss.ts +88 -0
  219. data/registry/app/javascript/controllers/ui/_internal/use_floating.ts +112 -0
  220. data/registry/app/javascript/controllers/ui/accordion_controller.ts +100 -0
  221. data/registry/app/javascript/controllers/ui/alert_controller.ts +7 -0
  222. data/registry/app/javascript/controllers/ui/banner_controller.ts +7 -0
  223. data/registry/app/javascript/controllers/ui/data_grid_controller.ts +198 -0
  224. data/registry/app/javascript/controllers/ui/date_picker_controller.ts +141 -0
  225. data/registry/app/javascript/controllers/ui/dialog_controller.ts +70 -0
  226. data/registry/app/javascript/controllers/ui/menu_controller.ts +201 -0
  227. data/registry/app/javascript/controllers/ui/password_visibility_controller.ts +43 -0
  228. data/registry/app/javascript/controllers/ui/popover_controller.ts +108 -0
  229. data/registry/app/javascript/controllers/ui/select_controller.ts +274 -0
  230. data/registry/app/javascript/controllers/ui/sidebar_controller.ts +64 -0
  231. data/registry/app/javascript/controllers/ui/tabs_controller.ts +74 -0
  232. data/registry/app/javascript/controllers/ui/theme_controller.ts +101 -0
  233. data/registry/app/javascript/controllers/ui/tooltip_controller.ts +102 -0
  234. data/registry/app/javascript/velvetkit_icons.ts +58 -0
  235. data/registry/app/views/layouts/_theme_script.html.erb +19 -0
  236. data/registry/components.yml +102 -0
  237. data/registry/config/locales/velvetkit.en.yml +56 -0
  238. data/registry/docs/agents.md +62 -0
  239. data/registry/test/components/previews/ui/accordion_preview/faq_style.html.erb +20 -0
  240. data/registry/test/components/previews/ui/accordion_preview/multiple.html.erb +27 -0
  241. data/registry/test/components/previews/ui/accordion_preview/single.html.erb +22 -0
  242. data/registry/test/components/previews/ui/accordion_preview.rb +31 -0
  243. data/registry/test/components/previews/ui/alert_preview/title_only.html.erb +12 -0
  244. data/registry/test/components/previews/ui/alert_preview/variants.html.erb +22 -0
  245. data/registry/test/components/previews/ui/alert_preview/with_icon.html.erb +12 -0
  246. data/registry/test/components/previews/ui/alert_preview/with_link.html.erb +18 -0
  247. data/registry/test/components/previews/ui/alert_preview.rb +34 -0
  248. data/registry/test/components/previews/ui/avatar_group_preview/default.html.erb +11 -0
  249. data/registry/test/components/previews/ui/avatar_group_preview/overflow.html.erb +23 -0
  250. data/registry/test/components/previews/ui/avatar_group_preview/small.html.erb +12 -0
  251. data/registry/test/components/previews/ui/avatar_group_preview.rb +23 -0
  252. data/registry/test/components/previews/ui/avatar_preview/colors.html.erb +8 -0
  253. data/registry/test/components/previews/ui/avatar_preview/shapes.html.erb +10 -0
  254. data/registry/test/components/previews/ui/avatar_preview/sizes.html.erb +8 -0
  255. data/registry/test/components/previews/ui/avatar_preview/with_image.html.erb +10 -0
  256. data/registry/test/components/previews/ui/avatar_preview.rb +24 -0
  257. data/registry/test/components/previews/ui/badge_preview/sizes.html.erb +5 -0
  258. data/registry/test/components/previews/ui/badge_preview/variants.html.erb +8 -0
  259. data/registry/test/components/previews/ui/badge_preview/with_icon.html.erb +14 -0
  260. data/registry/test/components/previews/ui/badge_preview.rb +20 -0
  261. data/registry/test/components/previews/ui/banner_preview/introduction.html.erb +9 -0
  262. data/registry/test/components/previews/ui/banner_preview/promotion.html.erb +9 -0
  263. data/registry/test/components/previews/ui/banner_preview/with_illustration.html.erb +12 -0
  264. data/registry/test/components/previews/ui/banner_preview.rb +25 -0
  265. data/registry/test/components/previews/ui/breadcrumb_preview/collapsed.html.erb +50 -0
  266. data/registry/test/components/previews/ui/breadcrumb_preview/default.html.erb +8 -0
  267. data/registry/test/components/previews/ui/breadcrumb_preview/long_path.html.erb +8 -0
  268. data/registry/test/components/previews/ui/breadcrumb_preview/medium.html.erb +7 -0
  269. data/registry/test/components/previews/ui/breadcrumb_preview/with_avatar.html.erb +8 -0
  270. data/registry/test/components/previews/ui/breadcrumb_preview.rb +66 -0
  271. data/registry/test/components/previews/ui/button_preview/disabled.html.erb +39 -0
  272. data/registry/test/components/previews/ui/button_preview/icon_only.html.erb +17 -0
  273. data/registry/test/components/previews/ui/button_preview/loading.html.erb +12 -0
  274. data/registry/test/components/previews/ui/button_preview/sizes.html.erb +6 -0
  275. data/registry/test/components/previews/ui/button_preview/variants.html.erb +8 -0
  276. data/registry/test/components/previews/ui/button_preview/with_both_icons.html.erb +12 -0
  277. data/registry/test/components/previews/ui/button_preview/with_leading_icon.html.erb +22 -0
  278. data/registry/test/components/previews/ui/button_preview/with_trailing_icon.html.erb +14 -0
  279. data/registry/test/components/previews/ui/button_preview.rb +65 -0
  280. data/registry/test/components/previews/ui/button_to_preview/states.html.erb +8 -0
  281. data/registry/test/components/previews/ui/button_to_preview/variants.html.erb +5 -0
  282. data/registry/test/components/previews/ui/button_to_preview/with_icon.html.erb +11 -0
  283. data/registry/test/components/previews/ui/button_to_preview.rb +21 -0
  284. data/registry/test/components/previews/ui/card_preview/complete.html.erb +31 -0
  285. data/registry/test/components/previews/ui/card_preview/default.html.erb +7 -0
  286. data/registry/test/components/previews/ui/card_preview/settings_form.html.erb +25 -0
  287. data/registry/test/components/previews/ui/card_preview/with_footer.html.erb +13 -0
  288. data/registry/test/components/previews/ui/card_preview/with_header.html.erb +11 -0
  289. data/registry/test/components/previews/ui/card_preview.rb +44 -0
  290. data/registry/test/components/previews/ui/checkbox_preview/in_form.html.erb +9 -0
  291. data/registry/test/components/previews/ui/checkbox_preview/states.html.erb +22 -0
  292. data/registry/test/components/previews/ui/checkbox_preview/with_label.html.erb +5 -0
  293. data/registry/test/components/previews/ui/checkbox_preview.rb +27 -0
  294. data/registry/test/components/previews/ui/data_grid_preview.rb +18 -0
  295. data/registry/test/components/previews/ui/date_picker_preview/default.html.erb +3 -0
  296. data/registry/test/components/previews/ui/date_picker_preview/states.html.erb +7 -0
  297. data/registry/test/components/previews/ui/date_picker_preview.rb +13 -0
  298. data/registry/test/components/previews/ui/dialog_preview/confirmation.html.erb +11 -0
  299. data/registry/test/components/previews/ui/dialog_preview/custom_trigger.html.erb +16 -0
  300. data/registry/test/components/previews/ui/dialog_preview/default.html.erb +15 -0
  301. data/registry/test/components/previews/ui/dialog_preview/long_content.html.erb +16 -0
  302. data/registry/test/components/previews/ui/dialog_preview/playground.html.erb +12 -0
  303. data/registry/test/components/previews/ui/dialog_preview/scrollable_body.html.erb +29 -0
  304. data/registry/test/components/previews/ui/dialog_preview/sizes.html.erb +46 -0
  305. data/registry/test/components/previews/ui/dialog_preview/without_close_button.html.erb +11 -0
  306. data/registry/test/components/previews/ui/dialog_preview/without_title.html.erb +13 -0
  307. data/registry/test/components/previews/ui/dialog_preview.rb +38 -0
  308. data/registry/test/components/previews/ui/divider_preview/horizontal.html.erb +5 -0
  309. data/registry/test/components/previews/ui/divider_preview/vertical.html.erb +9 -0
  310. data/registry/test/components/previews/ui/divider_preview/with_label.html.erb +5 -0
  311. data/registry/test/components/previews/ui/divider_preview.rb +19 -0
  312. data/registry/test/components/previews/ui/icon_preview/common.html.erb +8 -0
  313. data/registry/test/components/previews/ui/icon_preview/sizes.html.erb +7 -0
  314. data/registry/test/components/previews/ui/icon_preview.rb +20 -0
  315. data/registry/test/components/previews/ui/image_preview/radii.html.erb +8 -0
  316. data/registry/test/components/previews/ui/image_preview/sizing.html.erb +9 -0
  317. data/registry/test/components/previews/ui/image_preview.rb +16 -0
  318. data/registry/test/components/previews/ui/input_group_preview/addons.html.erb +29 -0
  319. data/registry/test/components/previews/ui/input_group_preview/playground.html.erb +12 -0
  320. data/registry/test/components/previews/ui/input_group_preview.rb +17 -0
  321. data/registry/test/components/previews/ui/input_preview/sizes.html.erb +5 -0
  322. data/registry/test/components/previews/ui/input_preview/states.html.erb +7 -0
  323. data/registry/test/components/previews/ui/input_preview/with_labels.html.erb +10 -0
  324. data/registry/test/components/previews/ui/input_preview.rb +33 -0
  325. data/registry/test/components/previews/ui/label_preview/sizes.html.erb +6 -0
  326. data/registry/test/components/previews/ui/label_preview/states.html.erb +6 -0
  327. data/registry/test/components/previews/ui/label_preview/weights.html.erb +5 -0
  328. data/registry/test/components/previews/ui/label_preview.rb +22 -0
  329. data/registry/test/components/previews/ui/link_preview/sizes.html.erb +5 -0
  330. data/registry/test/components/previews/ui/link_preview/variants.html.erb +8 -0
  331. data/registry/test/components/previews/ui/link_preview/with_icons.html.erb +11 -0
  332. data/registry/test/components/previews/ui/link_preview.rb +20 -0
  333. data/registry/test/components/previews/ui/menu_preview/positions.html.erb +122 -0
  334. data/registry/test/components/previews/ui/menu_preview/variants.html.erb +218 -0
  335. data/registry/test/components/previews/ui/menu_preview.rb +26 -0
  336. data/registry/test/components/previews/ui/native_select_preview/sizes.html.erb +6 -0
  337. data/registry/test/components/previews/ui/native_select_preview/states.html.erb +6 -0
  338. data/registry/test/components/previews/ui/native_select_preview.rb +23 -0
  339. data/registry/test/components/previews/ui/pagination_preview/states.html.erb +15 -0
  340. data/registry/test/components/previews/ui/pagination_preview.rb +15 -0
  341. data/registry/test/components/previews/ui/password_input_preview/states.html.erb +19 -0
  342. data/registry/test/components/previews/ui/password_input_preview.rb +21 -0
  343. data/registry/test/components/previews/ui/popover_preview/default.html.erb +7 -0
  344. data/registry/test/components/previews/ui/popover_preview/positions.html.erb +30 -0
  345. data/registry/test/components/previews/ui/popover_preview/with_close_button.html.erb +8 -0
  346. data/registry/test/components/previews/ui/popover_preview/with_rich_content.html.erb +17 -0
  347. data/registry/test/components/previews/ui/popover_preview/without_title.html.erb +9 -0
  348. data/registry/test/components/previews/ui/popover_preview.rb +32 -0
  349. data/registry/test/components/previews/ui/radio_group_preview/default.html.erb +10 -0
  350. data/registry/test/components/previews/ui/radio_group_preview/disabled.html.erb +17 -0
  351. data/registry/test/components/previews/ui/radio_group_preview/with_required.html.erb +10 -0
  352. data/registry/test/components/previews/ui/radio_group_preview.rb +25 -0
  353. data/registry/test/components/previews/ui/select_preview/default.html.erb +4 -0
  354. data/registry/test/components/previews/ui/select_preview/long_list.html.erb +5 -0
  355. data/registry/test/components/previews/ui/select_preview/multiple.html.erb +6 -0
  356. data/registry/test/components/previews/ui/select_preview/sizes.html.erb +5 -0
  357. data/registry/test/components/previews/ui/select_preview/states.html.erb +6 -0
  358. data/registry/test/components/previews/ui/select_preview.rb +45 -0
  359. data/registry/test/components/previews/ui/sheet_preview/bottom.html.erb +12 -0
  360. data/registry/test/components/previews/ui/sheet_preview/left.html.erb +10 -0
  361. data/registry/test/components/previews/ui/sheet_preview/playground.html.erb +15 -0
  362. data/registry/test/components/previews/ui/sheet_preview/right.html.erb +11 -0
  363. data/registry/test/components/previews/ui/sheet_preview/top.html.erb +8 -0
  364. data/registry/test/components/previews/ui/sheet_preview.rb +22 -0
  365. data/registry/test/components/previews/ui/sidebar_preview/_brand.html.erb +14 -0
  366. data/registry/test/components/previews/ui/sidebar_preview/_shell.html.erb +60 -0
  367. data/registry/test/components/previews/ui/sidebar_preview/collapsible.html.erb +55 -0
  368. data/registry/test/components/previews/ui/sidebar_preview/default.html.erb +17 -0
  369. data/registry/test/components/previews/ui/sidebar_preview/with_dropdown_header.html.erb +33 -0
  370. data/registry/test/components/previews/ui/sidebar_preview/with_groups.html.erb +22 -0
  371. data/registry/test/components/previews/ui/sidebar_preview.rb +15 -0
  372. data/registry/test/components/previews/ui/spinner_preview/in_buttons.html.erb +7 -0
  373. data/registry/test/components/previews/ui/spinner_preview/sizes.html.erb +7 -0
  374. data/registry/test/components/previews/ui/spinner_preview.rb +19 -0
  375. data/registry/test/components/previews/ui/switch_preview/default.html.erb +4 -0
  376. data/registry/test/components/previews/ui/switch_preview/sizes.html.erb +5 -0
  377. data/registry/test/components/previews/ui/switch_preview/states.html.erb +7 -0
  378. data/registry/test/components/previews/ui/switch_preview.rb +26 -0
  379. data/registry/test/components/previews/ui/table_preview/customers.html.erb +15 -0
  380. data/registry/test/components/previews/ui/table_preview/empty.html.erb +8 -0
  381. data/registry/test/components/previews/ui/table_preview/with_caption.html.erb +14 -0
  382. data/registry/test/components/previews/ui/table_preview.rb +12 -0
  383. data/registry/test/components/previews/ui/tabs_preview/default.html.erb +39 -0
  384. data/registry/test/components/previews/ui/tabs_preview/pill_variant.html.erb +18 -0
  385. data/registry/test/components/previews/ui/tabs_preview/settings_form.html.erb +42 -0
  386. data/registry/test/components/previews/ui/tabs_preview/with_icons.html.erb +34 -0
  387. data/registry/test/components/previews/ui/tabs_preview.rb +33 -0
  388. data/registry/test/components/previews/ui/text_preview/sizes.html.erb +10 -0
  389. data/registry/test/components/previews/ui/text_preview/tones.html.erb +5 -0
  390. data/registry/test/components/previews/ui/text_preview/weights.html.erb +6 -0
  391. data/registry/test/components/previews/ui/text_preview.rb +26 -0
  392. data/registry/test/components/previews/ui/theme_toggle_preview/in_a_header.html.erb +14 -0
  393. data/registry/test/components/previews/ui/theme_toggle_preview/palette.html.erb +47 -0
  394. data/registry/test/components/previews/ui/theme_toggle_preview.rb +15 -0
  395. data/registry/test/components/previews/ui/tooltip_preview/default.html.erb +8 -0
  396. data/registry/test/components/previews/ui/tooltip_preview/positions.html.erb +34 -0
  397. data/registry/test/components/previews/ui/tooltip_preview/with_button.html.erb +8 -0
  398. data/registry/test/components/previews/ui/tooltip_preview/with_icon.html.erb +10 -0
  399. data/registry/test/components/previews/ui/tooltip_preview.rb +29 -0
  400. data/registry/test/components/previews/ui/typography_preview/article.html.erb +11 -0
  401. data/registry/test/components/previews/ui/typography_preview/code_and_quotes.html.erb +6 -0
  402. data/registry/test/components/previews/ui/typography_preview/headings.html.erb +6 -0
  403. data/registry/test/components/previews/ui/typography_preview/text_styles.html.erb +7 -0
  404. data/registry/test/components/previews/ui/typography_preview.rb +23 -0
  405. data/registry/test/components/ui/accordion/content_test.rb +64 -0
  406. data/registry/test/components/ui/accordion/item_test.rb +61 -0
  407. data/registry/test/components/ui/accordion/trigger_test.rb +80 -0
  408. data/registry/test/components/ui/accordion_test.rb +96 -0
  409. data/registry/test/components/ui/alert/description_test.rb +37 -0
  410. data/registry/test/components/ui/alert/title_test.rb +38 -0
  411. data/registry/test/components/ui/alert_test.rb +184 -0
  412. data/registry/test/components/ui/avatar_group_test.rb +79 -0
  413. data/registry/test/components/ui/avatar_test.rb +129 -0
  414. data/registry/test/components/ui/badge_test.rb +112 -0
  415. data/registry/test/components/ui/banner_test.rb +116 -0
  416. data/registry/test/components/ui/base_test.rb +98 -0
  417. data/registry/test/components/ui/breadcrumb/ellipsis_test.rb +51 -0
  418. data/registry/test/components/ui/breadcrumb/item_test.rb +39 -0
  419. data/registry/test/components/ui/breadcrumb/list_test.rb +34 -0
  420. data/registry/test/components/ui/breadcrumb/separator_test.rb +36 -0
  421. data/registry/test/components/ui/breadcrumb_test.rb +276 -0
  422. data/registry/test/components/ui/button_test.rb +276 -0
  423. data/registry/test/components/ui/button_to_test.rb +74 -0
  424. data/registry/test/components/ui/card/content_test.rb +41 -0
  425. data/registry/test/components/ui/card/description_test.rb +32 -0
  426. data/registry/test/components/ui/card/footer_test.rb +34 -0
  427. data/registry/test/components/ui/card/header_test.rb +57 -0
  428. data/registry/test/components/ui/card/title_test.rb +34 -0
  429. data/registry/test/components/ui/card_test.rb +67 -0
  430. data/registry/test/components/ui/checkbox_test.rb +236 -0
  431. data/registry/test/components/ui/data_grid_test.rb +88 -0
  432. data/registry/test/components/ui/date_picker_test.rb +168 -0
  433. data/registry/test/components/ui/dialog/content_test.rb +156 -0
  434. data/registry/test/components/ui/dialog/trigger_test.rb +74 -0
  435. data/registry/test/components/ui/dialog_test.rb +54 -0
  436. data/registry/test/components/ui/divider_test.rb +69 -0
  437. data/registry/test/components/ui/icon_test.rb +82 -0
  438. data/registry/test/components/ui/image_test.rb +74 -0
  439. data/registry/test/components/ui/input_group_test.rb +143 -0
  440. data/registry/test/components/ui/input_test.rb +251 -0
  441. data/registry/test/components/ui/label_test.rb +96 -0
  442. data/registry/test/components/ui/link_test.rb +95 -0
  443. data/registry/test/components/ui/menu/content_test.rb +233 -0
  444. data/registry/test/components/ui/menu/group_test.rb +24 -0
  445. data/registry/test/components/ui/menu/item_test.rb +76 -0
  446. data/registry/test/components/ui/menu/label_test.rb +43 -0
  447. data/registry/test/components/ui/menu/separator_test.rb +32 -0
  448. data/registry/test/components/ui/menu/trigger_test.rb +109 -0
  449. data/registry/test/components/ui/menu_test.rb +152 -0
  450. data/registry/test/components/ui/native_select_test.rb +118 -0
  451. data/registry/test/components/ui/pagination_test.rb +92 -0
  452. data/registry/test/components/ui/password_input_test.rb +98 -0
  453. data/registry/test/components/ui/popover/content_test.rb +226 -0
  454. data/registry/test/components/ui/popover/trigger_test.rb +83 -0
  455. data/registry/test/components/ui/popover_test.rb +101 -0
  456. data/registry/test/components/ui/radio_group/item_test.rb +14 -0
  457. data/registry/test/components/ui/radio_group_test.rb +139 -0
  458. data/registry/test/components/ui/select_test.rb +181 -0
  459. data/registry/test/components/ui/sheet/content_test.rb +135 -0
  460. data/registry/test/components/ui/sheet/trigger_test.rb +23 -0
  461. data/registry/test/components/ui/sheet_test.rb +43 -0
  462. data/registry/test/components/ui/sidebar/menu_button_test.rb +43 -0
  463. data/registry/test/components/ui/sidebar/trigger_test.rb +17 -0
  464. data/registry/test/components/ui/sidebar/wrapper_test.rb +15 -0
  465. data/registry/test/components/ui/sidebar_test.rb +43 -0
  466. data/registry/test/components/ui/spinner_test.rb +63 -0
  467. data/registry/test/components/ui/switch_test.rb +206 -0
  468. data/registry/test/components/ui/table_test.rb +106 -0
  469. data/registry/test/components/ui/tabs/content_test.rb +93 -0
  470. data/registry/test/components/ui/tabs/list_test.rb +65 -0
  471. data/registry/test/components/ui/tabs/trigger_test.rb +159 -0
  472. data/registry/test/components/ui/tabs_test.rb +88 -0
  473. data/registry/test/components/ui/text_test.rb +84 -0
  474. data/registry/test/components/ui/theme_toggle_test.rb +88 -0
  475. data/registry/test/components/ui/tooltip/content_test.rb +135 -0
  476. data/registry/test/components/ui/tooltip/trigger_test.rb +52 -0
  477. data/registry/test/components/ui/tooltip_test.rb +97 -0
  478. data/registry/test/components/ui/typography/code_blockquote_test.rb +57 -0
  479. data/registry/test/components/ui/typography/heading_test.rb +71 -0
  480. data/registry/test/components/ui/typography/text_variants_test.rb +71 -0
  481. metadata +540 -0
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::TextPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param as select { choices: [p, span, div] }
6
+ # @param size select { choices: [xs, sm, base, lg, xl, 2xl, 3xl, 4xl] }
7
+ # @param weight select { choices: [normal, medium, semibold, bold] }
8
+ # @param tone select { choices: [default, muted, destructive] }
9
+ def playground(as: :p, size: :base, weight: :normal, tone: :default)
10
+ render(UI::Text.new(
11
+ as: as.to_sym,
12
+ size: size.to_sym,
13
+ weight: weight.to_sym,
14
+ tone: tone.to_sym
15
+ )) { "The quick brown fox jumps over the lazy dog." }
16
+ end
17
+
18
+ # @label Sizes
19
+ def sizes; render_with_template; end
20
+
21
+ # @label Weights
22
+ def weights; render_with_template; end
23
+
24
+ # @label Tones
25
+ def tones; render_with_template; end
26
+ end
@@ -0,0 +1,14 @@
1
+ <%# Mirrors the real app header: sidebar trigger left, theme toggle right. %>
2
+ <div class="rounded-lg border overflow-hidden">
3
+ <header class="flex border-b h-14 shrink-0 items-center gap-2">
4
+ <div class="flex items-center gap-2 px-2">
5
+ <%= render(UI::Sidebar::Trigger.new(sidebar_id: "preview-sidebar")) %>
6
+ </div>
7
+ <div class="ml-auto flex items-center gap-2 px-2">
8
+ <%= render(UI::ThemeToggle.new) %>
9
+ </div>
10
+ </header>
11
+ <div class="p-6">
12
+ <%= render(UI::Text.new) { "Switch the theme with the control above — the whole page follows." } %>
13
+ </div>
14
+ </div>
@@ -0,0 +1,47 @@
1
+ <%# Every semantic token side by side. The fastest way to review both themes:
2
+ flip the toggle and check nothing goes muddy or illegible. %>
3
+ <% pairs = [
4
+ ["background", "foreground"], ["card", "card-foreground"],
5
+ ["popover", "popover-foreground"], ["primary", "primary-foreground"],
6
+ ["secondary", "secondary-foreground"], ["muted", "muted-foreground"],
7
+ ["accent", "accent-foreground"], ["sidebar", "sidebar-foreground"],
8
+ ["sidebar-accent", "sidebar-accent-foreground"]
9
+ ] %>
10
+ <div class="flex flex-col gap-6">
11
+ <div class="flex items-center gap-3">
12
+ <%= render(UI::ThemeToggle.new) %>
13
+ <%= render(UI::Typography::Muted.new) { "Flip the theme, then scan for anything unreadable." } %>
14
+ </div>
15
+
16
+ <div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
17
+ <% pairs.each do |bg, fg| %>
18
+ <div class="rounded-md border p-4" style="background: var(--<%= bg %>); color: var(--<%= fg %>);">
19
+ <div class="text-sm font-medium">--<%= bg %></div>
20
+ <div class="text-xs opacity-80">--<%= fg %></div>
21
+ </div>
22
+ <% end %>
23
+ </div>
24
+
25
+ <div class="flex flex-wrap items-center gap-3">
26
+ <%= render(UI::Button.new) { "Primary" } %>
27
+ <%= render(UI::Button.new(variant: :secondary)) { "Secondary" } %>
28
+ <%= render(UI::Button.new(variant: :outline)) { "Outline" } %>
29
+ <%= render(UI::Button.new(variant: :ghost)) { "Ghost" } %>
30
+ <%= render(UI::Button.new(variant: :destructive)) { "Destructive" } %>
31
+ </div>
32
+
33
+ <div class="flex flex-wrap items-center gap-3">
34
+ <% %i[default secondary destructive outline success warning].each do |v| %>
35
+ <%= render(UI::Badge.new(variant: v)) { v.to_s.capitalize } %>
36
+ <% end %>
37
+ </div>
38
+
39
+ <div class="flex flex-col gap-3 max-w-md">
40
+ <% %i[default destructive warning success info].each do |v| %>
41
+ <%= render(UI::Alert.new(variant: v)) do |alert| %>
42
+ <% alert.with_title { v.to_s.capitalize } %>
43
+ <% alert.with_description { "How the #{v} alert reads in this theme." } %>
44
+ <% end %>
45
+ <% end %>
46
+ </div>
47
+ </div>
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::ThemeTogglePreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param label text
6
+ def playground(label: "Change theme")
7
+ render(UI::ThemeToggle.new(label: label))
8
+ end
9
+
10
+ # @label In A Header
11
+ def in_a_header; render_with_template; end
12
+
13
+ # @label Palette
14
+ def palette; render_with_template; end
15
+ end
@@ -0,0 +1,8 @@
1
+ <%= render(UI::Tooltip.new) do |tooltip| %>
2
+ <% tooltip.with_trigger do %>
3
+ <%= tag.button("Hover me", class: "px-4 py-2 bg-primary text-primary-foreground rounded-md") %>
4
+ <% end %>
5
+ <% tooltip.with_content do %>
6
+ Add to library
7
+ <% end %>
8
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <div class="flex flex-wrap gap-8 p-8">
2
+ <%= render(UI::Tooltip.new) do |tooltip| %>
3
+ <% tooltip.with_trigger do %>
4
+ <%= tag.button("Top", class: "px-4 py-2 bg-primary text-primary-foreground rounded-md") %>
5
+ <% end %>
6
+ <% tooltip.with_content(side: :top) do %>
7
+ Tooltip on top
8
+ <% end %>
9
+ <% end %>
10
+ <%= render(UI::Tooltip.new) do |tooltip| %>
11
+ <% tooltip.with_trigger do %>
12
+ <%= tag.button("Right", class: "px-4 py-2 bg-primary text-primary-foreground rounded-md") %>
13
+ <% end %>
14
+ <% tooltip.with_content(side: :right) do %>
15
+ Tooltip on right
16
+ <% end %>
17
+ <% end %>
18
+ <%= render(UI::Tooltip.new) do |tooltip| %>
19
+ <% tooltip.with_trigger do %>
20
+ <%= tag.button("Bottom", class: "px-4 py-2 bg-primary text-primary-foreground rounded-md") %>
21
+ <% end %>
22
+ <% tooltip.with_content(side: :bottom) do %>
23
+ Tooltip on bottom
24
+ <% end %>
25
+ <% end %>
26
+ <%= render(UI::Tooltip.new) do |tooltip| %>
27
+ <% tooltip.with_trigger do %>
28
+ <%= tag.button("Left", class: "px-4 py-2 bg-primary text-primary-foreground rounded-md") %>
29
+ <% end %>
30
+ <% tooltip.with_content(side: :left) do %>
31
+ Tooltip on left
32
+ <% end %>
33
+ <% end %>
34
+ </div>
@@ -0,0 +1,8 @@
1
+ <%= render(UI::Tooltip.new) do |tooltip| %>
2
+ <% tooltip.with_trigger do %>
3
+ <%= tag.button("Save Changes", class: "px-4 py-2 bg-primary text-primary-foreground rounded-md") %>
4
+ <% end %>
5
+ <% tooltip.with_content do %>
6
+ Save your changes to the document
7
+ <% end %>
8
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <%= render(UI::Tooltip.new) do |tooltip| %>
2
+ <% tooltip.with_trigger do %>
3
+ <span class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-muted hover:bg-muted/80 cursor-pointer">
4
+ <%= render(UI::Icon.new(name: :info, size: :sm)) %>
5
+ </span>
6
+ <% end %>
7
+ <% tooltip.with_content do %>
8
+ More information about this feature
9
+ <% end %>
10
+ <% end %>
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::TooltipPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param side select { choices: [top, right, bottom, left] }
6
+ # @param align select { choices: [start, center, end] }
7
+ def playground(side: :top, align: :center)
8
+ render(UI::Tooltip.new) do |tooltip|
9
+ tooltip.with_trigger do
10
+ tag.button("Hover me", class: "px-4 py-2 bg-primary text-primary-foreground rounded-md")
11
+ end
12
+ tooltip.with_content(side: side.to_sym, align: align.to_sym) do
13
+ "This is a tooltip"
14
+ end
15
+ end
16
+ end
17
+
18
+ # @label Default
19
+ def default; render_with_template; end
20
+
21
+ # @label Positions
22
+ def positions; render_with_template; end
23
+
24
+ # @label With Button
25
+ def with_button; render_with_template; end
26
+
27
+ # @label With Icon
28
+ def with_icon; render_with_template; end
29
+ end
@@ -0,0 +1,11 @@
1
+ <article class="max-w-2xl space-y-4">
2
+ <%= render(UI::Typography::H2.new) { "Release notes" } %>
3
+ <%= render(UI::Typography::Lead.new) { "This month's update makes reports faster to build and easier to share." } %>
4
+ <%= render(UI::Typography::H3.new) { "What changed" } %>
5
+ <%= render(UI::Text.new) do %>
6
+ Reports now load in under a second, and you can share one with a link. Owners can still limit sharing from
7
+ <%= render(UI::Typography::Code.new) { "Settings › Sharing" } %>.
8
+ <% end %>
9
+ <%= render(UI::Typography::Blockquote.new) { "We asked for faster reports and got them the same month." } %>
10
+ <%= render(UI::Typography::Muted.new) { "Published 21 October" } %>
11
+ </article>
@@ -0,0 +1,6 @@
1
+ <div class="max-w-xl space-y-6">
2
+ <%= render(UI::Text.new) do %>
3
+ Run <%= render(UI::Typography::Code.new) { "bin/rails generate velvetkit:add dialog" } %> to add a dialog.
4
+ <% end %>
5
+ <%= render(UI::Typography::Blockquote.new) { "Good components disappear: you notice the work, not the widgets." } %>
6
+ </div>
@@ -0,0 +1,6 @@
1
+ <div class="space-y-4">
2
+ <%= render(UI::Typography::H1.new) { "Heading 1" } %>
3
+ <%= render(UI::Typography::H2.new) { "Heading 2" } %>
4
+ <%= render(UI::Typography::H3.new) { "Heading 3" } %>
5
+ <%= render(UI::Typography::H4.new) { "Heading 4" } %>
6
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="max-w-xl space-y-4">
2
+ <%= render(UI::Typography::Lead.new) { "Lead: an introduction that sets up the page in a sentence or two." } %>
3
+ <%= render(UI::Typography::Large.new) { "Large: a line that needs a little more weight." } %>
4
+ <%= render(UI::Text.new) { "Body text, for most of what a page says." } %>
5
+ <%= render(UI::Typography::Muted.new) { "Muted: secondary detail, like a caption or a hint." } %>
6
+ <%= render(UI::Typography::Small.new) { "Small: a label or fine print." } %>
7
+ </div>
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::TypographyPreview < ViewComponent::Preview
4
+ # @label Headings
5
+ def headings
6
+ render_with_template
7
+ end
8
+
9
+ # @label Text styles
10
+ def text_styles
11
+ render_with_template
12
+ end
13
+
14
+ # @label Code and quotes
15
+ def code_and_quotes
16
+ render_with_template
17
+ end
18
+
19
+ # @label An article
20
+ def article
21
+ render_with_template
22
+ end
23
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class UI::Accordion::ContentTest < ViewComponent::TestCase
6
+ def setup_fixtures(*); end
7
+ def teardown_fixtures(*); end
8
+
9
+ def content_el
10
+ page.find("[data-ui--accordion-target='content']")
11
+ end
12
+
13
+ def test_renders_collapsed_by_default
14
+ render_inline(UI::Accordion::Content.new) { "Body" }
15
+ assert_equal "closed", content_el["data-state"]
16
+ assert_includes content_el[:class], "grid-rows-[0fr]"
17
+ end
18
+
19
+ def test_has_region_role
20
+ render_inline(UI::Accordion::Content.new) { "Body" }
21
+ assert_equal "region", content_el[:role]
22
+ end
23
+
24
+ def test_aria_labelledby_when_trigger_id_provided
25
+ render_inline(UI::Accordion::Content.new(trigger_id: "trigger-1")) { "Body" }
26
+ assert_equal "trigger-1", content_el["aria-labelledby"]
27
+ end
28
+
29
+ def test_auto_generated_id
30
+ render_inline(UI::Accordion::Content.new) { "Body" }
31
+ assert content_el[:id].present?
32
+ assert content_el[:id].start_with?("ui-accordion-content-")
33
+ end
34
+
35
+ def test_custom_id_preserved
36
+ render_inline(UI::Accordion::Content.new(id: "my-content")) { "Body" }
37
+ assert_selector "[data-ui--accordion-target='content']#my-content"
38
+ end
39
+
40
+ def test_renders_content
41
+ render_inline(UI::Accordion::Content.new) { "Body text" }
42
+ assert_selector "[data-ui--accordion-target='content']", text: "Body text"
43
+ end
44
+
45
+ def test_wrapper_uses_grid_animation_classes
46
+ render_inline(UI::Accordion::Content.new) { "x" }
47
+ classes = content_el[:class].split
48
+ assert_includes classes, "grid"
49
+ assert_includes classes, "grid-rows-[0fr]"
50
+ assert_includes classes, "transition-[grid-template-rows]"
51
+ assert_includes classes, "duration-200"
52
+ end
53
+
54
+ def test_user_class_merge
55
+ render_inline(UI::Accordion::Content.new(class: "bg-red-500")) { "x" }
56
+ classes = content_el[:class].split
57
+ assert_includes classes, "bg-red-500"
58
+ end
59
+
60
+ def test_html_attrs_passthrough
61
+ render_inline(UI::Accordion::Content.new(data: { test_id: "content-x" })) { "x" }
62
+ assert_selector "[data-test-id='content-x']"
63
+ end
64
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class UI::Accordion::ItemTest < ViewComponent::TestCase
6
+ def setup_fixtures(*); end
7
+ def teardown_fixtures(*); end
8
+
9
+ def test_renders_div_with_item_target
10
+ render_inline(UI::Accordion::Item.new(value: "test")) { "Content" }
11
+ div = page.find("[data-ui--accordion-target='item']")
12
+ assert_equal "test", div["data-value"]
13
+ assert_equal "closed", div["data-state"]
14
+ end
15
+
16
+ def test_renders_trigger_slot
17
+ render_inline(UI::Accordion::Item.new(value: "test")) do |item|
18
+ item.with_trigger { "Trigger" }
19
+ end
20
+ assert_selector "button[data-ui--accordion-target='trigger']", text: "Trigger"
21
+ end
22
+
23
+ def test_renders_content_slot_via_with_content_alias
24
+ render_inline(UI::Accordion::Item.new(value: "test")) do |item|
25
+ item.with_content { "Body" }
26
+ end
27
+ assert_selector "[data-ui--accordion-target='content']", text: "Body", visible: :all
28
+ end
29
+
30
+ def test_renders_trigger_and_content_together
31
+ render_inline(UI::Accordion::Item.new(value: "test")) do |item|
32
+ item.with_trigger { "Trigger" }
33
+ item.with_content { "Body" }
34
+ end
35
+ assert_selector "button", text: "Trigger"
36
+ assert_selector "[data-ui--accordion-target='content']", text: "Body", visible: :all
37
+ end
38
+
39
+ def test_default_classes
40
+ render_inline(UI::Accordion::Item.new(value: "test")) { "x" }
41
+ classes = page.find("[data-ui--accordion-target='item']")[:class].split
42
+ assert_includes classes, "border-b"
43
+ end
44
+
45
+ def test_last_item_no_border
46
+ render_inline(UI::Accordion::Item.new(value: "test", class: "last:border-b-0")) { "x" }
47
+ classes = page.find("[data-ui--accordion-target='item']")[:class].split
48
+ assert_includes classes, "last:border-b-0"
49
+ end
50
+
51
+ def test_user_class_merge
52
+ render_inline(UI::Accordion::Item.new(value: "test", class: "my-item")) { "x" }
53
+ classes = page.find("[data-ui--accordion-target='item']")[:class].split
54
+ assert_includes classes, "my-item"
55
+ end
56
+
57
+ def test_html_attrs_passthrough
58
+ render_inline(UI::Accordion::Item.new(value: "test", data: { test_id: "item-x" })) { "x" }
59
+ assert_selector "[data-ui--accordion-target='item'][data-test-id='item-x']"
60
+ end
61
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class UI::Accordion::TriggerTest < ViewComponent::TestCase
6
+ def setup_fixtures(*); end
7
+ def teardown_fixtures(*); end
8
+
9
+ def test_renders_button_inside_heading
10
+ render_inline(UI::Accordion::Trigger.new) { "Trigger" }
11
+ assert_selector "h3 button", text: "Trigger"
12
+ end
13
+
14
+ def test_has_aria_expanded_false_by_default
15
+ render_inline(UI::Accordion::Trigger.new) { "x" }
16
+ btn = page.find("button")
17
+ assert_equal "false", btn["aria-expanded"]
18
+ end
19
+
20
+ def test_has_stimulus_target_and_action
21
+ render_inline(UI::Accordion::Trigger.new) { "x" }
22
+ btn = page.find("button")
23
+ assert_equal "trigger", btn["data-ui--accordion-target"]
24
+ assert_includes btn["data-action"], "click->ui--accordion#toggle"
25
+ assert_includes btn["data-action"], "keydown->ui--accordion#keydown"
26
+ end
27
+
28
+ def test_renders_chevron_icon
29
+ render_inline(UI::Accordion::Trigger.new) { "x" }
30
+ assert_selector "button i[data-lucide='chevron-right']"
31
+ end
32
+
33
+ def test_aria_controls_when_content_id_provided
34
+ render_inline(UI::Accordion::Trigger.new(content_id: "panel-1")) { "x" }
35
+ btn = page.find("button")
36
+ assert_equal "panel-1", btn["aria-controls"]
37
+ end
38
+
39
+ def test_auto_generated_id
40
+ render_inline(UI::Accordion::Trigger.new) { "x" }
41
+ btn = page.find("button")
42
+ assert btn[:id].present?
43
+ assert btn[:id].start_with?("ui-accordion-trigger-")
44
+ end
45
+
46
+ def test_custom_id_preserved
47
+ render_inline(UI::Accordion::Trigger.new(id: "my-trigger")) { "x" }
48
+ assert_selector "button#my-trigger"
49
+ end
50
+
51
+ def test_disabled
52
+ render_inline(UI::Accordion::Trigger.new(disabled: true)) { "x" }
53
+ assert_selector "button[disabled]"
54
+ end
55
+
56
+ def test_default_classes
57
+ render_inline(UI::Accordion::Trigger.new) { "x" }
58
+ classes = page.find("button")[:class].split
59
+ assert_includes classes, "group"
60
+ assert_includes classes, "flex"
61
+ assert_includes classes, "w-full"
62
+ assert_includes classes, "items-center"
63
+ assert_includes classes, "gap-3"
64
+ assert_includes classes, "py-2"
65
+ assert_includes classes, "text-sm"
66
+ assert_includes classes, "font-medium"
67
+ assert_includes classes, "text-primary"
68
+ end
69
+
70
+ def test_user_class_merge
71
+ render_inline(UI::Accordion::Trigger.new(class: "bg-red-500")) { "x" }
72
+ classes = page.find("button")[:class].split
73
+ assert_includes classes, "bg-red-500"
74
+ end
75
+
76
+ def test_html_attrs_passthrough
77
+ render_inline(UI::Accordion::Trigger.new(data: { test_id: "trig" })) { "x" }
78
+ assert_selector "button[data-test-id='trig']"
79
+ end
80
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class UI::AccordionTest < ViewComponent::TestCase
6
+ def setup_fixtures(*); end
7
+ def teardown_fixtures(*); end
8
+
9
+ def render_accordion(type: :single, default_value: "item-1")
10
+ render_inline(UI::Accordion.new(type: type, default_value: default_value)) do |accordion|
11
+ accordion.with_item(value: "item-1") do |item|
12
+ item.with_trigger { "Trigger 1" }
13
+ item.with_content { "Content 1" }
14
+ end
15
+ accordion.with_item(value: "item-2") do |item|
16
+ item.with_trigger { "Trigger 2" }
17
+ item.with_content { "Content 2" }
18
+ end
19
+ end
20
+ end
21
+
22
+ def test_root_carries_controller
23
+ render_accordion
24
+ root = page.find("[data-controller~='ui--accordion']")
25
+ assert root.present?
26
+ end
27
+
28
+ def test_single_type_value
29
+ render_accordion(type: :single)
30
+ root = page.find("[data-controller~='ui--accordion']")
31
+ assert_equal "single", root["data-ui--accordion-type-value"]
32
+ end
33
+
34
+ def test_multiple_type_value
35
+ render_accordion(type: :multiple, default_value: [ "item-1", "item-2" ])
36
+ root = page.find("[data-controller~='ui--accordion']")
37
+ assert_equal "multiple", root["data-ui--accordion-type-value"]
38
+ end
39
+
40
+ def test_default_single_value
41
+ render_accordion(default_value: "item-1")
42
+ root = page.find("[data-controller~='ui--accordion']")
43
+ assert_includes root["data-ui--accordion-values-value"], "item-1"
44
+ end
45
+
46
+ def test_no_default_value_yields_empty_array
47
+ render_inline(UI::Accordion.new) do |accordion|
48
+ accordion.with_item(value: "x") { |i| i.with_trigger { "T" }; i.with_content { "C" } }
49
+ end
50
+ root = page.find("[data-controller~='ui--accordion']")
51
+ assert_equal "[]", root["data-ui--accordion-values-value"]
52
+ end
53
+
54
+ def test_renders_items
55
+ render_accordion
56
+ items = page.all("[data-ui--accordion-target='item']")
57
+ assert_equal 2, items.size
58
+ end
59
+
60
+ def test_root_classes
61
+ render_accordion
62
+ root = page.find("[data-controller~='ui--accordion']")
63
+ classes = root[:class].split
64
+ assert_includes classes, "w-full"
65
+ end
66
+
67
+ def test_user_class_merge
68
+ render_inline(UI::Accordion.new(class: "my-custom")) do |accordion|
69
+ accordion.with_item(value: "x") { |i| i.with_trigger { "T" }; i.with_content { "C" } }
70
+ end
71
+ classes = page.find("[data-controller~='ui--accordion']")[:class].split
72
+ assert_includes classes, "my-custom"
73
+ assert_includes classes, "w-full"
74
+ end
75
+
76
+ def test_html_attrs_passthrough
77
+ render_inline(UI::Accordion.new(data: { test_id: "acc-x" })) do |accordion|
78
+ accordion.with_item(value: "x") { |i| i.with_trigger { "T" }; i.with_content { "C" } }
79
+ end
80
+ assert_selector "[data-controller~='ui--accordion'][data-test-id='acc-x']"
81
+ end
82
+
83
+ def test_auto_generated_id
84
+ render_accordion
85
+ root = page.find("[data-controller~='ui--accordion']")
86
+ assert root[:id].present?
87
+ assert root[:id].start_with?("ui-accordion-")
88
+ end
89
+
90
+ def test_custom_id_preserved
91
+ render_inline(UI::Accordion.new(id: "my-accordion")) do |accordion|
92
+ accordion.with_item(value: "x") { |i| i.with_trigger { "T" }; i.with_content { "C" } }
93
+ end
94
+ assert_selector "[data-controller~='ui--accordion']#my-accordion"
95
+ end
96
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class UI::Alert::DescriptionTest < ViewComponent::TestCase
6
+ def setup_fixtures(*); end
7
+ def teardown_fixtures(*); end
8
+
9
+ def test_renders_div
10
+ render_inline(UI::Alert::Description.new) { "Description" }
11
+ assert_selector "div", text: "Description"
12
+ end
13
+
14
+ def test_default_classes
15
+ render_inline(UI::Alert::Description.new) { "x" }
16
+ classes = page.find("div")[:class].split
17
+ assert_includes classes, "text-sm"
18
+ assert_includes classes, "leading-relaxed"
19
+ end
20
+
21
+ def test_user_class_override
22
+ render_inline(UI::Alert::Description.new(class: "text-base")) { "Custom" }
23
+ classes = page.find("div")[:class].split
24
+ refute_includes classes, "text-sm"
25
+ assert_includes classes, "text-base"
26
+ end
27
+
28
+ def test_html_attrs_passthrough
29
+ render_inline(UI::Alert::Description.new(id: "alert-desc", data: { test: "d" })) { "y" }
30
+ assert_selector "div#alert-desc[data-test='d']"
31
+ end
32
+
33
+ def test_content_slot
34
+ render_inline(UI::Alert::Description.new) { "Full description" }
35
+ assert_equal "Full description", page.find("div").text.strip
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class UI::Alert::TitleTest < ViewComponent::TestCase
6
+ def setup_fixtures(*); end
7
+ def teardown_fixtures(*); end
8
+
9
+ def test_renders_h5
10
+ render_inline(UI::Alert::Title.new) { "Title" }
11
+ assert_selector "h5", text: "Title"
12
+ end
13
+
14
+ def test_default_classes
15
+ render_inline(UI::Alert::Title.new) { "x" }
16
+ classes = page.find("h5")[:class].split
17
+ assert_includes classes, "font-medium"
18
+ assert_includes classes, "leading-none"
19
+ assert_includes classes, "tracking-tight"
20
+ end
21
+
22
+ def test_user_class_override
23
+ render_inline(UI::Alert::Title.new(class: "font-normal")) { "Custom" }
24
+ classes = page.find("h5")[:class].split
25
+ refute_includes classes, "font-medium"
26
+ assert_includes classes, "font-normal"
27
+ end
28
+
29
+ def test_html_attrs_passthrough
30
+ render_inline(UI::Alert::Title.new(id: "alert-title", data: { test: "t" })) { "y" }
31
+ assert_selector "h5#alert-title[data-test='t']"
32
+ end
33
+
34
+ def test_content_slot
35
+ render_inline(UI::Alert::Title.new) { "My Title" }
36
+ assert_equal "My Title", page.find("h5").text.strip
37
+ end
38
+ end