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,15 @@
1
+ <%= render(UI::Sheet.new) do |sheet| %>
2
+ <% sheet.with_trigger(variant: :outline) { "Open sheet" } %>
3
+ <% sheet.with_content(side: @side) do |content| %>
4
+ <% content.with_title { "Edit profile" } %>
5
+ <% content.with_description { "Make changes to your profile here. Click save when you're done." } %>
6
+ <% content.with_footer do %>
7
+ <%= render(UI::Sheet::Close.new(variant: :outline)) { "Cancel" } %>
8
+ <%= render(UI::Button.new) { "Save changes" } %>
9
+ <% end %>
10
+ <div class="grid gap-3">
11
+ <%= render(UI::Input.new(type: :text, value: "Rain Root")) %>
12
+ <%= render(UI::Input.new(type: :email, value: "rainroot@example.com")) %>
13
+ </div>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= render(UI::Sheet.new) do |sheet| %>
2
+ <% sheet.with_trigger { "Open right sheet" } %>
3
+ <% sheet.with_content(side: :right) do |content| %>
4
+ <% content.with_title { "Notifications" } %>
5
+ <% content.with_description { "Manage your notification preferences." } %>
6
+ <div class="space-y-3 text-sm text-muted-foreground">
7
+ <p>Right sheets work well for settings and secondary tasks.</p>
8
+ <p>They reuse the dialog controller but anchor the panel to a side.</p>
9
+ </div>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%= render(UI::Sheet.new) do |sheet| %>
2
+ <% sheet.with_trigger(variant: :outline) { "Open top sheet" } %>
3
+ <% sheet.with_content(side: :top) do |content| %>
4
+ <% content.with_title { "Command palette" } %>
5
+ <% content.with_description { "Quick actions and navigation in a compact top sheet." } %>
6
+ <%= render(UI::Input.new(type: :search, placeholder: "Search commands")) %>
7
+ <% end %>
8
+ <% end %>
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::SheetPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param side select { choices: [right, left, top, bottom] }
6
+ def playground(side: :right)
7
+ @side = side.to_sym
8
+ render_with_template
9
+ end
10
+
11
+ # @label Right
12
+ def right; render_with_template; end
13
+
14
+ # @label Left
15
+ def left; render_with_template; end
16
+
17
+ # @label Top
18
+ def top; render_with_template; end
19
+
20
+ # @label Bottom
21
+ def bottom; render_with_template; end
22
+ end
@@ -0,0 +1,14 @@
1
+ <%# The VelvetKit logo for the sidebar header: the full logo while expanded,
2
+ the V mark once collapsed to icons. Collapsed, the mark drops the side
3
+ padding and sits in a box the size of a menu button, so it lines up over
4
+ the icons. The images are velvetkit.com's; in an app, put its own logo here. %>
5
+ <div class="flex h-9 min-w-0 items-center px-2 group-data-[collapsible=icon]:px-0">
6
+ <span class="group-data-[collapsible=icon]:hidden">
7
+ <img src="/velvetkit/logo-light.png" alt="VelvetKit" class="h-8 w-auto max-w-none dark:hidden">
8
+ <img src="/velvetkit/logo-dark.png" alt="VelvetKit" class="hidden h-8 w-auto max-w-none dark:block">
9
+ </span>
10
+ <span class="hidden size-8 shrink-0 items-center justify-center group-data-[collapsible=icon]:flex">
11
+ <img src="/velvetkit/mark-light.png" alt="VelvetKit" class="size-7 max-w-none shrink-0 object-contain dark:hidden">
12
+ <img src="/velvetkit/mark-dark.png" alt="VelvetKit" class="hidden size-7 max-w-none shrink-0 object-contain dark:block">
13
+ </span>
14
+ </div>
@@ -0,0 +1,60 @@
1
+ <%# Shared chrome for every UI::Sidebar preview: the framed viewport, the
2
+ wrapper that carries the Stimulus controller, the sidebar itself and the
3
+ inset content area. Each scenario supplies only what makes it different —
4
+ its nav groups, an optional brand block and its main content — so this
5
+ scaffold is defined once instead of copied into every template.
6
+
7
+ Locals:
8
+ sidebar_id: id on the <aside>; the trigger points aria-controls at it
9
+ collapsible: :none or :icon
10
+ state: :expanded or :collapsed
11
+ groups: [{ label:, items: [{ icon:, text:, active:, icon_class: }] }]
12
+ brand: captured markup for the sidebar header (optional)
13
+ title: text shown beside the trigger
14
+ main: captured markup for the content area
15
+ %>
16
+ <% sidebar_id = local_assigns.fetch(:sidebar_id, "preview-sidebar") %>
17
+ <% collapsible = local_assigns.fetch(:collapsible, :none) %>
18
+ <% state = local_assigns.fetch(:state, :expanded) %>
19
+ <% groups = local_assigns.fetch(:groups, []) %>
20
+ <% title = local_assigns.fetch(:title, "Dashboard") %>
21
+
22
+ <%# transform-gpu makes this frame a containing block, so the sidebar's
23
+ `position: fixed` inner panel resolves against the frame instead of
24
+ escaping to the browser viewport. Without it the sidebar renders outside
25
+ the preview box. %>
26
+ <div class="relative h-[600px] overflow-hidden rounded-lg border transform-gpu">
27
+ <%= render(UI::Sidebar::Wrapper.new(min_height: :full)) do %>
28
+ <%= render(UI::Sidebar.new(id: sidebar_id, collapsible: collapsible, state: state)) do |sidebar| %>
29
+ <% if local_assigns[:brand].present? %>
30
+ <% sidebar.with_sidebar_header { local_assigns[:brand] } %>
31
+ <% end %>
32
+ <% sidebar.with_sidebar_content do |content| %>
33
+ <% groups.each do |group_data| %>
34
+ <% content.with_sidebar_group do |group| %>
35
+ <% group.with_label(group_data[:label]) %>
36
+ <% group.with_menu do |menu| %>
37
+ <% Array(group_data[:items]).each do |nav| %>
38
+ <% menu.with_menu_item do |item| %>
39
+ <% item.with_menu_button("#", active: nav[:active].present?) do %>
40
+ <%= render(UI::Icon.new(name: nav[:icon], size: :sm, class: nav[:icon_class])) %>
41
+ <span><%= nav[:text] %></span>
42
+ <% end %>
43
+ <% end %>
44
+ <% end %>
45
+ <% end %>
46
+ <% end %>
47
+ <% end %>
48
+ <% end %>
49
+ <% end %>
50
+
51
+ <%= render(UI::Sidebar::Inset.new) do %>
52
+ <%# Height is fixed: the header must not resize when the sidebar collapses. %>
53
+ <header class="flex h-14 shrink-0 items-center gap-2 border-b px-4">
54
+ <%= render(UI::Sidebar::Trigger.new(sidebar_id: sidebar_id)) %>
55
+ <span class="text-sm font-medium"><%= title %></span>
56
+ </header>
57
+ <div class="p-6"><%= local_assigns[:main] %></div>
58
+ <% end %>
59
+ <% end %>
60
+ </div>
@@ -0,0 +1,55 @@
1
+ <% brand = render("ui/sidebar_preview/brand") %>
2
+
3
+ <% main = capture do %>
4
+ <div class="space-y-6">
5
+ <div>
6
+ <h2 class="text-lg font-semibold mb-2">Collapsible Sidebar</h2>
7
+ <p class="text-sm text-muted-foreground">Click the toggle button to collapse the sidebar to icon-only mode. State is persisted in localStorage.</p>
8
+ </div>
9
+ <%= render(UI::Tabs.new(default_value: "overview")) do |tabs| %>
10
+ <% tabs.with_list do |list| %>
11
+ <% list.with_trigger(value: "overview") { "Overview" } %>
12
+ <% list.with_trigger(value: "analytics") { "Analytics" } %>
13
+ <% list.with_trigger(value: "reports") { "Reports" } %>
14
+ <% end %>
15
+ <% tabs.with_panel(value: "overview") do %>
16
+ <div class="space-y-3">
17
+ <p class="text-sm text-muted-foreground">Project overview with key metrics and recent activity.</p>
18
+ <%= render(UI::Menu.new) do |menu| %>
19
+ <% menu.with_trigger(variant: :outline, size: :sm) { "Actions" } %>
20
+ <% menu.with_content do |content| %>
21
+ <% content.with_label("Options") %>
22
+ <% content.with_item("Edit project") %>
23
+ <% content.with_item("Share") %>
24
+ <% content.with_divider %>
25
+ <% content.with_item("Delete", variant: :destructive) %>
26
+ <% end %>
27
+ <% end %>
28
+ </div>
29
+ <% end %>
30
+ <% tabs.with_panel(value: "analytics") do %>
31
+ <p class="text-sm text-muted-foreground">Traffic and conversion analytics over the last 30 days.</p>
32
+ <% end %>
33
+ <% tabs.with_panel(value: "reports") do %>
34
+ <p class="text-sm text-muted-foreground">Generated reports and scheduled exports.</p>
35
+ <% end %>
36
+ <% end %>
37
+ </div>
38
+ <% end %>
39
+
40
+ <%= render "ui/sidebar_preview/shell",
41
+ sidebar_id: "preview-sidebar-collapsible",
42
+ collapsible: :icon,
43
+ brand: brand,
44
+ main: main,
45
+ groups: [
46
+ { label: "Platform", items: [
47
+ { icon: "layout-dashboard", text: "Dashboard", active: true },
48
+ { icon: "bar-chart-3", text: "Analytics" },
49
+ { icon: "folder-kanban", text: "Projects" }
50
+ ] },
51
+ { label: "Account", items: [
52
+ { icon: "user", text: "Profile" },
53
+ { icon: "credit-card", text: "Billing" }
54
+ ] }
55
+ ] %>
@@ -0,0 +1,17 @@
1
+ <% brand = render("ui/sidebar_preview/brand") %>
2
+
3
+ <% main = capture do %>
4
+ <p class="text-muted-foreground">Main content area next to the sidebar.</p>
5
+ <% end %>
6
+
7
+ <%= render "ui/sidebar_preview/shell",
8
+ sidebar_id: "preview-sidebar-default",
9
+ brand: brand,
10
+ main: main,
11
+ groups: [
12
+ { label: "Applications", items: [
13
+ { icon: "layout-dashboard", text: "Dashboard", active: true, icon_class: "stroke-orange-500" },
14
+ { icon: "users", text: "Team", icon_class: "stroke-purple-500" },
15
+ { icon: "file-text", text: "Reports" }
16
+ ] }
17
+ ] %>
@@ -0,0 +1,33 @@
1
+ <% brand = capture do %>
2
+ <%= render(UI::Menu.new) do |menu| %>
3
+ <% menu.with_trigger(variant: :ghost, class: "w-full justify-start px-2 h-auto py-1.5 group-data-[collapsible=icon]:size-8 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0") do %>
4
+ <img src="/velvetkit/mark-light.png" alt="" class="size-8 max-w-none shrink-0 object-contain dark:hidden"><img src="/velvetkit/mark-dark.png" alt="" class="hidden size-8 max-w-none shrink-0 object-contain dark:block">
5
+ <div class="grid flex-1 text-left text-sm leading-tight group-data-[collapsible=icon]:hidden">
6
+ <span class="truncate font-medium">VelvetKit</span>
7
+ <span class="truncate text-xs text-muted-foreground">Workspace</span>
8
+ </div>
9
+ <%= render(UI::Icon.new(name: "chevrons-up-down", size: :sm, class: "ml-auto group-data-[collapsible=icon]:hidden")) %>
10
+ <% end %>
11
+ <% menu.with_content(side: :right, align: :start, class: "w-56") do |content| %>
12
+ <% content.with_label("Workspaces") %>
13
+ <% content.with_item("VelvetKit") %>
14
+ <% content.with_item("Marketing site") %>
15
+ <% content.with_item("Internal tools") %>
16
+ <% end %>
17
+ <% end %>
18
+ <% end %>
19
+
20
+ <% main = capture do %>
21
+ <p class="text-muted-foreground">Sidebar with a dropdown menu inside the header for context switching.</p>
22
+ <% end %>
23
+
24
+ <%= render "ui/sidebar_preview/shell",
25
+ sidebar_id: "preview-sidebar-dropdown",
26
+ brand: brand,
27
+ main: main,
28
+ groups: [
29
+ { label: "Applications", items: [
30
+ { icon: "layout-dashboard", text: "Dashboard", active: true, icon_class: "stroke-orange-500" },
31
+ { icon: "users", text: "Team", icon_class: "stroke-purple-500" }
32
+ ] }
33
+ ] %>
@@ -0,0 +1,22 @@
1
+ <% brand = render("ui/sidebar_preview/brand") %>
2
+
3
+ <% main = capture do %>
4
+ <p class="text-muted-foreground">Two labelled groups sharing one sidebar.</p>
5
+ <% end %>
6
+
7
+ <%= render "ui/sidebar_preview/shell",
8
+ sidebar_id: "preview-sidebar-groups",
9
+ brand: brand,
10
+ main: main,
11
+ groups: [
12
+ { label: "Navigation", items: [
13
+ { icon: "home", text: "Home", active: true },
14
+ { icon: "layout-dashboard", text: "Dashboard" },
15
+ { icon: "mail", text: "Messages" }
16
+ ] },
17
+ { label: "Settings", items: [
18
+ { icon: "settings", text: "General" },
19
+ { icon: "shield", text: "Security" },
20
+ { icon: "bell", text: "Notifications" }
21
+ ] }
22
+ ] %>
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::SidebarPreview < ViewComponent::Preview
4
+ # @label Default
5
+ def default; render_with_template; end
6
+
7
+ # @label With Groups
8
+ def with_groups; render_with_template; end
9
+
10
+ # @label With Dropdown Header
11
+ def with_dropdown_header; render_with_template; end
12
+
13
+ # @label Collapsible
14
+ def collapsible; render_with_template; end
15
+ end
@@ -0,0 +1,7 @@
1
+ <div class="flex flex-wrap items-center gap-3">
2
+ <%= render(UI::Button.new(loading: true, size: :sm)) { "Saving" } %>
3
+ <%= render(UI::Button.new(loading: true)) { "Saving" } %>
4
+ <%= render(UI::Button.new(loading: true, size: :lg)) { "Saving" } %>
5
+ <%= render(UI::Button.new(loading: true, variant: :destructive)) { "Deleting" } %>
6
+ <%= render(UI::Button.new(loading: true, variant: :outline)) { "Loading" } %>
7
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="flex items-end gap-4">
2
+ <%= render(UI::Spinner.new(size: :xs)) %>
3
+ <%= render(UI::Spinner.new(size: :sm)) %>
4
+ <%= render(UI::Spinner.new(size: :default)) %>
5
+ <%= render(UI::Spinner.new(size: :lg)) %>
6
+ <%= render(UI::Spinner.new(size: :xl)) %>
7
+ </div>
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::SpinnerPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param size select { choices: [xs, sm, default, lg, xl] }
6
+ def playground(size: :default)
7
+ render(UI::Spinner.new(size: size.to_sym))
8
+ end
9
+
10
+ # @label All Sizes
11
+ def sizes
12
+ render_with_template
13
+ end
14
+
15
+ # @label In Buttons
16
+ def in_buttons
17
+ render_with_template
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ <div class="flex flex-col gap-4">
2
+ <%= render(UI::Switch.new(name: "default_off", label: "Airplane mode")) %>
3
+ <%= render(UI::Switch.new(name: "default_on", checked: true, label: "Wi-Fi")) %>
4
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="flex flex-col gap-4">
2
+ <%= render(UI::Switch.new(name: "sm", size: :sm, label: "Small")) %>
3
+ <%= render(UI::Switch.new(name: "default", size: :default, label: "Default")) %>
4
+ <%= render(UI::Switch.new(name: "lg", size: :lg, label: "Large")) %>
5
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="flex flex-col gap-4">
2
+ <%= render(UI::Switch.new(name: "unchecked", label: "Off")) %>
3
+ <%= render(UI::Switch.new(name: "checked", checked: true, label: "On")) %>
4
+ <%= render(UI::Switch.new(name: "disabled_off", disabled: true, label: "Disabled off")) %>
5
+ <%= render(UI::Switch.new(name: "disabled_on", checked: true, disabled: true, label: "Disabled on")) %>
6
+ <%= render(UI::Switch.new(name: "no_label")) %>
7
+ </div>
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::SwitchPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param label text
6
+ # @param size select { choices: [sm, default, lg] }
7
+ # @param checked toggle
8
+ # @param required toggle
9
+ # @param disabled toggle
10
+ def playground(label: "Airplane mode", size: :default,
11
+ checked: false, required: false, disabled: false)
12
+ render(UI::Switch.new(
13
+ name: "demo", label: label, size: size.to_sym,
14
+ checked: checked, required: required, disabled: disabled
15
+ ))
16
+ end
17
+
18
+ # @label Default
19
+ def default; render_with_template; end
20
+
21
+ # @label Sizes
22
+ def sizes; render_with_template; end
23
+
24
+ # @label States
25
+ def states; render_with_template; end
26
+ end
@@ -0,0 +1,15 @@
1
+ <%= render(UI::Table.new) do |table| %>
2
+ <% table.with_head do |head| %>
3
+ <% head.with_column { "Customer" } %>
4
+ <% head.with_column { "Plan" } %>
5
+ <% head.with_column(align: :end) { "Seats" } %>
6
+ <% end %>
7
+ <% [ [ "Aditi Rao", "Business", "24" ], [ "Arjun Nair", "Team", "8" ],
8
+ [ "Chitra Menon", "Free", "1" ] ].each do |name, plan, seats| %>
9
+ <% table.with_row do |row| %>
10
+ <% row.with_cell { name } %>
11
+ <% row.with_cell { plan } %>
12
+ <% row.with_cell(align: :end) { seats } %>
13
+ <% end %>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# An empty table should say so rather than show a bare header. %>
2
+ <%= render(UI::Table.new) do |table| %>
3
+ <% table.with_head do |head| %>
4
+ <% head.with_column { "Customer" } %>
5
+ <% head.with_column(align: :end) { "Amount" } %>
6
+ <% end %>
7
+ <% table.with_empty_state { "No invoices yet" } %>
8
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <%= render(UI::Table.new(caption: "Invoices this month — 2 open")) do |table| %>
2
+ <% table.with_head do |head| %>
3
+ <% head.with_column { "Invoice" } %>
4
+ <% head.with_column(align: :end) { "Amount" } %>
5
+ <% end %>
6
+ <% table.with_row(selected: true) do |row| %>
7
+ <% row.with_cell { "INV-104" } %>
8
+ <% row.with_cell(align: :end) { "₹12,400" } %>
9
+ <% end %>
10
+ <% table.with_row do |row| %>
11
+ <% row.with_cell { "INV-105" } %>
12
+ <% row.with_cell(align: :end) { "₹8,250" } %>
13
+ <% end %>
14
+ <% end %>
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::TablePreview < ViewComponent::Preview
4
+ # @label Customers
5
+ def customers; render_with_template; end
6
+
7
+ # @label Empty
8
+ def empty; render_with_template; end
9
+
10
+ # @label With Caption And Selection
11
+ def with_caption; render_with_template; end
12
+ end
@@ -0,0 +1,39 @@
1
+ <div class="max-w-lg">
2
+ <%= render(UI::Tabs.new(default_value: "account")) do |tabs| %>
3
+ <% tabs.with_list do |list| %>
4
+ <% list.with_trigger(value: "account") { "Account" } %>
5
+ <% list.with_trigger(value: "password") { "Password" } %>
6
+ <% list.with_trigger(value: "team") { "Team" } %>
7
+ <% list.with_trigger(value: "billing", disabled: true) { "Billing" } %>
8
+ <% end %>
9
+ <% tabs.with_panel(value: "account") do %>
10
+ <div class="space-y-4">
11
+ <p class="text-sm text-muted-foreground">Manage your account settings and preferences.</p>
12
+ <div class="p-4 border rounded-md">
13
+ <p class="text-sm">Account content goes here.</p>
14
+ </div>
15
+ </div>
16
+ <% end %>
17
+ <% tabs.with_panel(value: "password") do %>
18
+ <div class="space-y-4">
19
+ <p class="text-sm text-muted-foreground">Update your password and security settings.</p>
20
+ <div class="p-4 border rounded-md">
21
+ <p class="text-sm">Password content goes here.</p>
22
+ </div>
23
+ </div>
24
+ <% end %>
25
+ <% tabs.with_panel(value: "team") do %>
26
+ <div class="space-y-4">
27
+ <p class="text-sm text-muted-foreground">Manage team members and permissions.</p>
28
+ <div class="p-4 border rounded-md">
29
+ <p class="text-sm">Team content goes here.</p>
30
+ </div>
31
+ </div>
32
+ <% end %>
33
+ <% tabs.with_panel(value: "billing") do %>
34
+ <div class="space-y-4">
35
+ <p class="text-sm text-muted-foreground">View and manage billing information.</p>
36
+ </div>
37
+ <% end %>
38
+ <% end %>
39
+ </div>
@@ -0,0 +1,18 @@
1
+ <div class="max-w-lg">
2
+ <%= render(UI::Tabs.new(variant: :pill, default_value: "general")) do |tabs| %>
3
+ <% tabs.with_list do |list| %>
4
+ <% list.with_trigger(value: "general") { "General" } %>
5
+ <% list.with_trigger(value: "security") { "Security" } %>
6
+ <% list.with_trigger(value: "notifications") { "Notifications" } %>
7
+ <% end %>
8
+ <% tabs.with_panel(value: "general") do %>
9
+ <p class="text-sm text-muted-foreground">General settings for your application.</p>
10
+ <% end %>
11
+ <% tabs.with_panel(value: "security") do %>
12
+ <p class="text-sm text-muted-foreground">Security and privacy settings.</p>
13
+ <% end %>
14
+ <% tabs.with_panel(value: "notifications") do %>
15
+ <p class="text-sm text-muted-foreground">Notification preferences.</p>
16
+ <% end %>
17
+ <% end %>
18
+ </div>
@@ -0,0 +1,42 @@
1
+ <div class="max-w-lg border rounded-lg p-6">
2
+ <h2 class="text-lg font-semibold mb-4">Settings</h2>
3
+ <%= render(UI::Tabs.new(variant: :line, default_value: "account")) do |tabs| %>
4
+ <% tabs.with_list do |list| %>
5
+ <% list.with_trigger(value: "account") { "Account" } %>
6
+ <% list.with_trigger(value: "password") { "Password" } %>
7
+ <% list.with_trigger(value: "billing") { "Billing" } %>
8
+ <% end %>
9
+ <% tabs.with_panel(value: "account") do %>
10
+ <div class="space-y-4 mt-4">
11
+ <div>
12
+ <%= label_tag :name, "Name", class: "block text-sm font-medium mb-1" %>
13
+ <%= text_field_tag :name, "", class: "w-full px-3 py-2 border rounded-md text-sm" %>
14
+ </div>
15
+ <div>
16
+ <%= label_tag :email, "Email", class: "block text-sm font-medium mb-1" %>
17
+ <%= email_field_tag :email, "", class: "w-full px-3 py-2 border rounded-md text-sm" %>
18
+ </div>
19
+ <%= render(UI::Button.new) { "Save Changes" } %>
20
+ </div>
21
+ <% end %>
22
+ <% tabs.with_panel(value: "password") do %>
23
+ <div class="space-y-4 mt-4">
24
+ <div>
25
+ <%= label_tag :current, "Current Password", class: "block text-sm font-medium mb-1" %>
26
+ <%= password_field_tag :current, "", class: "w-full px-3 py-2 border rounded-md text-sm" %>
27
+ </div>
28
+ <div>
29
+ <%= label_tag :new, "New Password", class: "block text-sm font-medium mb-1" %>
30
+ <%= password_field_tag :new, "", class: "w-full px-3 py-2 border rounded-md text-sm" %>
31
+ </div>
32
+ <%= render(UI::Button.new) { "Update Password" } %>
33
+ </div>
34
+ <% end %>
35
+ <% tabs.with_panel(value: "billing") do %>
36
+ <div class="space-y-4 mt-4">
37
+ <p class="text-sm text-muted-foreground">Manage your billing information and subscription.</p>
38
+ <%= render(UI::Button.new(variant: :outline)) { "View Invoices" } %>
39
+ </div>
40
+ <% end %>
41
+ <% end %>
42
+ </div>
@@ -0,0 +1,34 @@
1
+ <div class="max-w-lg">
2
+ <%= render(UI::Tabs.new(default_value: "profile")) do |tabs| %>
3
+ <% tabs.with_list do |list| %>
4
+ <% list.with_trigger(value: "profile") do |trigger| %>
5
+ <% trigger.with_leading_icon(name: "user") %>
6
+ Profile
7
+ <% end %>
8
+ <% list.with_trigger(value: "security") do |trigger| %>
9
+ <% trigger.with_leading_icon(name: "lock") %>
10
+ Security
11
+ <% end %>
12
+ <% list.with_trigger(value: "settings") do |trigger| %>
13
+ <% trigger.with_leading_icon(name: "panel-left") %>
14
+ Settings
15
+ <% end %>
16
+ <% list.with_trigger(value: "verified", disabled: true) do |trigger| %>
17
+ <% trigger.with_leading_icon(name: "badge-check") %>
18
+ Verified
19
+ <% end %>
20
+ <% end %>
21
+ <% tabs.with_panel(value: "profile") do %>
22
+ <p class="text-sm text-muted-foreground">Your profile information.</p>
23
+ <% end %>
24
+ <% tabs.with_panel(value: "security") do %>
25
+ <p class="text-sm text-muted-foreground">Security settings.</p>
26
+ <% end %>
27
+ <% tabs.with_panel(value: "settings") do %>
28
+ <p class="text-sm text-muted-foreground">Application settings.</p>
29
+ <% end %>
30
+ <% tabs.with_panel(value: "verified") do %>
31
+ <p class="text-sm text-muted-foreground">Verification status.</p>
32
+ <% end %>
33
+ <% end %>
34
+ </div>
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::TabsPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param variant select { choices: [line, pill, solid] }
6
+ # @param size select { choices: [sm, default, lg] }
7
+ def playground(variant: :line, size: :default)
8
+ render(UI::Tabs.new(variant: variant.to_sym, size: size.to_sym, default_value: "account")) do |tabs|
9
+ tabs.with_list do |list|
10
+ list.with_trigger(value: "account") { "Account" }
11
+ list.with_trigger(value: "password") { "Password" }
12
+ list.with_trigger(value: "team") { "Team" }
13
+ list.with_trigger(value: "billing", disabled: true) { "Billing" }
14
+ end
15
+ tabs.with_panel(value: "account") { "Account settings live here." }
16
+ tabs.with_panel(value: "password") { "Password rotation lives here." }
17
+ tabs.with_panel(value: "team") { "Team membership lives here." }
18
+ tabs.with_panel(value: "billing") { "Billing details live here." }
19
+ end
20
+ end
21
+
22
+ # @label Default (Line)
23
+ def default; render_with_template; end
24
+
25
+ # @label Pill Variant
26
+ def pill_variant; render_with_template; end
27
+
28
+ # @label With Icons
29
+ def with_icons; render_with_template; end
30
+
31
+ # @label Settings Form
32
+ def settings_form; render_with_template; end
33
+ end
@@ -0,0 +1,10 @@
1
+ <div class="space-y-4">
2
+ <%= render(UI::Text.new(size: :xs)) { "Extra Small (xs) — The quick brown fox" } %>
3
+ <%= render(UI::Text.new(size: :sm)) { "Small (sm) — The quick brown fox" } %>
4
+ <%= render(UI::Text.new(size: :base)) { "Base — The quick brown fox" } %>
5
+ <%= render(UI::Text.new(size: :lg)) { "Large (lg) — The quick brown fox" } %>
6
+ <%= render(UI::Text.new(size: :xl)) { "Extra Large (xl) — The quick brown fox" } %>
7
+ <%= render(UI::Text.new(size: :"2xl")) { "2XL — The quick brown fox" } %>
8
+ <%= render(UI::Text.new(size: :"3xl")) { "3XL — The quick brown fox" } %>
9
+ <%= render(UI::Text.new(size: :"4xl")) { "4XL — The quick brown fox" } %>
10
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="space-y-4">
2
+ <%= render(UI::Text.new(tone: :default)) { "Default tone — The quick brown fox" } %>
3
+ <%= render(UI::Text.new(tone: :muted)) { "Muted tone — The quick brown fox" } %>
4
+ <%= render(UI::Text.new(tone: :destructive)) { "Destructive tone — The quick brown fox" } %>
5
+ </div>
@@ -0,0 +1,6 @@
1
+ <div class="space-y-4">
2
+ <%= render(UI::Text.new(weight: :normal)) { "Normal weight — The quick brown fox" } %>
3
+ <%= render(UI::Text.new(weight: :medium)) { "Medium weight — The quick brown fox" } %>
4
+ <%= render(UI::Text.new(weight: :semibold)) { "Semibold weight — The quick brown fox" } %>
5
+ <%= render(UI::Text.new(weight: :bold)) { "Bold weight — The quick brown fox" } %>
6
+ </div>