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,22 @@
1
+ <div class="flex flex-wrap items-center gap-3">
2
+ <%= render(UI::Button.new(variant: :default)) do |btn|
3
+ btn.with_leading_icon(name: "lock")
4
+ "Sign in"
5
+ end %>
6
+ <%= render(UI::Button.new(variant: :outline)) do |btn|
7
+ btn.with_leading_icon(name: "eye")
8
+ "Preview"
9
+ end %>
10
+ <%= render(UI::Button.new(variant: :secondary)) do |btn|
11
+ btn.with_leading_icon(name: "badge-check")
12
+ "Verified"
13
+ end %>
14
+ <%= render(UI::Button.new(variant: :destructive)) do |btn|
15
+ btn.with_leading_icon(name: "x")
16
+ "Delete"
17
+ end %>
18
+ <%= render(UI::Button.new(variant: :ghost)) do |btn|
19
+ btn.with_leading_icon(name: "user")
20
+ "Profile"
21
+ end %>
22
+ </div>
@@ -0,0 +1,14 @@
1
+ <div class="flex flex-wrap items-center gap-3">
2
+ <%= render(UI::Button.new(variant: :default)) do |btn|
3
+ btn.with_trailing_icon(name: "arrow-right")
4
+ "Continue"
5
+ end %>
6
+ <%= render(UI::Button.new(variant: :outline)) do |btn|
7
+ btn.with_trailing_icon(name: "chevrons-up-down")
8
+ "Sort"
9
+ end %>
10
+ <%= render(UI::Button.new(variant: :link)) do |btn|
11
+ btn.with_trailing_icon(name: "arrow-right")
12
+ "Read more"
13
+ end %>
14
+ </div>
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::ButtonPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param text text "Button text"
6
+ # @param variant select { choices: [default, destructive, outline, secondary, ghost, link] }
7
+ # @param size select { choices: [default, sm, lg, icon] }
8
+ # @param leading_icon select { choices: [none, lock, arrow-right, badge-check, eye, user] }
9
+ # @param trailing_icon select { choices: [none, arrow-right, chevrons-up-down, eye, x] }
10
+ # @param full_width toggle
11
+ # @param loading toggle
12
+ # @param disabled toggle
13
+ def playground(text: "Click me", variant: :default, size: :default,
14
+ leading_icon: :none, trailing_icon: :none,
15
+ full_width: false, loading: false, disabled: false)
16
+ render(UI::Button.new(
17
+ variant: variant.to_sym, size: size.to_sym,
18
+ full_width: full_width, loading: loading, disabled: disabled
19
+ )) do |btn|
20
+ btn.with_leading_icon(name: leading_icon.to_s) unless leading_icon.to_s == "none"
21
+ btn.with_trailing_icon(name: trailing_icon.to_s) unless trailing_icon.to_s == "none"
22
+ text
23
+ end
24
+ end
25
+
26
+ # @label All Variants
27
+ def variants
28
+ render_with_template
29
+ end
30
+
31
+ # @label All Sizes
32
+ def sizes
33
+ render_with_template
34
+ end
35
+
36
+ # @label With Leading Icon
37
+ def with_leading_icon
38
+ render_with_template
39
+ end
40
+
41
+ # @label With Trailing Icon
42
+ def with_trailing_icon
43
+ render_with_template
44
+ end
45
+
46
+ # @label With Both Icons
47
+ def with_both_icons
48
+ render_with_template
49
+ end
50
+
51
+ # @label Icon Only
52
+ def icon_only
53
+ render_with_template
54
+ end
55
+
56
+ # @label Loading
57
+ def loading
58
+ render_with_template
59
+ end
60
+
61
+ # @label Disabled
62
+ def disabled
63
+ render_with_template
64
+ end
65
+ end
@@ -0,0 +1,8 @@
1
+ <div class="flex flex-wrap items-center gap-3">
2
+ <%= render(UI::ButtonTo.new("#")) { "Default" } %>
3
+ <%= render(UI::ButtonTo.new("#", loading: true)) { "Loading" } %>
4
+ <%= render(UI::ButtonTo.new("#", disabled: true)) { "Disabled" } %>
5
+ </div>
6
+ <div class="mt-6 max-w-xs">
7
+ <%= render(UI::ButtonTo.new("#", variant: :outline, full_width: true, form_class: "w-full")) { "Full width" } %>
8
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="flex flex-wrap items-center gap-3">
2
+ <% %i[default destructive outline secondary ghost link].each do |v| %>
3
+ <%= render(UI::ButtonTo.new("#", variant: v)) { v.to_s.capitalize } %>
4
+ <% end %>
5
+ </div>
@@ -0,0 +1,11 @@
1
+ <%# Icon slots render here, unlike the Velvet component this replaces. %>
2
+ <div class="flex flex-wrap items-center gap-3">
3
+ <%= render(UI::ButtonTo.new("#", variant: :outline)) do |button| %>
4
+ <% button.with_leading_icon(name: "mail") %>
5
+ Sign in with email
6
+ <% end %>
7
+ <%= render(UI::ButtonTo.new("#")) do |button| %>
8
+ <% button.with_trailing_icon(name: "arrow-right") %>
9
+ Continue
10
+ <% end %>
11
+ </div>
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::ButtonToPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param variant select { choices: [default, destructive, outline, secondary, ghost, link] }
6
+ # @param size select { choices: [sm, default, lg] }
7
+ # @param full_width toggle
8
+ # @param text text
9
+ def playground(variant: :default, size: :default, full_width: false, text: "Submit")
10
+ render(UI::ButtonTo.new("#", variant: variant.to_sym, size: size.to_sym, full_width: full_width)) { text }
11
+ end
12
+
13
+ # @label Variants
14
+ def variants; render_with_template; end
15
+
16
+ # @label With Icon
17
+ def with_icon; render_with_template; end
18
+
19
+ # @label States
20
+ def states; render_with_template; end
21
+ end
@@ -0,0 +1,31 @@
1
+ <div class="max-w-sm">
2
+ <%= render(UI::Card.new) do |card| %>
3
+ <% card.with_header do |header| %>
4
+ <% header.with_title { "Team Members" } %>
5
+ <% header.with_description { "Manage your team and permissions." } %>
6
+ <% end %>
7
+ <% card.with_content do %>
8
+ <div class="space-y-3">
9
+ <div class="flex items-center gap-3">
10
+ <div class="h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary">JD</div>
11
+ <div>
12
+ <p class="text-sm font-medium">John Doe</p>
13
+ <p class="text-xs text-muted-foreground">Admin</p>
14
+ </div>
15
+ </div>
16
+ <div class="flex items-center gap-3">
17
+ <div class="h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary">AS</div>
18
+ <div>
19
+ <p class="text-sm font-medium">Alice Smith</p>
20
+ <p class="text-xs text-muted-foreground">Editor</p>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ <% end %>
25
+ <% card.with_footer do %>
26
+ <div class="flex gap-2 w-full">
27
+ <%= render(UI::Button.new(variant: :outline, size: :sm, class: "w-full")) { "Invite Member" } %>
28
+ </div>
29
+ <% end %>
30
+ <% end %>
31
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="max-w-sm">
2
+ <%= render(UI::Card.new) do |card| %>
3
+ <% card.with_content do %>
4
+ <p class="text-sm text-muted-foreground">A simple card with only body content.</p>
5
+ <% end %>
6
+ <% end %>
7
+ </div>
@@ -0,0 +1,25 @@
1
+ <div class="max-w-sm">
2
+ <%= render(UI::Card.new) do |card| %>
3
+ <% card.with_header do |header| %>
4
+ <% header.with_title { "Account Settings" } %>
5
+ <% header.with_description { "Update your account preferences." } %>
6
+ <% end %>
7
+ <% card.with_content do %>
8
+ <div class="space-y-4">
9
+ <div class="space-y-2">
10
+ <%= render(UI::Label.new(for: "name")) { "Display Name" } %>
11
+ <%= render(UI::Input.new(id: "name", placeholder: "Enter your name")) %>
12
+ </div>
13
+ <div class="space-y-2">
14
+ <%= render(UI::Label.new(for: "username")) { "Username" } %>
15
+ <%= render(UI::Input.new(id: "username", placeholder: "@username")) %>
16
+ </div>
17
+ </div>
18
+ <% end %>
19
+ <% card.with_footer do %>
20
+ <div class="flex gap-2">
21
+ <%= render(UI::Button.new(variant: :primary, size: :sm)) { "Save Changes" } %>
22
+ </div>
23
+ <% end %>
24
+ <% end %>
25
+ </div>
@@ -0,0 +1,13 @@
1
+ <div class="max-w-sm">
2
+ <%= render(UI::Card.new) do |card| %>
3
+ <% card.with_content do %>
4
+ <p class="text-sm text-muted-foreground">Card with body content and a footer containing actions.</p>
5
+ <% end %>
6
+ <% card.with_footer do %>
7
+ <div class="flex gap-2">
8
+ <%= render(UI::Button.new(variant: :primary, size: :sm)) { "Save" } %>
9
+ <%= render(UI::Button.new(variant: :outline, size: :sm)) { "Cancel" } %>
10
+ </div>
11
+ <% end %>
12
+ <% end %>
13
+ </div>
@@ -0,0 +1,11 @@
1
+ <div class="max-w-sm">
2
+ <%= render(UI::Card.new) do |card| %>
3
+ <% card.with_header do |header| %>
4
+ <% header.with_title { "Notifications" } %>
5
+ <% header.with_description { "Configure how you receive notifications." } %>
6
+ <% end %>
7
+ <% card.with_content do %>
8
+ <p class="text-sm text-muted-foreground">Card with a header containing a title and description.</p>
9
+ <% end %>
10
+ <% end %>
11
+ </div>
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::CardPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param title text
6
+ # @param description text
7
+ # @param show_footer toggle
8
+ def playground(title: "Create project", description: "Deploy your new project in one click.", show_footer: true)
9
+ render(UI::Card.new) do |card|
10
+ card.with_header do |header|
11
+ header.with_title { title }
12
+ header.with_description { description }
13
+ end
14
+ card.with_content do
15
+ tag.p(class: "text-sm text-muted-foreground") { "Card body content goes here." }
16
+ end
17
+ if show_footer
18
+ card.with_footer do
19
+ tag.div(class: "flex gap-2") do
20
+ safe_join([
21
+ render(UI::Button.new(variant: :primary, size: :sm)) { "Deploy" },
22
+ render(UI::Button.new(variant: :outline, size: :sm)) { "Cancel" }
23
+ ])
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ # @label Default (content only)
31
+ def default; render_with_template; end
32
+
33
+ # @label With Header
34
+ def with_header; render_with_template; end
35
+
36
+ # @label With Footer
37
+ def with_footer; render_with_template; end
38
+
39
+ # @label Complete (all sections)
40
+ def complete; render_with_template; end
41
+
42
+ # @label Settings Form
43
+ def settings_form; render_with_template; end
44
+ end
@@ -0,0 +1,9 @@
1
+ <form class="space-y-4 border rounded-md p-6 max-w-sm" action="#" method="post">
2
+ <fieldset class="space-y-3">
3
+ <legend class="text-sm font-medium mb-2">Notification preferences</legend>
4
+ <%= render(UI::Checkbox.new(name: "prefs[email]", checked: true, label: "Email notifications")) %>
5
+ <%= render(UI::Checkbox.new(name: "prefs[push]", label: "Push notifications")) %>
6
+ <%= render(UI::Checkbox.new(name: "prefs[sms]", disabled: true, label: "SMS (unavailable)")) %>
7
+ </fieldset>
8
+ <%= render(UI::Button.new(type: :submit)) { "Save preferences" } %>
9
+ </form>
@@ -0,0 +1,22 @@
1
+ <div class="flex flex-col gap-4">
2
+ <div class="flex items-center gap-4">
3
+ <%= render(UI::Checkbox.new(name: "unchecked")) %>
4
+ <span class="text-sm text-muted-foreground">Unchecked</span>
5
+ </div>
6
+ <div class="flex items-center gap-4">
7
+ <%= render(UI::Checkbox.new(name: "checked", checked: true)) %>
8
+ <span class="text-sm text-muted-foreground">Checked</span>
9
+ </div>
10
+ <div class="flex items-center gap-4">
11
+ <%= render(UI::Checkbox.new(name: "disabled", disabled: true)) %>
12
+ <span class="text-sm text-muted-foreground">Disabled</span>
13
+ </div>
14
+ <div class="flex items-center gap-4">
15
+ <%= render(UI::Checkbox.new(name: "disabled-checked", disabled: true, checked: true)) %>
16
+ <span class="text-sm text-muted-foreground">Disabled + Checked</span>
17
+ </div>
18
+ <div class="flex items-center gap-4">
19
+ <%= render(UI::Checkbox.new(name: "error", error: true)) %>
20
+ <span class="text-sm text-destructive">Error</span>
21
+ </div>
22
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="flex flex-col gap-4">
2
+ <%= render(UI::Checkbox.new(name: "small", label: "Small checkbox", size: :sm)) %>
3
+ <%= render(UI::Checkbox.new(name: "default", label: "Default checkbox", size: :default)) %>
4
+ <%= render(UI::Checkbox.new(name: "large", label: "Large checkbox", size: :lg)) %>
5
+ </div>
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::CheckboxPreview < 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 error toggle
10
+ # @param disabled toggle
11
+ def playground(label: "Accept terms", size: :default,
12
+ checked: false, required: false, error: false, disabled: false)
13
+ render(UI::Checkbox.new(
14
+ name: "demo", label: label, size: size.to_sym,
15
+ checked: checked, required: required, error: error, disabled: disabled
16
+ ))
17
+ end
18
+
19
+ # @label States
20
+ def states; render_with_template; end
21
+
22
+ # @label With Label
23
+ def with_label; render_with_template; end
24
+
25
+ # @label In Form
26
+ def in_form; render_with_template; end
27
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::DataGridPreview < ViewComponent::Preview
4
+ # The grid fetches its rows from the server, so a preview needs a real class
5
+ # endpoint to point at, so here it shows the markup and the reserved height.
6
+ # velvetkit.com's Data grid page has a live one.
7
+ #
8
+ # @label Playground
9
+ # @param page_size number
10
+ def playground(page_size: 15)
11
+ render(UI::DataGrid.new(url: "/customers.json", page_size: page_size.to_i)) do |grid|
12
+ grid.column(:id, "ID", width: 90, type: :number)
13
+ grid.column(:name, "Name")
14
+ grid.column(:plan, "Plan")
15
+ grid.column(:owner, "Owner", sortable: false, filterable: false)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ <div class="max-w-xs">
2
+ <%= render(UI::DatePicker.new(name: "event[date]", label: "Date of birth", value: Date.new(2012, 5, 2))) %>
3
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="flex max-w-xs flex-col gap-6">
2
+ <%= render(UI::DatePicker.new(name: "a", label: "Empty")) %>
3
+ <%= render(UI::DatePicker.new(name: "b", label: "Required", required: true)) %>
4
+ <%= render(UI::DatePicker.new(name: "c", label: "With an error", error: true, value: Date.new(2026, 10, 21))) %>
5
+ <%= render(UI::DatePicker.new(name: "d", label: "Disabled", disabled: true, value: Date.new(2026, 10, 21))) %>
6
+ <%= render(UI::DatePicker.new(name: "e", label: "Without the hint", hint: false)) %>
7
+ </div>
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::DatePickerPreview < ViewComponent::Preview
4
+ # @label Default
5
+ def default
6
+ render_with_template
7
+ end
8
+
9
+ # @label States
10
+ def states
11
+ render_with_template
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ <%= render(UI::Dialog.new) do |dialog| %>
2
+ <% dialog.with_trigger(variant: :destructive) { "Delete project" } %>
3
+ <% dialog.with_content(size: :sm) do |content| %>
4
+ <% content.with_title { "Delete this project?" } %>
5
+ <% content.with_description { "This action cannot be undone. All project data will be removed permanently." } %>
6
+ <% content.with_footer do %>
7
+ <%= render(UI::Dialog::Close.new(variant: :outline, class: "static")) { "Cancel" } %>
8
+ <%= render(UI::Button.new(variant: :destructive)) { "Delete" } %>
9
+ <% end %>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <%= render(UI::Dialog.new) do |dialog| %>
2
+ <% dialog.with_trigger(as_child: true, variant: :ghost) do %>
3
+ <a href="#dialog-demo" class="inline-flex items-center gap-2 text-sm font-medium underline underline-offset-4">
4
+ Review release notes
5
+ <%= render(UI::Icon.new(name: "arrow-right", size: :sm)) %>
6
+ </a>
7
+ <% end %>
8
+ <% dialog.with_content do |content| %>
9
+ <% content.with_title { "Release notes" } %>
10
+ <% content.with_description { "A custom trigger can adopt the dialog semantics without rendering a nested button." } %>
11
+ <div class="space-y-2 text-sm text-muted-foreground">
12
+ <p>Dialog trigger attributes are merged into the anchor element.</p>
13
+ <p>The trigger keeps its own classes while inheriting the chosen button variant tokens.</p>
14
+ </div>
15
+ <% end %>
16
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <%= render(UI::Dialog.new) do |dialog| %>
2
+ <% dialog.with_trigger(variant: :outline) { "Open dialog" } %>
3
+ <% dialog.with_content 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::Dialog::Close.new(variant: :outline, class: "static")) { "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,16 @@
1
+ <%= render(UI::Dialog.new) do |dialog| %>
2
+ <% dialog.with_trigger(variant: :secondary) { "Open policy dialog" } %>
3
+ <% dialog.with_content(size: :lg) do |content| %>
4
+ <% content.with_title { "Usage policy" } %>
5
+ <% content.with_description { "Long-form modal content should remain readable and keep actions pinned at the end." } %>
6
+ <div class="space-y-4 text-sm leading-6 text-muted-foreground">
7
+ <p>Dialogs are best reserved for focused tasks, decisions, or short forms.</p>
8
+ <p>Because the component uses the native dialog element, focus is moved into the modal and restored to the opener when it closes.</p>
9
+ <p>The content element is portaled under <code>body</code> at runtime so the backdrop and stacking context stay predictable.</p>
10
+ </div>
11
+ <% content.with_footer do %>
12
+ <%= render(UI::Dialog::Close.new(variant: :outline, class: "static")) { "Close" } %>
13
+ <%= render(UI::Button.new(variant: :secondary)) { "Acknowledge" } %>
14
+ <% end %>
15
+ <% end %>
16
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <%= render(UI::Dialog.new) do |dialog| %>
2
+ <% dialog.with_trigger(variant: @trigger_variant) { "Open dialog" } %>
3
+ <% dialog.with_content(size: @size, show_close: @show_close) 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::Dialog::Close.new(variant: :outline)) { "Cancel" } %>
8
+ <%= render(UI::Button.new) { "Save changes" } %>
9
+ <% end %>
10
+ Dialog body content lives here.
11
+ <% end %>
12
+ <% end %>
@@ -0,0 +1,29 @@
1
+ <%= render(UI::Dialog.new) do |dialog| %>
2
+ <% dialog.with_trigger(variant: :secondary) { "Open scrollable dialog" } %>
3
+ <% dialog.with_content(size: :lg) do |content| %>
4
+ <% content.with_title { "Terms of Service" } %>
5
+ <% content.with_description { "Please review the full terms before continuing." } %>
6
+ <div class="space-y-4 text-sm leading-6 text-muted-foreground">
7
+ <p><strong>1. Acceptance of Terms</strong><br>
8
+ By accessing or using our services, you agree to be bound by these Terms of Service. If you do not agree to all the terms and conditions, you may not access the service.</p>
9
+ <p><strong>2. Changes to Terms</strong><br>
10
+ We reserve the right, at our sole discretion, to modify or replace these Terms at any time. It is your responsibility to review these Terms periodically for changes.</p>
11
+ <p><strong>3. Use License</strong><br>
12
+ Permission is granted to temporarily use the materials on our website for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title.</p>
13
+ <p><strong>4. Disclaimer</strong><br>
14
+ The materials on our website are provided on an 'as is' basis. We make no warranties, expressed or implied, and hereby disclaim and negate all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.</p>
15
+ <p><strong>5. Limitations</strong><br>
16
+ In no event shall we or our suppliers be liable for any damages arising out of the use or inability to use the materials on our website, even if we or an authorized representative has been notified orally or in writing of the possibility of such damage.</p>
17
+ <p><strong>6. Accuracy of Materials</strong><br>
18
+ The materials appearing on our website could include technical, typographical, or photographic errors. We do not warrant that any of the materials on our website are accurate, complete, or current.</p>
19
+ <p><strong>7. Links</strong><br>
20
+ We have not reviewed all of the sites linked to our website and are not responsible for the contents of any such linked site. The inclusion of any link does not imply endorsement by us of the site.</p>
21
+ <p><strong>8. Governing Law</strong><br>
22
+ These terms and conditions are governed by and construed in accordance with the laws of the jurisdiction in which we operate, and you irrevocably submit to the exclusive jurisdiction of the courts in that location.</p>
23
+ </div>
24
+ <% content.with_footer do %>
25
+ <%= render(UI::Dialog::Close.new(variant: :outline, class: "static")) { "Decline" } %>
26
+ <%= render(UI::Button.new) { "Accept" } %>
27
+ <% end %>
28
+ <% end %>
29
+ <% end %>
@@ -0,0 +1,46 @@
1
+ <div class="flex flex-wrap gap-4">
2
+ <%= render(UI::Dialog.new) do |dialog| %>
3
+ <% dialog.with_trigger(variant: :outline, size: :sm) { "Open sm" } %>
4
+ <% dialog.with_content(size: :sm) do |content| %>
5
+ <% content.with_title { "Small dialog" } %>
6
+ <% content.with_description { "This is the smallest dialog size, great for confirmations." } %>
7
+ <% content.with_footer do %>
8
+ <%= render(UI::Dialog::Close.new(variant: :outline, class: "static")) { "Cancel" } %>
9
+ <%= render(UI::Button.new(size: :sm)) { "Confirm" } %>
10
+ <% end %>
11
+ <% end %>
12
+ <% end %>
13
+ <%= render(UI::Dialog.new) do |dialog| %>
14
+ <% dialog.with_trigger(variant: :outline) { "Open default" } %>
15
+ <% dialog.with_content do |content| %>
16
+ <% content.with_title { "Default dialog" } %>
17
+ <% content.with_description { "This is the default dialog size for most use cases." } %>
18
+ <% content.with_footer do %>
19
+ <%= render(UI::Dialog::Close.new(variant: :outline, class: "static")) { "Cancel" } %>
20
+ <%= render(UI::Button.new) { "Confirm" } %>
21
+ <% end %>
22
+ <% end %>
23
+ <% end %>
24
+ <%= render(UI::Dialog.new) do |dialog| %>
25
+ <% dialog.with_trigger(variant: :outline) { "Open lg" } %>
26
+ <% dialog.with_content(size: :lg) do |content| %>
27
+ <% content.with_title { "Large dialog" } %>
28
+ <% content.with_description { "This is a large dialog, useful for forms and detailed content." } %>
29
+ <% content.with_footer do %>
30
+ <%= render(UI::Dialog::Close.new(variant: :outline, class: "static")) { "Cancel" } %>
31
+ <%= render(UI::Button.new) { "Confirm" } %>
32
+ <% end %>
33
+ <% end %>
34
+ <% end %>
35
+ <%= render(UI::Dialog.new) do |dialog| %>
36
+ <% dialog.with_trigger(variant: :outline) { "Open xl" } %>
37
+ <% dialog.with_content(size: :xl) do |content| %>
38
+ <% content.with_title { "Extra large dialog" } %>
39
+ <% content.with_description { "This is the largest dialog size, ideal for complex workflows." } %>
40
+ <% content.with_footer do %>
41
+ <%= render(UI::Dialog::Close.new(variant: :outline, class: "static")) { "Cancel" } %>
42
+ <%= render(UI::Button.new) { "Confirm" } %>
43
+ <% end %>
44
+ <% end %>
45
+ <% end %>
46
+ </div>
@@ -0,0 +1,11 @@
1
+ <%= render(UI::Dialog.new) do |dialog| %>
2
+ <% dialog.with_trigger(variant: :outline) { "Open dialog" } %>
3
+ <% dialog.with_content(show_close: false) do |content| %>
4
+ <% content.with_title { "No close button" } %>
5
+ <% content.with_description { "This dialog hides the default close button. Users must interact with the footer actions to dismiss it." } %>
6
+ <% content.with_footer do %>
7
+ <%= render(UI::Dialog::Close.new(variant: :outline, class: "static")) { "Dismiss" } %>
8
+ <%= render(UI::Button.new) { "Continue" } %>
9
+ <% end %>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <%= render(UI::Dialog.new) do |dialog| %>
2
+ <% dialog.with_trigger(variant: :outline) { "Open dialog" } %>
3
+ <% dialog.with_content do %>
4
+ <div class="space-y-3 text-sm text-muted-foreground">
5
+ <p>This dialog has no title or description header.</p>
6
+ <p>Use this pattern when the content speaks for itself or when you want a cleaner, minimal modal.</p>
7
+ </div>
8
+ <div class="mt-4 flex justify-end gap-2">
9
+ <%= render(UI::Dialog::Close.new(variant: :outline, class: "static")) { "Close" } %>
10
+ <%= render(UI::Button.new) { "Okay" } %>
11
+ </div>
12
+ <% end %>
13
+ <% end %>
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UI::DialogPreview < ViewComponent::Preview
4
+ # @label Playground
5
+ # @param trigger_variant select { choices: [default, outline, secondary, ghost] }
6
+ # @param size select { choices: [sm, default, lg, xl] }
7
+ # @param show_close toggle
8
+ def playground(trigger_variant: :outline, size: :default, show_close: true)
9
+ @trigger_variant = trigger_variant.to_sym
10
+ @size = size.to_sym
11
+ @show_close = show_close
12
+ render_with_template
13
+ end
14
+
15
+ # @label Default
16
+ def default; render_with_template; end
17
+
18
+ # @label Confirmation
19
+ def confirmation; render_with_template; end
20
+
21
+ # @label Custom Trigger
22
+ def custom_trigger; render_with_template; end
23
+
24
+ # @label Long Content
25
+ def long_content; render_with_template; end
26
+
27
+ # @label Sizes
28
+ def sizes; render_with_template; end
29
+
30
+ # @label Without Close Button
31
+ def without_close_button; render_with_template; end
32
+
33
+ # @label Without Title
34
+ def without_title; render_with_template; end
35
+
36
+ # @label Scrollable Body
37
+ def scrollable_body; render_with_template; end
38
+ end
@@ -0,0 +1,5 @@
1
+ <div class="space-y-4">
2
+ <%= render(UI::Divider.new) %>
3
+ <p class="text-sm text-muted-foreground">Content between dividers</p>
4
+ <%= render(UI::Divider.new) %>
5
+ </div>