shadcn_view_component 0.3.3

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 (507) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +255 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +604 -0
  5. data/app/assets/stylesheets/shadcn-themes.css +1075 -0
  6. data/app/assets/stylesheets/shadcn.css +853 -0
  7. data/app/components/shadcn/accordion/component.rb +44 -0
  8. data/app/components/shadcn/accordion/content/component.rb +44 -0
  9. data/app/components/shadcn/accordion/item/component.rb +41 -0
  10. data/app/components/shadcn/accordion/preview.rb +16 -0
  11. data/app/components/shadcn/accordion/previews/default.html.erb +10 -0
  12. data/app/components/shadcn/accordion/previews/multiple.html.erb +13 -0
  13. data/app/components/shadcn/accordion/trigger/component.rb +52 -0
  14. data/app/components/shadcn/alert/component.rb +52 -0
  15. data/app/components/shadcn/alert/preview.rb +11 -0
  16. data/app/components/shadcn/alert/previews/default.html.erb +8 -0
  17. data/app/components/shadcn/alert.rb +17 -0
  18. data/app/components/shadcn/alert_dialog/action/component.rb +35 -0
  19. data/app/components/shadcn/alert_dialog/cancel/component.rb +16 -0
  20. data/app/components/shadcn/alert_dialog/component.rb +20 -0
  21. data/app/components/shadcn/alert_dialog/content/component.rb +55 -0
  22. data/app/components/shadcn/alert_dialog/header/component.rb +33 -0
  23. data/app/components/shadcn/alert_dialog/preview.rb +16 -0
  24. data/app/components/shadcn/alert_dialog/previews/default.html.erb +13 -0
  25. data/app/components/shadcn/alert_dialog/previews/small_with_media.html.erb +38 -0
  26. data/app/components/shadcn/alert_dialog.rb +32 -0
  27. data/app/components/shadcn/application_view_component.rb +225 -0
  28. data/app/components/shadcn/application_view_component_preview.rb +8 -0
  29. data/app/components/shadcn/aspect_ratio/component.rb +34 -0
  30. data/app/components/shadcn/aspect_ratio/preview.rb +16 -0
  31. data/app/components/shadcn/aspect_ratio/previews/default.html.erb +5 -0
  32. data/app/components/shadcn/aspect_ratio/previews/ratios.html.erb +12 -0
  33. data/app/components/shadcn/attachment/action/component.rb +19 -0
  34. data/app/components/shadcn/attachment/component.rb +76 -0
  35. data/app/components/shadcn/attachment/media/component.rb +56 -0
  36. data/app/components/shadcn/attachment/preview.rb +11 -0
  37. data/app/components/shadcn/attachment/previews/default.html.erb +80 -0
  38. data/app/components/shadcn/attachment/trigger/component.rb +31 -0
  39. data/app/components/shadcn/attachment.rb +42 -0
  40. data/app/components/shadcn/avatar/component.rb +43 -0
  41. data/app/components/shadcn/avatar/fallback/component.rb +23 -0
  42. data/app/components/shadcn/avatar/image/component.rb +20 -0
  43. data/app/components/shadcn/avatar/preview.rb +11 -0
  44. data/app/components/shadcn/avatar/previews/default.html.erb +13 -0
  45. data/app/components/shadcn/avatar.rb +32 -0
  46. data/app/components/shadcn/badge/component.rb +57 -0
  47. data/app/components/shadcn/badge/preview.rb +11 -0
  48. data/app/components/shadcn/badge/previews/default.html.erb +5 -0
  49. data/app/components/shadcn/breadcrumb/component.rb +15 -0
  50. data/app/components/shadcn/breadcrumb/ellipsis/component.rb +28 -0
  51. data/app/components/shadcn/breadcrumb/page/component.rb +25 -0
  52. data/app/components/shadcn/breadcrumb/preview.rb +16 -0
  53. data/app/components/shadcn/breadcrumb/previews/custom_separator.html.erb +17 -0
  54. data/app/components/shadcn/breadcrumb/previews/default.html.erb +15 -0
  55. data/app/components/shadcn/breadcrumb/separator/component.rb +31 -0
  56. data/app/components/shadcn/breadcrumb.rb +18 -0
  57. data/app/components/shadcn/bubble/component.rb +90 -0
  58. data/app/components/shadcn/bubble/preview.rb +16 -0
  59. data/app/components/shadcn/bubble/previews/alignment.html.erb +33 -0
  60. data/app/components/shadcn/bubble/previews/default.html.erb +22 -0
  61. data/app/components/shadcn/bubble/reactions/component.rb +54 -0
  62. data/app/components/shadcn/bubble.rb +24 -0
  63. data/app/components/shadcn/button/component.rb +85 -0
  64. data/app/components/shadcn/button/preview.rb +31 -0
  65. data/app/components/shadcn/button/previews/default.html.erb +8 -0
  66. data/app/components/shadcn/button/previews/sizes.html.erb +5 -0
  67. data/app/components/shadcn/button/previews/variants.html.erb +5 -0
  68. data/app/components/shadcn/button_group/component.rb +53 -0
  69. data/app/components/shadcn/button_group/preview.rb +15 -0
  70. data/app/components/shadcn/button_group/previews/default.html.erb +18 -0
  71. data/app/components/shadcn/button_group/previews/vertical.html.erb +5 -0
  72. data/app/components/shadcn/button_group/separator/component.rb +30 -0
  73. data/app/components/shadcn/button_group/text/component.rb +20 -0
  74. data/app/components/shadcn/calendar/component.rb +558 -0
  75. data/app/components/shadcn/calendar/month.rb +74 -0
  76. data/app/components/shadcn/calendar/preview.rb +64 -0
  77. data/app/components/shadcn/calendar/previews/booked.html.erb +18 -0
  78. data/app/components/shadcn/calendar/previews/custom_cell_size.html.erb +11 -0
  79. data/app/components/shadcn/calendar/previews/date_and_time.html.erb +31 -0
  80. data/app/components/shadcn/calendar/previews/default.html.erb +8 -0
  81. data/app/components/shadcn/calendar/previews/dropdown_caption.html.erb +11 -0
  82. data/app/components/shadcn/calendar/previews/in_a_form.html.erb +8 -0
  83. data/app/components/shadcn/calendar/previews/presets.html.erb +20 -0
  84. data/app/components/shadcn/calendar/previews/range.html.erb +10 -0
  85. data/app/components/shadcn/calendar/previews/week_numbers.html.erb +8 -0
  86. data/app/components/shadcn/card/component.rb +24 -0
  87. data/app/components/shadcn/card/header/component.rb +26 -0
  88. data/app/components/shadcn/card/preview.rb +16 -0
  89. data/app/components/shadcn/card/previews/default.html.erb +18 -0
  90. data/app/components/shadcn/card/previews/image.html.erb +37 -0
  91. data/app/components/shadcn/card.rb +21 -0
  92. data/app/components/shadcn/carousel/component.rb +61 -0
  93. data/app/components/shadcn/carousel/content/component.rb +86 -0
  94. data/app/components/shadcn/carousel/item/component.rb +42 -0
  95. data/app/components/shadcn/carousel/next/component.rb +59 -0
  96. data/app/components/shadcn/carousel/preview.rb +22 -0
  97. data/app/components/shadcn/carousel/previews/default.html.erb +22 -0
  98. data/app/components/shadcn/carousel/previews/sizes.html.erb +42 -0
  99. data/app/components/shadcn/carousel/previews/vertical.html.erb +27 -0
  100. data/app/components/shadcn/carousel/previous/component.rb +59 -0
  101. data/app/components/shadcn/chart/area/component.rb +43 -0
  102. data/app/components/shadcn/chart/bar/component.rb +75 -0
  103. data/app/components/shadcn/chart/cartesian/component.rb +152 -0
  104. data/app/components/shadcn/chart/component.rb +124 -0
  105. data/app/components/shadcn/chart/focusable.rb +38 -0
  106. data/app/components/shadcn/chart/legend/component.rb +55 -0
  107. data/app/components/shadcn/chart/line/component.rb +50 -0
  108. data/app/components/shadcn/chart/pie/component.rb +169 -0
  109. data/app/components/shadcn/chart/plot.rb +147 -0
  110. data/app/components/shadcn/chart/preview.rb +45 -0
  111. data/app/components/shadcn/chart/previews/area.html.erb +31 -0
  112. data/app/components/shadcn/chart/previews/bar.html.erb +32 -0
  113. data/app/components/shadcn/chart/previews/bar_stacked.html.erb +30 -0
  114. data/app/components/shadcn/chart/previews/default.html.erb +30 -0
  115. data/app/components/shadcn/chart/previews/donut.html.erb +16 -0
  116. data/app/components/shadcn/chart/previews/line.html.erb +30 -0
  117. data/app/components/shadcn/chart/previews/percentages.html.erb +17 -0
  118. data/app/components/shadcn/chart/table/component.rb +75 -0
  119. data/app/components/shadcn/chart/tooltip/component.rb +111 -0
  120. data/app/components/shadcn/checkbox/component.rb +109 -0
  121. data/app/components/shadcn/checkbox/preview.rb +16 -0
  122. data/app/components/shadcn/checkbox/previews/default.html.erb +14 -0
  123. data/app/components/shadcn/checkbox/previews/states.html.erb +23 -0
  124. data/app/components/shadcn/collapsible/component.rb +35 -0
  125. data/app/components/shadcn/collapsible/content/component.rb +20 -0
  126. data/app/components/shadcn/collapsible/preview.rb +11 -0
  127. data/app/components/shadcn/collapsible/previews/default.html.erb +9 -0
  128. data/app/components/shadcn/collapsible/trigger/component.rb +23 -0
  129. data/app/components/shadcn/combobox/chip/component.rb +60 -0
  130. data/app/components/shadcn/combobox/chip_input/component.rb +43 -0
  131. data/app/components/shadcn/combobox/chips/component.rb +54 -0
  132. data/app/components/shadcn/combobox/clear/component.rb +32 -0
  133. data/app/components/shadcn/combobox/component.rb +113 -0
  134. data/app/components/shadcn/combobox/content/component.rb +59 -0
  135. data/app/components/shadcn/combobox/empty/component.rb +23 -0
  136. data/app/components/shadcn/combobox/input/component.rb +85 -0
  137. data/app/components/shadcn/combobox/item/component.rb +69 -0
  138. data/app/components/shadcn/combobox/list/component.rb +27 -0
  139. data/app/components/shadcn/combobox/preview.rb +31 -0
  140. data/app/components/shadcn/combobox/previews/default.html.erb +20 -0
  141. data/app/components/shadcn/combobox/previews/grouped.html.erb +25 -0
  142. data/app/components/shadcn/combobox/previews/multiple.html.erb +28 -0
  143. data/app/components/shadcn/combobox/previews/with_clear.html.erb +16 -0
  144. data/app/components/shadcn/combobox/trigger/component.rb +54 -0
  145. data/app/components/shadcn/combobox.rb +16 -0
  146. data/app/components/shadcn/command/component.rb +67 -0
  147. data/app/components/shadcn/command/dialog/component.rb +72 -0
  148. data/app/components/shadcn/command/empty/component.rb +29 -0
  149. data/app/components/shadcn/command/group/component.rb +63 -0
  150. data/app/components/shadcn/command/input/component.rb +66 -0
  151. data/app/components/shadcn/command/item/component.rb +62 -0
  152. data/app/components/shadcn/command/list/component.rb +36 -0
  153. data/app/components/shadcn/command/preview.rb +26 -0
  154. data/app/components/shadcn/command/previews/default.html.erb +41 -0
  155. data/app/components/shadcn/command/previews/dialog.html.erb +33 -0
  156. data/app/components/shadcn/command/previews/ranking.html.erb +16 -0
  157. data/app/components/shadcn/command/separator/component.rb +33 -0
  158. data/app/components/shadcn/command.rb +14 -0
  159. data/app/components/shadcn/concerns/modal_root.rb +43 -0
  160. data/app/components/shadcn/concerns/submits_value.rb +81 -0
  161. data/app/components/shadcn/context_menu/component.rb +45 -0
  162. data/app/components/shadcn/context_menu/content/component.rb +40 -0
  163. data/app/components/shadcn/context_menu/preview.rb +16 -0
  164. data/app/components/shadcn/context_menu/previews/default.html.erb +34 -0
  165. data/app/components/shadcn/context_menu/previews/items.html.erb +35 -0
  166. data/app/components/shadcn/context_menu/sub/component.rb +47 -0
  167. data/app/components/shadcn/context_menu/sub_content/component.rb +31 -0
  168. data/app/components/shadcn/context_menu/trigger/component.rb +50 -0
  169. data/app/components/shadcn/context_menu.rb +43 -0
  170. data/app/components/shadcn/dialog/close/component.rb +20 -0
  171. data/app/components/shadcn/dialog/component.rb +20 -0
  172. data/app/components/shadcn/dialog/content/component.rb +78 -0
  173. data/app/components/shadcn/dialog/header/component.rb +23 -0
  174. data/app/components/shadcn/dialog/overlay/component.rb +28 -0
  175. data/app/components/shadcn/dialog/preview.rb +28 -0
  176. data/app/components/shadcn/dialog/previews/default.html.erb +17 -0
  177. data/app/components/shadcn/dialog/previews/inside_stacking_context.html.erb +25 -0
  178. data/app/components/shadcn/dialog/previews/no_close_button.html.erb +15 -0
  179. data/app/components/shadcn/dialog/previews/scrollable_content.html.erb +29 -0
  180. data/app/components/shadcn/dialog/trigger/component.rb +24 -0
  181. data/app/components/shadcn/dialog.rb +17 -0
  182. data/app/components/shadcn/drawer/component.rb +33 -0
  183. data/app/components/shadcn/drawer/content/component.rb +95 -0
  184. data/app/components/shadcn/drawer/header/component.rb +34 -0
  185. data/app/components/shadcn/drawer/preview.rb +29 -0
  186. data/app/components/shadcn/drawer/previews/default.html.erb +21 -0
  187. data/app/components/shadcn/drawer/previews/scrollable.html.erb +20 -0
  188. data/app/components/shadcn/drawer/previews/scrollable_side.html.erb +21 -0
  189. data/app/components/shadcn/drawer/previews/sides.html.erb +17 -0
  190. data/app/components/shadcn/drawer.rb +27 -0
  191. data/app/components/shadcn/dropdown_menu/checkbox_item/component.rb +61 -0
  192. data/app/components/shadcn/dropdown_menu/component.rb +48 -0
  193. data/app/components/shadcn/dropdown_menu/content/component.rb +49 -0
  194. data/app/components/shadcn/dropdown_menu/group/component.rb +16 -0
  195. data/app/components/shadcn/dropdown_menu/item/component.rb +50 -0
  196. data/app/components/shadcn/dropdown_menu/preview.rb +16 -0
  197. data/app/components/shadcn/dropdown_menu/previews/checkboxes_and_radio.html.erb +34 -0
  198. data/app/components/shadcn/dropdown_menu/previews/default.html.erb +23 -0
  199. data/app/components/shadcn/dropdown_menu/radio_group/component.rb +16 -0
  200. data/app/components/shadcn/dropdown_menu/radio_item/component.rb +63 -0
  201. data/app/components/shadcn/dropdown_menu/sub/component.rb +44 -0
  202. data/app/components/shadcn/dropdown_menu/sub_content/component.rb +45 -0
  203. data/app/components/shadcn/dropdown_menu/sub_trigger/component.rb +58 -0
  204. data/app/components/shadcn/dropdown_menu/trigger/component.rb +25 -0
  205. data/app/components/shadcn/dropdown_menu.rb +18 -0
  206. data/app/components/shadcn/empty/component.rb +17 -0
  207. data/app/components/shadcn/empty/media/component.rb +48 -0
  208. data/app/components/shadcn/empty/preview.rb +16 -0
  209. data/app/components/shadcn/empty/previews/default.html.erb +15 -0
  210. data/app/components/shadcn/empty/previews/media.html.erb +32 -0
  211. data/app/components/shadcn/empty.rb +26 -0
  212. data/app/components/shadcn/field/component.rb +61 -0
  213. data/app/components/shadcn/field/error/component.rb +49 -0
  214. data/app/components/shadcn/field/legend/component.rb +30 -0
  215. data/app/components/shadcn/field/preview.rb +17 -0
  216. data/app/components/shadcn/field/previews/default.html.erb +29 -0
  217. data/app/components/shadcn/field/previews/form_builder.html.erb +38 -0
  218. data/app/components/shadcn/field/separator/component.rb +35 -0
  219. data/app/components/shadcn/field.rb +44 -0
  220. data/app/components/shadcn/hover_card/component.rb +51 -0
  221. data/app/components/shadcn/hover_card/content/component.rb +45 -0
  222. data/app/components/shadcn/hover_card/preview.rb +16 -0
  223. data/app/components/shadcn/hover_card/previews/default.html.erb +36 -0
  224. data/app/components/shadcn/hover_card/previews/sides.html.erb +15 -0
  225. data/app/components/shadcn/hover_card/trigger/component.rb +33 -0
  226. data/app/components/shadcn/icon/component.rb +76 -0
  227. data/app/components/shadcn/icon.rb +43 -0
  228. data/app/components/shadcn/input/component.rb +26 -0
  229. data/app/components/shadcn/input/preview.rb +16 -0
  230. data/app/components/shadcn/input/previews/default.html.erb +5 -0
  231. data/app/components/shadcn/input/previews/states.html.erb +20 -0
  232. data/app/components/shadcn/input_group/addon/component.rb +65 -0
  233. data/app/components/shadcn/input_group/button/component.rb +40 -0
  234. data/app/components/shadcn/input_group/component.rb +43 -0
  235. data/app/components/shadcn/input_group/input/component.rb +25 -0
  236. data/app/components/shadcn/input_group/preview.rb +17 -0
  237. data/app/components/shadcn/input_group/previews/addons.html.erb +53 -0
  238. data/app/components/shadcn/input_group/previews/default.html.erb +25 -0
  239. data/app/components/shadcn/input_group/text/component.rb +20 -0
  240. data/app/components/shadcn/input_group/textarea/component.rb +21 -0
  241. data/app/components/shadcn/input_otp/component.rb +95 -0
  242. data/app/components/shadcn/input_otp/preview.rb +17 -0
  243. data/app/components/shadcn/input_otp/previews/default.html.erb +19 -0
  244. data/app/components/shadcn/input_otp/previews/variations.html.erb +33 -0
  245. data/app/components/shadcn/input_otp/separator/component.rb +21 -0
  246. data/app/components/shadcn/input_otp/slot/component.rb +60 -0
  247. data/app/components/shadcn/input_otp.rb +11 -0
  248. data/app/components/shadcn/item/component.rb +56 -0
  249. data/app/components/shadcn/item/group/component.rb +53 -0
  250. data/app/components/shadcn/item/media/component.rb +53 -0
  251. data/app/components/shadcn/item/preview.rb +20 -0
  252. data/app/components/shadcn/item/previews/default.html.erb +32 -0
  253. data/app/components/shadcn/item/previews/link.html.erb +39 -0
  254. data/app/components/shadcn/item/previews/outline.html.erb +8 -0
  255. data/app/components/shadcn/item/separator/component.rb +24 -0
  256. data/app/components/shadcn/item.rb +34 -0
  257. data/app/components/shadcn/kbd/component.rb +23 -0
  258. data/app/components/shadcn/kbd/group/component.rb +17 -0
  259. data/app/components/shadcn/kbd/preview.rb +11 -0
  260. data/app/components/shadcn/kbd/previews/default.html.erb +4 -0
  261. data/app/components/shadcn/label/component.rb +20 -0
  262. data/app/components/shadcn/label/preview.rb +11 -0
  263. data/app/components/shadcn/label/previews/default.html.erb +4 -0
  264. data/app/components/shadcn/marker/component.rb +46 -0
  265. data/app/components/shadcn/marker/icon/component.rb +23 -0
  266. data/app/components/shadcn/marker/preview.rb +17 -0
  267. data/app/components/shadcn/marker/previews/default.html.erb +18 -0
  268. data/app/components/shadcn/marker/previews/status.html.erb +32 -0
  269. data/app/components/shadcn/marker.rb +17 -0
  270. data/app/components/shadcn/menubar/component.rb +36 -0
  271. data/app/components/shadcn/menubar/content/component.rb +49 -0
  272. data/app/components/shadcn/menubar/menu/component.rb +39 -0
  273. data/app/components/shadcn/menubar/preview.rb +16 -0
  274. data/app/components/shadcn/menubar/previews/default.html.erb +52 -0
  275. data/app/components/shadcn/menubar/previews/items.html.erb +38 -0
  276. data/app/components/shadcn/menubar/sub/component.rb +43 -0
  277. data/app/components/shadcn/menubar/sub_content/component.rb +31 -0
  278. data/app/components/shadcn/menubar/sub_trigger/component.rb +31 -0
  279. data/app/components/shadcn/menubar/trigger/component.rb +41 -0
  280. data/app/components/shadcn/menubar.rb +47 -0
  281. data/app/components/shadcn/message/component.rb +33 -0
  282. data/app/components/shadcn/message/preview.rb +16 -0
  283. data/app/components/shadcn/message/previews/actions.html.erb +51 -0
  284. data/app/components/shadcn/message/previews/default.html.erb +35 -0
  285. data/app/components/shadcn/message.rb +35 -0
  286. data/app/components/shadcn/message_scroller/button/component.rb +78 -0
  287. data/app/components/shadcn/message_scroller/component.rb +59 -0
  288. data/app/components/shadcn/message_scroller/content/component.rb +50 -0
  289. data/app/components/shadcn/message_scroller/item/component.rb +45 -0
  290. data/app/components/shadcn/message_scroller/preview.rb +11 -0
  291. data/app/components/shadcn/message_scroller/previews/default.html.erb +72 -0
  292. data/app/components/shadcn/message_scroller/viewport/component.rb +41 -0
  293. data/app/components/shadcn/mode_switcher/component.rb +67 -0
  294. data/app/components/shadcn/mode_switcher/preview.rb +11 -0
  295. data/app/components/shadcn/mode_switcher/previews/default.html.erb +5 -0
  296. data/app/components/shadcn/mode_toggle/component.rb +75 -0
  297. data/app/components/shadcn/mode_toggle/preview.rb +11 -0
  298. data/app/components/shadcn/mode_toggle/previews/default.html.erb +20 -0
  299. data/app/components/shadcn/native_select/component.rb +59 -0
  300. data/app/components/shadcn/native_select/preview.rb +16 -0
  301. data/app/components/shadcn/native_select/previews/default.html.erb +9 -0
  302. data/app/components/shadcn/native_select/previews/groups.html.erb +26 -0
  303. data/app/components/shadcn/native_select.rb +16 -0
  304. data/app/components/shadcn/navigation_menu/component.rb +51 -0
  305. data/app/components/shadcn/navigation_menu/content/component.rb +58 -0
  306. data/app/components/shadcn/navigation_menu/preview.rb +11 -0
  307. data/app/components/shadcn/navigation_menu/previews/default.html.erb +49 -0
  308. data/app/components/shadcn/navigation_menu/trigger/component.rb +59 -0
  309. data/app/components/shadcn/navigation_menu.rb +28 -0
  310. data/app/components/shadcn/pagination/component.rb +19 -0
  311. data/app/components/shadcn/pagination/ellipsis/component.rb +28 -0
  312. data/app/components/shadcn/pagination/link/component.rb +37 -0
  313. data/app/components/shadcn/pagination/next/component.rb +31 -0
  314. data/app/components/shadcn/pagination/preview.rb +16 -0
  315. data/app/components/shadcn/pagination/previews/default.html.erb +18 -0
  316. data/app/components/shadcn/pagination/previews/simple.html.erb +39 -0
  317. data/app/components/shadcn/pagination/previous/component.rb +31 -0
  318. data/app/components/shadcn/pagination.rb +14 -0
  319. data/app/components/shadcn/parts.rb +38 -0
  320. data/app/components/shadcn/popover/anchor/component.rb +17 -0
  321. data/app/components/shadcn/popover/component.rb +42 -0
  322. data/app/components/shadcn/popover/content/component.rb +40 -0
  323. data/app/components/shadcn/popover/preview.rb +28 -0
  324. data/app/components/shadcn/popover/previews/align.html.erb +13 -0
  325. data/app/components/shadcn/popover/previews/default.html.erb +9 -0
  326. data/app/components/shadcn/popover/previews/inside_containing_block.html.erb +41 -0
  327. data/app/components/shadcn/popover/previews/inside_stacking_context.html.erb +30 -0
  328. data/app/components/shadcn/popover/trigger/component.rb +24 -0
  329. data/app/components/shadcn/popover.rb +16 -0
  330. data/app/components/shadcn/progress/component.rb +62 -0
  331. data/app/components/shadcn/progress/preview.rb +11 -0
  332. data/app/components/shadcn/progress/previews/default.html.erb +5 -0
  333. data/app/components/shadcn/radio_group/component.rb +51 -0
  334. data/app/components/shadcn/radio_group/item/component.rb +74 -0
  335. data/app/components/shadcn/radio_group/preview.rb +16 -0
  336. data/app/components/shadcn/radio_group/previews/default.html.erb +15 -0
  337. data/app/components/shadcn/radio_group/previews/states.html.erb +41 -0
  338. data/app/components/shadcn/resizable/component.rb +59 -0
  339. data/app/components/shadcn/resizable/handle/component.rb +70 -0
  340. data/app/components/shadcn/resizable/panel/component.rb +53 -0
  341. data/app/components/shadcn/resizable/preview.rb +23 -0
  342. data/app/components/shadcn/resizable/previews/default.html.erb +25 -0
  343. data/app/components/shadcn/resizable/previews/vertical.html.erb +19 -0
  344. data/app/components/shadcn/resizable/previews/with_handle.html.erb +17 -0
  345. data/app/components/shadcn/scroll_area/component.rb +84 -0
  346. data/app/components/shadcn/scroll_area/preview.rb +11 -0
  347. data/app/components/shadcn/scroll_area/previews/default.html.erb +36 -0
  348. data/app/components/shadcn/scroll_area/scrollbar/component.rb +72 -0
  349. data/app/components/shadcn/scroll_area/viewport/component.rb +70 -0
  350. data/app/components/shadcn/scroll_area.rb +14 -0
  351. data/app/components/shadcn/select/component.rb +67 -0
  352. data/app/components/shadcn/select/content/component.rb +157 -0
  353. data/app/components/shadcn/select/empty/component.rb +38 -0
  354. data/app/components/shadcn/select/group/component.rb +16 -0
  355. data/app/components/shadcn/select/item/component.rb +71 -0
  356. data/app/components/shadcn/select/list/component.rb +41 -0
  357. data/app/components/shadcn/select/preview.rb +24 -0
  358. data/app/components/shadcn/select/previews/default.html.erb +17 -0
  359. data/app/components/shadcn/select/previews/scrollable.html.erb +19 -0
  360. data/app/components/shadcn/select/previews/searchable.html.erb +18 -0
  361. data/app/components/shadcn/select/previews/states.html.erb +25 -0
  362. data/app/components/shadcn/select/search/component.rb +54 -0
  363. data/app/components/shadcn/select/trigger/component.rb +78 -0
  364. data/app/components/shadcn/select/value/component.rb +28 -0
  365. data/app/components/shadcn/select.rb +15 -0
  366. data/app/components/shadcn/separator/component.rb +41 -0
  367. data/app/components/shadcn/separator/preview.rb +11 -0
  368. data/app/components/shadcn/separator/previews/default.html.erb +9 -0
  369. data/app/components/shadcn/sheet/component.rb +16 -0
  370. data/app/components/shadcn/sheet/content/component.rb +98 -0
  371. data/app/components/shadcn/sheet/header/component.rb +23 -0
  372. data/app/components/shadcn/sheet/preview.rb +16 -0
  373. data/app/components/shadcn/sheet/previews/default.html.erb +16 -0
  374. data/app/components/shadcn/sheet/previews/no_close_button.html.erb +14 -0
  375. data/app/components/shadcn/sheet.rb +22 -0
  376. data/app/components/shadcn/sidebar/component.rb +232 -0
  377. data/app/components/shadcn/sidebar/input/component.rb +25 -0
  378. data/app/components/shadcn/sidebar/menu_action/component.rb +58 -0
  379. data/app/components/shadcn/sidebar/menu_button/component.rb +100 -0
  380. data/app/components/shadcn/sidebar/menu_skeleton/component.rb +51 -0
  381. data/app/components/shadcn/sidebar/menu_sub_button/component.rb +60 -0
  382. data/app/components/shadcn/sidebar/preview.rb +22 -0
  383. data/app/components/shadcn/sidebar/previews/default.html.erb +189 -0
  384. data/app/components/shadcn/sidebar/previews/floating.html.erb +39 -0
  385. data/app/components/shadcn/sidebar/previews/inset.html.erb +48 -0
  386. data/app/components/shadcn/sidebar/provider/component.rb +53 -0
  387. data/app/components/shadcn/sidebar/rail/component.rb +43 -0
  388. data/app/components/shadcn/sidebar/separator/component.rb +23 -0
  389. data/app/components/shadcn/sidebar/trigger/component.rb +39 -0
  390. data/app/components/shadcn/sidebar.rb +98 -0
  391. data/app/components/shadcn/skeleton/component.rb +14 -0
  392. data/app/components/shadcn/skeleton/preview.rb +11 -0
  393. data/app/components/shadcn/skeleton/previews/default.html.erb +7 -0
  394. data/app/components/shadcn/slider/component.rb +145 -0
  395. data/app/components/shadcn/slider/preview.rb +16 -0
  396. data/app/components/shadcn/slider/previews/default.html.erb +17 -0
  397. data/app/components/shadcn/slider/previews/vertical.html.erb +7 -0
  398. data/app/components/shadcn/slider/range/component.rb +51 -0
  399. data/app/components/shadcn/slider/thumb/component.rb +58 -0
  400. data/app/components/shadcn/slider/track/component.rb +32 -0
  401. data/app/components/shadcn/spinner/component.rb +21 -0
  402. data/app/components/shadcn/spinner/preview.rb +11 -0
  403. data/app/components/shadcn/spinner/previews/default.html.erb +7 -0
  404. data/app/components/shadcn/switch/component.rb +104 -0
  405. data/app/components/shadcn/switch/preview.rb +11 -0
  406. data/app/components/shadcn/switch/previews/default.html.erb +24 -0
  407. data/app/components/shadcn/table/component.rb +26 -0
  408. data/app/components/shadcn/table/preview.rb +16 -0
  409. data/app/components/shadcn/table/previews/default.html.erb +19 -0
  410. data/app/components/shadcn/table/previews/footer.html.erb +25 -0
  411. data/app/components/shadcn/table.rb +33 -0
  412. data/app/components/shadcn/tabs/component.rb +40 -0
  413. data/app/components/shadcn/tabs/content/component.rb +34 -0
  414. data/app/components/shadcn/tabs/list/component.rb +51 -0
  415. data/app/components/shadcn/tabs/preview.rb +16 -0
  416. data/app/components/shadcn/tabs/previews/default.html.erb +21 -0
  417. data/app/components/shadcn/tabs/previews/line.html.erb +23 -0
  418. data/app/components/shadcn/tabs/trigger/component.rb +66 -0
  419. data/app/components/shadcn/textarea/component.rb +24 -0
  420. data/app/components/shadcn/textarea/preview.rb +16 -0
  421. data/app/components/shadcn/textarea/previews/default.html.erb +3 -0
  422. data/app/components/shadcn/textarea/previews/states.html.erb +18 -0
  423. data/app/components/shadcn/theme_selector/component.rb +88 -0
  424. data/app/components/shadcn/theme_selector/preview.rb +17 -0
  425. data/app/components/shadcn/theme_selector/previews/all_themes.html.erb +20 -0
  426. data/app/components/shadcn/theme_selector/previews/default.html.erb +18 -0
  427. data/app/components/shadcn/toaster/component.rb +139 -0
  428. data/app/components/shadcn/toaster/preview.rb +16 -0
  429. data/app/components/shadcn/toaster/previews/default.html.erb +19 -0
  430. data/app/components/shadcn/toaster/previews/from_the_server.html.erb +15 -0
  431. data/app/components/shadcn/toaster/toast/component.rb +134 -0
  432. data/app/components/shadcn/toggle/component.rb +81 -0
  433. data/app/components/shadcn/toggle/preview.rb +11 -0
  434. data/app/components/shadcn/toggle/previews/default.html.erb +6 -0
  435. data/app/components/shadcn/toggle_group/component.rb +62 -0
  436. data/app/components/shadcn/toggle_group/item/component.rb +59 -0
  437. data/app/components/shadcn/toggle_group/preview.rb +16 -0
  438. data/app/components/shadcn/toggle_group/previews/default.html.erb +12 -0
  439. data/app/components/shadcn/toggle_group/previews/sizes.html.erb +37 -0
  440. data/app/components/shadcn/tooltip/component.rb +38 -0
  441. data/app/components/shadcn/tooltip/content/component.rb +62 -0
  442. data/app/components/shadcn/tooltip/preview.rb +21 -0
  443. data/app/components/shadcn/tooltip/previews/default.html.erb +4 -0
  444. data/app/components/shadcn/tooltip/previews/sides.html.erb +19 -0
  445. data/app/components/shadcn/tooltip/previews/with_keyboard_shortcut.html.erb +11 -0
  446. data/app/components/shadcn/tooltip/provider/component.rb +28 -0
  447. data/app/components/shadcn/tooltip/trigger/component.rb +25 -0
  448. data/app/helpers/shadcn_view_component/form_helper.rb +25 -0
  449. data/app/helpers/shadcn_view_component/theme_helper.rb +61 -0
  450. data/app/javascript/shadcn/animation.js +180 -0
  451. data/app/javascript/shadcn/command_score.js +110 -0
  452. data/app/javascript/shadcn/controllers/accordion_controller.js +127 -0
  453. data/app/javascript/shadcn/controllers/avatar_controller.js +32 -0
  454. data/app/javascript/shadcn/controllers/calendar_controller.js +633 -0
  455. data/app/javascript/shadcn/controllers/carousel_controller.js +139 -0
  456. data/app/javascript/shadcn/controllers/chart_controller.js +110 -0
  457. data/app/javascript/shadcn/controllers/checkbox_controller.js +61 -0
  458. data/app/javascript/shadcn/controllers/collapsible_controller.js +55 -0
  459. data/app/javascript/shadcn/controllers/combobox_controller.js +360 -0
  460. data/app/javascript/shadcn/controllers/command_controller.js +199 -0
  461. data/app/javascript/shadcn/controllers/dialog_controller.js +154 -0
  462. data/app/javascript/shadcn/controllers/drawer_controller.js +265 -0
  463. data/app/javascript/shadcn/controllers/dropdown_menu_controller.js +259 -0
  464. data/app/javascript/shadcn/controllers/hover_card_controller.js +85 -0
  465. data/app/javascript/shadcn/controllers/input_group_controller.js +17 -0
  466. data/app/javascript/shadcn/controllers/input_otp_controller.js +80 -0
  467. data/app/javascript/shadcn/controllers/menubar_controller.js +151 -0
  468. data/app/javascript/shadcn/controllers/message_scroller_controller.js +456 -0
  469. data/app/javascript/shadcn/controllers/navigation_menu_controller.js +211 -0
  470. data/app/javascript/shadcn/controllers/popover_controller.js +66 -0
  471. data/app/javascript/shadcn/controllers/radio_group_controller.js +69 -0
  472. data/app/javascript/shadcn/controllers/resizable_controller.js +167 -0
  473. data/app/javascript/shadcn/controllers/scroll_area_controller.js +253 -0
  474. data/app/javascript/shadcn/controllers/select_controller.js +320 -0
  475. data/app/javascript/shadcn/controllers/sidebar_controller.js +273 -0
  476. data/app/javascript/shadcn/controllers/slider_controller.js +230 -0
  477. data/app/javascript/shadcn/controllers/switch_controller.js +39 -0
  478. data/app/javascript/shadcn/controllers/tabs_controller.js +81 -0
  479. data/app/javascript/shadcn/controllers/theme_controller.js +67 -0
  480. data/app/javascript/shadcn/controllers/toaster_controller.js +281 -0
  481. data/app/javascript/shadcn/controllers/toggle_controller.js +24 -0
  482. data/app/javascript/shadcn/controllers/toggle_group_controller.js +73 -0
  483. data/app/javascript/shadcn/controllers/tooltip_controller.js +58 -0
  484. data/app/javascript/shadcn/direction.js +41 -0
  485. data/app/javascript/shadcn/dismiss.js +73 -0
  486. data/app/javascript/shadcn/floating.js +207 -0
  487. data/app/javascript/shadcn/focus.js +86 -0
  488. data/app/javascript/shadcn/id.js +14 -0
  489. data/app/javascript/shadcn/index.js +110 -0
  490. data/app/javascript/shadcn/popper.js +262 -0
  491. data/app/javascript/shadcn/scroll_geometry.js +267 -0
  492. data/app/javascript/shadcn/theme.js +161 -0
  493. data/app/javascript/shadcn/top_layer.js +59 -0
  494. data/app/javascript/shadcn/typeahead.js +99 -0
  495. data/config/importmap.rb +6 -0
  496. data/config/locales/en.yml +62 -0
  497. data/lib/generators/shadcn_view_component/install/install_generator.rb +149 -0
  498. data/lib/shadcn_view_component/engine.rb +73 -0
  499. data/lib/shadcn_view_component/form_builder.rb +264 -0
  500. data/lib/shadcn_view_component/icon_registry.rb +66 -0
  501. data/lib/shadcn_view_component/icons.rb +32 -0
  502. data/lib/shadcn_view_component/themes.rb +1065 -0
  503. data/lib/shadcn_view_component/version.rb +3 -0
  504. data/lib/shadcn_view_component.rb +82 -0
  505. data/lib/tasks/icons.rake +53 -0
  506. data/lib/tasks/themes.rake +171 -0
  507. metadata +608 -0
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module DropdownMenu
5
+ module RadioItem
6
+ # DropdownMenuRadioItem
7
+ class Component < ApplicationViewComponent
8
+ INDICATOR_CLASSES = "pointer-events-none absolute left-2 flex size-3.5 " \
9
+ "items-center justify-center"
10
+
11
+ slot_name :"dropdown-menu-radio-item"
12
+
13
+ style do
14
+ base {
15
+ "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 " \
16
+ "text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground " \
17
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50 " \
18
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
19
+ }
20
+ end
21
+
22
+ attr_reader :value, :checked, :disabled
23
+
24
+ def initialize(value:, checked: false, disabled: false, **attributes)
25
+ @value = value.to_s
26
+ @checked = checked
27
+ @disabled = disabled
28
+ super(**attributes)
29
+ end
30
+
31
+ def element_attributes(**defaults)
32
+ super(**{
33
+ role: "menuitemradio",
34
+ tabindex: "-1",
35
+ "aria-checked" => checked.to_s,
36
+ "data-value" => value,
37
+ "data-state" => (checked ? "checked" : "unchecked"),
38
+ "data-disabled" => (true if disabled),
39
+ "data-shadcn--dropdown-menu-target" => "item",
40
+ "data-action" => "click->shadcn--dropdown-menu#select " \
41
+ "pointerenter->shadcn--dropdown-menu#pointerenter"
42
+ }.merge(defaults))
43
+ end
44
+
45
+ def call
46
+ render_element(body: safe_join([ indicator, content ]))
47
+ end
48
+
49
+ private
50
+
51
+ def indicator
52
+ tag.span(class: INDICATOR_CLASSES) do
53
+ tag.span(
54
+ render(Icon::Component.new("circle", class: "size-2 fill-current")),
55
+ "data-slot": "dropdown-menu-radio-item-indicator",
56
+ hidden: (true unless checked)
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module DropdownMenu
5
+ module Sub
6
+ # DropdownMenuSub — a nested menu.
7
+ #
8
+ # It is another `shadcn--dropdown-menu` controller, anchored to the right
9
+ # of its trigger. Stimulus scopes targets to the nearest controller of the
10
+ # same identifier, so the parent menu's items and the submenu's stay apart.
11
+ class Component < ApplicationViewComponent
12
+ renders_one :trigger, "Shadcn::DropdownMenu::SubTrigger::Component"
13
+ renders_one :menu_content, "Shadcn::DropdownMenu::SubContent::Component"
14
+
15
+ slot_name :"dropdown-menu-sub"
16
+
17
+ attr_reader :open, :side, :align, :side_offset
18
+
19
+ def initialize(open: false, side: :right, align: :start, side_offset: 0, **attributes)
20
+ @open = open
21
+ @side = side&.to_sym || :right
22
+ @align = align&.to_sym || :start
23
+ @side_offset = side_offset
24
+ super(**attributes)
25
+ end
26
+
27
+ def element_attributes(**defaults)
28
+ super(**{
29
+ style: merged_style(CONTENTS_STYLE),
30
+ "data-controller" => "shadcn--dropdown-menu",
31
+ "data-shadcn--dropdown-menu-open-value" => open,
32
+ "data-shadcn--dropdown-menu-side-value" => side,
33
+ "data-shadcn--dropdown-menu-align-value" => align,
34
+ "data-shadcn--dropdown-menu-side-offset-value" => side_offset
35
+ }.merge(defaults))
36
+ end
37
+
38
+ def call
39
+ render_element(body: safe_join([ trigger, menu_content, content ].compact))
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module DropdownMenu
5
+ module SubContent
6
+ # DropdownMenuSubContent
7
+ class Component < ApplicationViewComponent
8
+ renders_many :items, "Shadcn::DropdownMenu::Item::Component"
9
+
10
+ slot_name :"dropdown-menu-sub-content"
11
+
12
+ style do
13
+ base {
14
+ "z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) " \
15
+ "overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground " \
16
+ "shadow-lg data-[side=bottom]:slide-in-from-top-2 " \
17
+ "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 " \
18
+ "data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out " \
19
+ "data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 " \
20
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 " \
21
+ "data-[state=open]:zoom-in-95"
22
+ }
23
+ end
24
+
25
+ def element_attributes(**defaults)
26
+ super(**{
27
+ role: "menu",
28
+ tabindex: "-1",
29
+ "aria-orientation" => "vertical",
30
+ "data-state" => "closed",
31
+ hidden: true,
32
+ "data-shadcn--dropdown-menu-target" => "content",
33
+ "data-action" => "keydown->shadcn--dropdown-menu#contentKeydown " \
34
+ "pointerenter->shadcn--dropdown-menu#cancelSubTimers " \
35
+ "pointerleave->shadcn--dropdown-menu#closeLater"
36
+ }.merge(defaults))
37
+ end
38
+
39
+ def call
40
+ render_element(body: safe_join([ items, content ].flatten.compact))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module DropdownMenu
5
+ module SubTrigger
6
+ # DropdownMenuSubTrigger — a menu item that opens the nested menu.
7
+ class Component < ApplicationViewComponent
8
+ slot_name :"dropdown-menu-sub-trigger"
9
+
10
+ style do
11
+ base {
12
+ "flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm " \
13
+ "outline-hidden select-none focus:bg-accent focus:text-accent-foreground " \
14
+ "data-[inset]:pl-8 data-[state=open]:bg-accent " \
15
+ "data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none " \
16
+ "[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 " \
17
+ "[&_svg:not([class*='text-'])]:text-muted-foreground"
18
+ }
19
+ end
20
+
21
+ attr_reader :inset, :disabled
22
+
23
+ def initialize(inset: false, disabled: false, **attributes)
24
+ @inset = inset
25
+ @disabled = disabled
26
+ super(**attributes)
27
+ end
28
+
29
+ def element_attributes(**defaults)
30
+ super(**{
31
+ role: "menuitem",
32
+ tabindex: "-1",
33
+ "aria-haspopup" => "menu",
34
+ "aria-expanded" => "false",
35
+ "data-state" => "closed",
36
+ "data-inset" => (true if inset),
37
+ "data-disabled" => (true if disabled),
38
+ "data-shadcn--dropdown-menu-target" => "trigger",
39
+ "data-action" => "click->shadcn--dropdown-menu#toggle " \
40
+ "keydown->shadcn--dropdown-menu#triggerKeydown " \
41
+ "pointerenter->shadcn--dropdown-menu#open " \
42
+ "pointerleave->shadcn--dropdown-menu#closeLater"
43
+ }.merge(defaults))
44
+ end
45
+
46
+ def call
47
+ render_element(body: safe_join([ content, chevron ]))
48
+ end
49
+
50
+ private
51
+
52
+ def chevron
53
+ render(Icon::Component.new("chevron-right", class: "ml-auto size-4"))
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module DropdownMenu
5
+ module Trigger
6
+ # DropdownMenuTrigger
7
+ class Component < ApplicationViewComponent
8
+ default_tag :button
9
+ slot_name :"dropdown-menu-trigger"
10
+
11
+ def element_attributes(**defaults)
12
+ super(**{
13
+ type: "button",
14
+ "aria-haspopup" => "menu",
15
+ "aria-expanded" => "false",
16
+ "data-state" => "closed",
17
+ "data-shadcn--dropdown-menu-target" => "trigger",
18
+ "data-action" => "click->shadcn--dropdown-menu#toggle " \
19
+ "keydown->shadcn--dropdown-menu#triggerKeydown"
20
+ }.merge(defaults))
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ # The parts of the DropdownMenu family that are just an element with a
5
+ # `data-slot` and a fixed set of classes. Anything with variants, slots or
6
+ # markup of its own keeps its own `component.rb`.
7
+ module DropdownMenu
8
+ extend Parts
9
+
10
+ part :label, slot: "dropdown-menu-label",
11
+ classes: "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8"
12
+
13
+ part :separator, slot: "dropdown-menu-separator", classes: "-mx-1 my-1 h-px bg-border"
14
+
15
+ part :shortcut, tag: :span, slot: "dropdown-menu-shortcut",
16
+ classes: "ml-auto text-xs tracking-widest text-muted-foreground"
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Empty
5
+ # Port of registry/new-york-v4/ui/empty.tsx
6
+ class Component < ApplicationViewComponent
7
+ slot_name :empty
8
+
9
+ style do
10
+ base {
11
+ "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg " \
12
+ "border-dashed p-6 text-center text-balance md:p-12"
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Empty
5
+ module Media
6
+ # EmptyMedia. Named after the TSX function, but the `data-slot` it emits
7
+ # is `empty-icon` — the two differ upstream, and the slot is what the
8
+ # sibling selectors key on.
9
+ class Component < ApplicationViewComponent
10
+ slot_name :"empty-icon"
11
+
12
+ style do
13
+ base {
14
+ "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none " \
15
+ "[&_svg]:shrink-0"
16
+ }
17
+
18
+ variants {
19
+ variant {
20
+ default { "bg-transparent" }
21
+ icon {
22
+ "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted " \
23
+ "text-foreground [&_svg:not([class*='size-'])]:size-6"
24
+ }
25
+ }
26
+ }
27
+
28
+ defaults { { variant: :default } }
29
+ end
30
+
31
+ attr_reader :variant
32
+
33
+ def initialize(variant: :default, **attributes)
34
+ @variant = variant&.to_sym || :default
35
+ super(**attributes)
36
+ end
37
+
38
+ def style_variants
39
+ { variant: }
40
+ end
41
+
42
+ def element_attributes(**defaults)
43
+ super(**{ "data-variant" => variant }.merge(defaults))
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Empty
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+
10
+ # Upstream's "Avatar" and "Avatar Group" — the media slot's other variant.
11
+ def media
12
+ render_with_template
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ <%= render(Shadcn::Empty::Component.new(class: "w-full max-w-md border")) do %>
2
+ <%= render(Shadcn::Empty::Header::Component.new) do %>
3
+ <%= render(Shadcn::Empty::Media::Component.new(variant: :icon)) do %>
4
+ <%= render(Shadcn::Icon::Component.new("circle")) %>
5
+ <% end %>
6
+ <%= render(Shadcn::Empty::Title::Component.new) { "No projects yet" } %>
7
+ <%= render(Shadcn::Empty::Description::Component.new) do %>
8
+ Create your first project to get started, or
9
+ <a href="#">read the guide</a>.
10
+ <% end %>
11
+ <% end %>
12
+ <%= render(Shadcn::Empty::Content::Component.new) do %>
13
+ <%= render(Shadcn::Button::Component.new) { "New project" } %>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <%# Upstream's "Avatar" and "Avatar Group": the media slot's *other* variant.
2
+ `icon` — the one in the default preview — draws the muted rounded box; the
3
+ default variant draws nothing at all and lets whatever is inside stand on
4
+ its own, which is what a face or a stack of faces needs. %>
5
+ <div class="flex flex-col gap-8">
6
+ <%= render(Shadcn::Empty::Component.new(class: "w-full max-w-md border")) do %>
7
+ <%= render(Shadcn::Empty::Header::Component.new) do %>
8
+ <%= render(Shadcn::Empty::Media::Component.new) do %>
9
+ <%= render(Shadcn::Avatar::Component.new(size: :lg)) do |a| %>
10
+ <% a.with_fallback { "EW" } %>
11
+ <% end %>
12
+ <% end %>
13
+ <%= render(Shadcn::Empty::Title::Component.new) { "No messages from Evelyn" } %>
14
+ <%= render(Shadcn::Empty::Description::Component.new) { "When she writes, it will be here." } %>
15
+ <% end %>
16
+ <% end %>
17
+
18
+ <%= render(Shadcn::Empty::Component.new(class: "w-full max-w-md border")) do %>
19
+ <%= render(Shadcn::Empty::Header::Component.new) do %>
20
+ <%= render(Shadcn::Empty::Media::Component.new) do %>
21
+ <%= render(Shadcn::Avatar::Group::Component.new) do %>
22
+ <% %w[AB CD EF].each do |initials| %>
23
+ <%= render(Shadcn::Avatar::Component.new) do |a| %><% a.with_fallback { initials } %><% end %>
24
+ <% end %>
25
+ <%= render(Shadcn::Avatar::GroupCount::Component.new) { "+2" } %>
26
+ <% end %>
27
+ <% end %>
28
+ <%= render(Shadcn::Empty::Title::Component.new) { "Nobody has joined yet" } %>
29
+ <%= render(Shadcn::Empty::Description::Component.new) { "Invite the five people above." } %>
30
+ <% end %>
31
+ <% end %>
32
+ </div>
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ # The parts of the Empty family that are just an element with a `data-slot`
5
+ # and a fixed set of classes. `EmptyMedia` has a variant, so it keeps its own
6
+ # `component.rb`.
7
+ #
8
+ # `EmptyDescription` is typed `React.ComponentProps<"p">` upstream but renders
9
+ # a `<div>`. The element is what the port follows.
10
+ module Empty
11
+ extend Parts
12
+
13
+ part :header, slot: "empty-header",
14
+ classes: "flex max-w-sm flex-col items-center gap-2 text-center"
15
+
16
+ part :title, slot: "empty-title", classes: "text-lg font-medium tracking-tight"
17
+
18
+ part :description, slot: "empty-description",
19
+ classes: "text-sm/relaxed text-muted-foreground [&>a]:underline " \
20
+ "[&>a]:underline-offset-4 [&>a:hover]:text-primary"
21
+
22
+ part :content, slot: "empty-content",
23
+ classes: "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 " \
24
+ "text-sm text-balance"
25
+ end
26
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Field
5
+ # Port of registry/new-york-v4/ui/field.tsx
6
+ class Component < ApplicationViewComponent
7
+ renders_one :label, "Shadcn::Field::Label::Component"
8
+ renders_one :field_content, "Shadcn::Field::Content::Component"
9
+ renders_one :description, "Shadcn::Field::Description::Component"
10
+ renders_one :error, "Shadcn::Field::Error::Component"
11
+
12
+ slot_name :field
13
+
14
+ style do
15
+ base { "group/field flex w-full gap-3 data-[invalid=true]:text-destructive" }
16
+
17
+ variants {
18
+ orientation {
19
+ vertical { "flex-col [&>*]:w-full [&>.sr-only]:w-auto" }
20
+ horizontal {
21
+ "flex-row items-center " \
22
+ "[&>[data-slot=field-label]]:flex-auto " \
23
+ "has-[>[data-slot=field-content]]:items-start " \
24
+ "has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
25
+ }
26
+ responsive {
27
+ # Class names are never split across line continuations: Tailwind
28
+ # scans the source text, so half a token would generate no CSS.
29
+ "flex-col @md/field-group:flex-row @md/field-group:items-center " \
30
+ "[&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto " \
31
+ "@md/field-group:[&>[data-slot=field-label]]:flex-auto " \
32
+ "@md/field-group:has-[>[data-slot=field-content]]:items-start " \
33
+ "@md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
34
+ }
35
+ }
36
+ }
37
+
38
+ defaults { { orientation: :vertical } }
39
+ end
40
+
41
+ attr_reader :orientation
42
+
43
+ def initialize(orientation: :vertical, **attributes)
44
+ @orientation = orientation&.to_sym || :vertical
45
+ super(**attributes)
46
+ end
47
+
48
+ def style_variants
49
+ { orientation: }
50
+ end
51
+
52
+ def element_attributes(**defaults)
53
+ super(**{ role: "group", "data-orientation" => orientation }.merge(defaults))
54
+ end
55
+
56
+ def call
57
+ render_element(body: safe_join([ label, field_content, content, description, error ].compact))
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Field
5
+ module Error
6
+ # FieldError — renders nothing when there is neither content nor errors.
7
+ # A single error is inlined; several are listed, like the TSX does.
8
+ class Component < ApplicationViewComponent
9
+ LIST_CLASSES = "ml-4 flex list-disc flex-col gap-1"
10
+
11
+ slot_name :"field-error"
12
+
13
+ style do
14
+ base { "text-sm font-normal text-destructive" }
15
+ end
16
+
17
+ attr_reader :errors
18
+
19
+ def initialize(errors: [], **attributes)
20
+ @errors = Array(errors).compact.map(&:to_s).uniq
21
+ super(**attributes)
22
+ end
23
+
24
+ def render?
25
+ content.present? || errors.any?
26
+ end
27
+
28
+ def element_attributes(**defaults)
29
+ super(**{ role: "alert" }.merge(defaults))
30
+ end
31
+
32
+ def call
33
+ render_element(body: body)
34
+ end
35
+
36
+ private
37
+
38
+ def body
39
+ return content if content.present?
40
+ return errors.first if errors.one?
41
+
42
+ tag.ul(class: LIST_CLASSES) do
43
+ safe_join(errors.map { |message| tag.li(message) })
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Field
5
+ module Legend
6
+ # FieldLegend
7
+ class Component < ApplicationViewComponent
8
+ default_tag :legend
9
+ slot_name :"field-legend"
10
+
11
+ style do
12
+ base {
13
+ "mb-3 font-medium data-[variant=legend]:text-base data-[variant=label]:text-sm"
14
+ }
15
+ end
16
+
17
+ attr_reader :variant
18
+
19
+ def initialize(variant: :legend, **attributes)
20
+ @variant = variant&.to_sym || :legend
21
+ super(**attributes)
22
+ end
23
+
24
+ def element_attributes(**defaults)
25
+ super(**{ "data-variant" => variant }.merge(defaults))
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Field
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+
10
+ # The same components driven by `shadcn_form_with`, so ids, names, error
11
+ # text and the ARIA wiring come from the model.
12
+ def form_builder
13
+ render_with_template
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ <%= render(Shadcn::Field::Group::Component.new(class: "w-[420px]")) do %>
2
+ <%= render(Shadcn::Field::Component.new) do |f| %>
3
+ <% f.with_label(for: "field-email") { "Email" } %>
4
+ <% f.with_field_content do %>
5
+ <%= render(Shadcn::Input::Component.new(id: "field-email", type: "email", placeholder: "m@example.com")) %>
6
+ <% end %>
7
+ <% f.with_description { "We will never share your email." } %>
8
+ <% end %>
9
+ <%= render(Shadcn::Field::Separator::Component.new) { "Or" } %>
10
+ <%= render(Shadcn::Field::Component.new(orientation: :horizontal)) do |f| %>
11
+ <%# The Switch is a `<button role="switch">`, and a `<label for>` names it:
12
+ button is a labelable element, and the accessibility tree agrees —
13
+ measured, after this comment claimed the opposite. %>
14
+ <% f.with_label(for: "field-news") { "Subscribe to the newsletter" } %>
15
+ <% f.with_field_content do %>
16
+ <%= render(Shadcn::Switch::Component.new(id: "field-news")) %>
17
+ <% end %>
18
+ <% end %>
19
+ <%= render(Shadcn::Field::Component.new) do |f| %>
20
+ <% f.with_label(for: "field-password") { "Password" } %>
21
+ <% f.with_field_content do %>
22
+ <%= render(Shadcn::Input::Component.new(id: "field-password", type: "password",
23
+ "aria-invalid": "true",
24
+ "aria-describedby": "field-password-error")) %>
25
+ <% end %>
26
+ <% f.with_error(id: "field-password-error",
27
+ errors: ["Password is too short", "Password needs a digit"]) %>
28
+ <% end %>
29
+ <% end %>
@@ -0,0 +1,38 @@
1
+ <%# The FormBuilder integration: ids, names, error text, `aria-invalid` and
2
+ `aria-describedby` all come from the model. %>
3
+ <% signup = Struct.new(:email, :bio, :plan, :newsletter, :visibility, :agreed, :errors, keyword_init: true) do
4
+ def self.name = "Signup"
5
+ def to_model = self
6
+ def model_name = ActiveModel::Name.new(self.class, nil, "Signup")
7
+ def persisted? = false
8
+ def to_key = nil
9
+ end.new(
10
+ email: "not-an-email", bio: "Rails developer.", plan: "pro",
11
+ newsletter: true, visibility: "public", agreed: false,
12
+ errors: ActiveModel::Errors.new(Object.new).tap { |e|
13
+ e.add(:email, "must contain @")
14
+ e.add(:email, "is too short (minimum is 6 characters)")
15
+ }
16
+ ) %>
17
+
18
+ <%= shadcn_form_with(model: signup, url: "#", scope: :signup, class: "w-[420px]") do |f| %>
19
+ <%= render(Shadcn::Field::Group::Component.new) do %>
20
+ <%= f.shadcn_input_field :email, label: "Email", description: "We never share it.", type: "email" %>
21
+
22
+ <%= f.shadcn_textarea_field :bio, label: "Bio", description: "A sentence or two." %>
23
+
24
+ <%= f.shadcn_field :plan, label: "Plan" do %>
25
+ <%= f.shadcn_select :plan, [["Free", "free"], ["Pro", "pro"]], placeholder: "Choose a plan" %>
26
+ <% end %>
27
+
28
+ <%= f.shadcn_field :visibility, label: "Visibility" do %>
29
+ <%= f.shadcn_radio_group :visibility, [["Public", "public"], ["Private", "private"]] %>
30
+ <% end %>
31
+
32
+ <%= f.shadcn_switch_field :newsletter, label: "Send me the newsletter" %>
33
+
34
+ <%= f.shadcn_checkbox_field :agreed, label: "I accept the terms" %>
35
+
36
+ <%= f.shadcn_submit "Create account" %>
37
+ <% end %>
38
+ <% end %>