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,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Field
5
+ module Separator
6
+ # FieldSeparator — a rule with optional centred content.
7
+ class Component < ApplicationViewComponent
8
+ CONTENT_CLASSES = "relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
9
+
10
+ slot_name :"field-separator"
11
+
12
+ style do
13
+ base {
14
+ "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2"
15
+ }
16
+ end
17
+
18
+ def element_attributes(**defaults)
19
+ super(**{ "data-content" => content.present? }.merge(defaults))
20
+ end
21
+
22
+ def call
23
+ body = [ render(Shadcn::Separator::Component.new(class: "absolute inset-0 top-1/2")) ]
24
+
25
+ if content.present?
26
+ body << tag.span(content, class: CONTENT_CLASSES,
27
+ "data-slot": "field-separator-content")
28
+ end
29
+
30
+ render_element(body: safe_join(body))
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ # The parts of the Field 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 Field
8
+ extend Parts
9
+
10
+ part :content, slot: "field-content",
11
+ classes: "group/field-content flex flex-1 flex-col gap-1.5 leading-snug"
12
+
13
+ part :description, tag: :p, slot: "field-description",
14
+ classes: "text-sm leading-normal font-normal text-muted-foreground " \
15
+ "group-has-[[data-orientation=horizontal]]/field:text-balance " \
16
+ "last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5 " \
17
+ "[&>a]:underline [&>a]:underline-offset-4 " \
18
+ "[&>a:hover]:text-primary"
19
+
20
+ part :group, slot: "field-group",
21
+ classes: "group/field-group @container/field-group flex w-full flex-col gap-7 " \
22
+ "data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4"
23
+
24
+ part :label, tag: :label, slot: "field-label",
25
+ classes: "flex items-center gap-2 text-sm leading-none font-medium select-none " \
26
+ "group-data-[disabled=true]:pointer-events-none " \
27
+ "group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed " \
28
+ "peer-disabled:opacity-50 group/field-label peer/field-label flex w-fit " \
29
+ "gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 " \
30
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col " \
31
+ "has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border " \
32
+ "[&>*]:data-[slot=field]:p-4 has-data-[state=checked]:border-primary " \
33
+ "has-data-[state=checked]:bg-primary/5 " \
34
+ "dark:has-data-[state=checked]:bg-primary/10"
35
+
36
+ part :set, tag: :fieldset, slot: "field-set",
37
+ classes: "flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 " \
38
+ "has-[>[data-slot=radio-group]]:gap-3"
39
+
40
+ part :title, slot: "field-label",
41
+ classes: "flex w-fit items-center gap-2 text-sm leading-snug font-medium " \
42
+ "group-data-[disabled=true]/field:opacity-50"
43
+ end
44
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module HoverCard
5
+ # Port of registry/new-york-v4/ui/hover-card.tsx, whose behaviour is Radix's
6
+ # `HoverCard` — vendored at `vendor/radix/ui/hover-card.tsx`.
7
+ #
8
+ # A tooltip's larger relative, and the difference is what the delays are
9
+ # for: this content is meant to be *entered*. Moving the pointer from the
10
+ # trigger onto the card keeps it open, which is why the card carries the
11
+ # same enter and leave handlers the trigger does.
12
+ class Component < ApplicationViewComponent
13
+ renders_one :trigger, "Shadcn::HoverCard::Trigger::Component"
14
+ renders_one :hover_card_content, "Shadcn::HoverCard::Content::Component"
15
+
16
+ slot_name :"hover-card"
17
+
18
+ attr_reader :side, :align, :side_offset, :open_delay, :close_delay
19
+
20
+ # `open_delay` and `close_delay` are Radix's own defaults
21
+ # (vendor/radix/ui/hover-card.tsx:59-60), not numbers chosen here. The
22
+ # closing one is the longer-lived decision of the two: it is the window in
23
+ # which you can cross the gap between the trigger and the card.
24
+ def initialize(side: :bottom, align: :center, side_offset: 4,
25
+ open_delay: 700, close_delay: 300, **attributes)
26
+ @side = side&.to_sym || :bottom
27
+ @align = align&.to_sym || :center
28
+ @side_offset = side_offset
29
+ @open_delay = open_delay
30
+ @close_delay = close_delay
31
+ super(**attributes)
32
+ end
33
+
34
+ def element_attributes(**defaults)
35
+ super(**{
36
+ style: merged_style(CONTENTS_STYLE),
37
+ "data-controller" => "shadcn--hover-card",
38
+ "data-shadcn--hover-card-side-value" => side,
39
+ "data-shadcn--hover-card-align-value" => align,
40
+ "data-shadcn--hover-card-side-offset-value" => side_offset,
41
+ "data-shadcn--hover-card-open-delay-value" => open_delay,
42
+ "data-shadcn--hover-card-close-delay-value" => close_delay
43
+ }.merge(defaults))
44
+ end
45
+
46
+ def call
47
+ render_element(body: safe_join([ trigger, hover_card_content, content ].compact))
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module HoverCard
5
+ module Content
6
+ # HoverCardContent.
7
+ #
8
+ # It carries the same pointer handlers as the trigger, and that is the
9
+ # component rather than a detail: without them, moving onto the card is
10
+ # leaving the trigger and the card closes under the pointer that went for
11
+ # it (hover-card.tsx:220-221).
12
+ #
13
+ # No `role` and no `aria-*`, which is Radix's own — measured, there is not
14
+ # one of either in the whole primitive. The card is a sighted-hover
15
+ # affordance, and nothing in it is announced. That is also why every
16
+ # tabbable inside it is taken out of the tab order; the controller does
17
+ # that, since the content is a host's markup rather than this component's.
18
+ class Component < ApplicationViewComponent
19
+ slot_name :"hover-card-content"
20
+
21
+ style do
22
+ base {
23
+ "z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md " \
24
+ "border bg-popover p-4 text-popover-foreground shadow-md outline-hidden " \
25
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 " \
26
+ "data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 " \
27
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 " \
28
+ "data-[state=closed]:zoom-out-95 data-[state=open]:animate-in " \
29
+ "data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95"
30
+ }
31
+ end
32
+
33
+ def element_attributes(**defaults)
34
+ super(**{
35
+ hidden: true,
36
+ "data-state" => "closed",
37
+ "data-shadcn--hover-card-target" => "content",
38
+ "data-action" => "pointerenter->shadcn--hover-card#open " \
39
+ "pointerleave->shadcn--hover-card#close"
40
+ }.merge(defaults))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module HoverCard
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+
10
+ # Upstream's "Sides" and "Trigger Delays".
11
+ def sides
12
+ render_with_template
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ <%# The card upstream's example builds: a link that, held under the pointer for
2
+ a moment, explains what it points at.
3
+
4
+ The trigger is `as: :a` — a hover card usually hangs off something already
5
+ in the text — and it stays focusable, because Radix opens on focus as well
6
+ as on hover (hover-card.tsx:138-141) and that is the only way a keyboard
7
+ reaches it at all.
8
+
9
+ Nothing inside the card is reachable by keyboard: the controller takes every
10
+ tabbable in it out of the tab order, which is Radix's own behaviour and
11
+ follows from the card having no way to be tabbed *into*. Put nothing in one
12
+ that is only available there. %>
13
+ <div class="flex h-64 items-start justify-center pt-12">
14
+ <%= render(Shadcn::HoverCard::Component.new) do |card| %>
15
+ <% card.with_trigger(as: :a, href: "#", class: "text-sm font-medium underline underline-offset-4") do %>
16
+ @shadcn
17
+ <% end %>
18
+ <% card.with_hover_card_content do %>
19
+ <div class="flex gap-4">
20
+ <%= render(Shadcn::Avatar::Component.new) do |avatar| %>
21
+ <% avatar.with_fallback { "SC" } %>
22
+ <% end %>
23
+ <div class="space-y-1">
24
+ <h4 class="text-sm font-semibold">@shadcn</h4>
25
+ <p class="text-sm">The React framework — created and maintained by @vercel.</p>
26
+ <p class="text-xs text-muted-foreground">Joined December 2021</p>
27
+ <%# A link, on purpose: it is what the tab-order behaviour has to act
28
+ on, and a card with nothing focusable in it would make that
29
+ assertion vacuous. It is also the realistic case — the reason to
30
+ build a card rather than a tooltip is that it can hold one. %>
31
+ <a href="#" class="text-xs font-medium underline underline-offset-4">View profile</a>
32
+ </div>
33
+ </div>
34
+ <% end %>
35
+ <% end %>
36
+ </div>
@@ -0,0 +1,15 @@
1
+ <%# Upstream's "Sides" and "Trigger Delays". The delays are the part worth
2
+ seeing: a hover card that opened instantly would fire on every pointer that
3
+ crossed it, and one that closed instantly could not be reached. %>
4
+ <div class="flex h-48 items-center justify-center gap-2">
5
+ <% [[:top, 0, 0], [:right, 700, 300], [:bottom, 0, 0], [:left, 700, 300]].each do |side, open_delay, close_delay| %>
6
+ <%= render(Shadcn::HoverCard::Component.new(side:, open_delay:, close_delay:)) do |h| %>
7
+ <% h.with_trigger(as: :button, class: Shadcn::Button::Component.variant_classes(variant: :outline)) { side.to_s.capitalize } %>
8
+ <% h.with_hover_card_content(class: "w-56") do %>
9
+ <p class="text-sm">
10
+ On the <%= side %>, after <%= open_delay %>ms.
11
+ </p>
12
+ <% end %>
13
+ <% end %>
14
+ <% end %>
15
+ </div>
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module HoverCard
5
+ module Trigger
6
+ # HoverCardTrigger. Radix opens on hover *and* on focus and closes on blur
7
+ # (hover-card.tsx:138-141), so a keyboard reaches the card even though it
8
+ # cannot be tabbed into — see the content for the other half of that.
9
+ #
10
+ # `default_tag :button` because the demo's trigger is one and a hoverable
11
+ # affordance needs to be focusable to open on focus at all; `as: :a` is
12
+ # the usual override, which is what upstream's example passes.
13
+ class Component < ApplicationViewComponent
14
+ default_tag :button
15
+ slot_name :"hover-card-trigger"
16
+
17
+ def element_attributes(**defaults)
18
+ attributes = {
19
+ "data-state" => "closed",
20
+ "data-shadcn--hover-card-target" => "trigger",
21
+ "data-action" => "pointerenter->shadcn--hover-card#open " \
22
+ "pointerleave->shadcn--hover-card#close " \
23
+ "focus->shadcn--hover-card#open " \
24
+ "blur->shadcn--hover-card#close"
25
+ }
26
+ attributes[:type] = "button" if tag_name.to_sym == :button
27
+
28
+ super(**attributes.merge(defaults))
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Icon
5
+ # Inline replacement for the `lucide-react` icons the components import.
6
+ #
7
+ # <%= render Shadcn::Icon::Component.new(:"chevron-down", class: "size-4") %>
8
+ #
9
+ # Emits the same SVG lucide does — 24×24 viewBox, `currentColor` stroke,
10
+ # `lucide lucide-<name>` classes — so the `[&_svg:not([class*='size-'])]:size-4`
11
+ # rules on the surrounding components behave identically.
12
+ class Component < ApplicationViewComponent
13
+ # The drawings live in `vendor/lucide/icons` as the files lucide
14
+ # publishes, and `rake icons:build` turns them into
15
+ # `ShadcnViewComponent::Icons::PATHS`. They used to be typed into this
16
+ # file by hand, which went exactly as that always goes: the count in the
17
+ # README said eleven of twenty-two, and `search` had been drawn from an
18
+ # older lucide — a magnifier with a shorter handle than the one every
19
+ # other icon in the set was drawn with.
20
+ SVG_ATTRIBUTES = {
21
+ "xmlns" => "http://www.w3.org/2000/svg",
22
+ "width" => "24",
23
+ "height" => "24",
24
+ "viewBox" => "0 0 24 24",
25
+ "fill" => "none",
26
+ "stroke" => "currentColor",
27
+ "stroke-width" => "2",
28
+ "stroke-linecap" => "round",
29
+ "stroke-linejoin" => "round"
30
+ }.freeze
31
+
32
+ default_tag :svg
33
+
34
+ def initialize(name, **attributes)
35
+ # Resolved through the registry, which owns the alias table: a host
36
+ # registering `"more-horizontal"` and this rendering `"ellipsis"` have
37
+ # to meet somewhere, and the file that stores the drawings is the only
38
+ # place both can.
39
+ @name = ShadcnViewComponent::IconRegistry.canonical(name)
40
+
41
+ # Loud where it can be fixed, silent where it cannot. An icon is
42
+ # decorative, and a gem should not be able to take down a page in an
43
+ # application it has never seen — the same trade Rails makes with a
44
+ # missing translation.
45
+ raise ArgumentError, "unknown lucide icon: #{name}" if path.nil? && Rails.env.local?
46
+
47
+ super(**attributes)
48
+ end
49
+
50
+ attr_reader :name
51
+
52
+ # The registry is read first so a host can replace one of the bundled
53
+ # eleven and not only add a twelfth. The other order ignored
54
+ # `register("check", …)` in silence — nothing raised, nothing logged, the
55
+ # gem's own tick still rendering — which leaves a host staring at an icon
56
+ # that will not change with no way to find out why.
57
+ def path
58
+ @path ||= Shadcn::Icon.registered[name] || ShadcnViewComponent::Icons::PATHS[name]
59
+ end
60
+
61
+ def call
62
+ return "".html_safe if path.nil?
63
+
64
+ render_element(body: path.html_safe)
65
+ end
66
+
67
+ def element_attributes(**defaults)
68
+ super(**SVG_ATTRIBUTES.merge(defaults))
69
+ end
70
+
71
+ def css_classes(extra = nil)
72
+ ShadcnViewComponent.cn("lucide", "lucide-#{name}", extra)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ # `Shadcn::Icon.register` / `.registered`, kept in this sibling file — the
5
+ # same split every other family uses between its shared module and
6
+ # `component.rb` — so `Shadcn::Icon` resolves without first loading
7
+ # `Icon::Component`, and these read naturally from a view or another
8
+ # component without going through it. That does *not* extend to a host's
9
+ # initializer: no autoloadable constant, this one included, resolves there
10
+ # — `ShadcnViewComponent::IconRegistry`, required directly in
11
+ # `lib/shadcn_view_component.rb`, is the entry point the README documents
12
+ # for that case.
13
+ #
14
+ # The storage itself lives in `ShadcnViewComponent::IconRegistry`, under
15
+ # `lib/`, rather than on this module: `app/components` reloads in
16
+ # development, and a hash held here would be discarded — and every
17
+ # registered icon un-registered — on the next code reload.
18
+ module Icon
19
+ def self.registered
20
+ ShadcnViewComponent::IconRegistry.registered
21
+ end
22
+
23
+ # Every lucide alias this gem knows — `more-horizontal` for `ellipsis`,
24
+ # `loader-2` for `loader-circle`. Registering under either spelling reaches
25
+ # both, because the registry stores one entry per icon rather than one per
26
+ # name.
27
+ def self.aliases
28
+ ShadcnViewComponent::IconRegistry::ALIASES
29
+ end
30
+
31
+ # The ported components import a couple of dozen lucide icons; lucide has
32
+ # about 1,500. The exact set is `ShadcnViewComponent::Icons::PATHS`, and a
33
+ # spec keeps it equal to what the components render — no number is written
34
+ # in prose here, because that is what went stale last time.
35
+ # A host reaches this pair from a view, a component or anywhere else
36
+ # autoloading has already run — not from an initializer, which is what
37
+ # `ShadcnViewComponent::IconRegistry` is for. Registering a bundled name
38
+ # replaces it.
39
+ def self.register(name, path)
40
+ ShadcnViewComponent::IconRegistry.register(name, path)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Input
5
+ # Port of registry/new-york-v4/ui/input.tsx
6
+ class Component < ApplicationViewComponent
7
+ default_tag :input
8
+ slot_name :input
9
+
10
+ style do
11
+ base {
12
+ "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 " \
13
+ "py-1 text-base shadow-xs transition-[color,box-shadow] outline-none " \
14
+ "selection:bg-primary selection:text-primary-foreground file:inline-flex " \
15
+ "file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium " \
16
+ "file:text-foreground placeholder:text-muted-foreground " \
17
+ "disabled:pointer-events-none disabled:cursor-not-allowed " \
18
+ "disabled:opacity-50 md:text-sm dark:bg-input/30 focus-visible:border-ring " \
19
+ "focus-visible:ring-[3px] focus-visible:ring-ring/50 " \
20
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 " \
21
+ "dark:aria-invalid:ring-destructive/40"
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Input
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+
10
+ # Upstream's "Disabled", "Invalid", "File" and "Required", together: each is an attribute passed straight through rather than a variant.
11
+ def states
12
+ render_with_template
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ <div class="flex w-[320px] flex-col gap-4">
2
+ <%= render(Shadcn::Input::Component.new(type: "email", placeholder: "m@example.com")) %>
3
+ <%= render(Shadcn::Input::Component.new(placeholder: "Disabled", disabled: true)) %>
4
+ <%= render(Shadcn::Input::Component.new(placeholder: "Invalid", "aria-invalid": "true")) %>
5
+ </div>
@@ -0,0 +1,20 @@
1
+ <%# Upstream shows these as four examples — "Disabled", "Invalid", "File" and
2
+ "Required". They are one page here because none of them is a variant: each
3
+ is an attribute the caller passes straight through, and seeing them beside
4
+ one another is what shows that.
5
+
6
+ Every control is labelled. axe takes a placeholder as an accessible name, so
7
+ a placeholder-less input is the one that fails — which is what the invalid
8
+ example needs to be, since it carries a value rather than a placeholder. %>
9
+ <div class="flex w-[320px] flex-col gap-4">
10
+ <% [["ordinary", "Ordinary", {}],
11
+ ["disabled", "Disabled", { disabled: true }],
12
+ ["invalid", "Invalid", { value: "not-an-email", "aria-invalid": "true" }],
13
+ ["file", "File", { type: "file" }],
14
+ ["required", "Required", { required: true }]].each do |id, label, options| %>
15
+ <div class="flex flex-col gap-2">
16
+ <%= render(Shadcn::Label::Component.new(for: "input-#{id}")) { label } %>
17
+ <%= render(Shadcn::Input::Component.new(id: "input-#{id}", **options)) %>
18
+ </div>
19
+ <% end %>
20
+ </div>
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module InputGroup
5
+ module Addon
6
+ # InputGroupAddon. The one part of this family that is not just markup:
7
+ # upstream gives it an `onClick` that focuses the group's input, unless
8
+ # the click landed on a button. That is the `focusControl` action.
9
+ class Component < ApplicationViewComponent
10
+ slot_name :"input-group-addon"
11
+
12
+ style do
13
+ base {
14
+ "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm " \
15
+ "font-medium text-muted-foreground select-none " \
16
+ "group-data-[disabled=true]/input-group:opacity-50 " \
17
+ "[&>kbd]:rounded-[calc(var(--radius)-5px)] " \
18
+ "[&>svg:not([class*='size-'])]:size-4"
19
+ }
20
+
21
+ variants {
22
+ align {
23
+ # The keys keep upstream's spelling, which is not valid Ruby.
24
+ send(:"inline-start") {
25
+ "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]"
26
+ }
27
+ send(:"inline-end") {
28
+ "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]"
29
+ }
30
+ send(:"block-start") {
31
+ "order-first w-full justify-start px-3 pt-3 " \
32
+ "group-has-[>input]/input-group:pt-2.5 [.border-b]:pb-3"
33
+ }
34
+ send(:"block-end") {
35
+ "order-last w-full justify-start px-3 pb-3 " \
36
+ "group-has-[>input]/input-group:pb-2.5 [.border-t]:pt-3"
37
+ }
38
+ }
39
+ }
40
+
41
+ defaults { { align: :"inline-start" } }
42
+ end
43
+
44
+ attr_reader :align
45
+
46
+ def initialize(align: :"inline-start", **attributes)
47
+ @align = align&.to_sym || :"inline-start"
48
+ super(**attributes)
49
+ end
50
+
51
+ def style_variants
52
+ { align: }
53
+ end
54
+
55
+ def element_attributes(**defaults)
56
+ super(**{
57
+ role: "group",
58
+ "data-align" => align,
59
+ "data-action" => "click->shadcn--input-group#focusControl"
60
+ }.merge(defaults))
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module InputGroup
5
+ module Button
6
+ # InputGroupButton renders a Button with its own size scale layered on
7
+ # top, and forces three of the button's props.
8
+ #
9
+ # Those three are *defaults*, not overrides: they go through `super`, so
10
+ # a caller asking for `variant: :destructive` still gets it. Ranking them
11
+ # above the caller would make the button un-restylable.
12
+ class Component < Shadcn::Button::Component
13
+ SIZES = {
14
+ xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 " \
15
+ "[&>svg:not([class*='size-'])]:size-3.5",
16
+ sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",
17
+ "icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
18
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0"
19
+ }.freeze
20
+
21
+ attr_reader :group_size
22
+
23
+ def initialize(variant: :ghost, size: :xs, **attributes)
24
+ @group_size = size&.to_sym || :xs
25
+ # The button's own size scale is not this one, so it keeps its
26
+ # default; the group's scale is applied as classes instead.
27
+ super(variant:, **attributes)
28
+ end
29
+
30
+ def element_attributes(**defaults)
31
+ super(**{ type: "button", "data-size" => group_size }.merge(defaults))
32
+ end
33
+
34
+ def css_classes(extra = nil)
35
+ super([ SIZES.fetch(group_size), extra ].compact.join(" "))
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module InputGroup
5
+ # Port of registry/new-york-v4/ui/input-group.tsx
6
+ #
7
+ # `group/input-group` is a named Tailwind group the addon's classes reach
8
+ # back into (`group-has-[>input]/input-group:…`), so the two halves only
9
+ # work as a pair.
10
+ class Component < ApplicationViewComponent
11
+ slot_name :"input-group"
12
+
13
+ style do
14
+ base {
15
+ "group/input-group relative flex w-full items-center rounded-md border " \
16
+ "border-input shadow-xs transition-[color,box-shadow] outline-none " \
17
+ "dark:bg-input/30 h-9 min-w-0 has-[>textarea]:h-auto " \
18
+ "has-[>[data-align=inline-start]]:[&>input]:pl-2 " \
19
+ "has-[>[data-align=inline-end]]:[&>input]:pr-2 " \
20
+ "has-[>[data-align=block-start]]:h-auto " \
21
+ "has-[>[data-align=block-start]]:flex-col " \
22
+ "has-[>[data-align=block-start]]:[&>input]:pb-3 " \
23
+ "has-[>[data-align=block-end]]:h-auto " \
24
+ "has-[>[data-align=block-end]]:flex-col " \
25
+ "has-[>[data-align=block-end]]:[&>input]:pt-3 " \
26
+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring " \
27
+ "has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] " \
28
+ "has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 " \
29
+ "has-[[data-slot][aria-invalid=true]]:border-destructive " \
30
+ "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 " \
31
+ "dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40"
32
+ }
33
+ end
34
+
35
+ # The controller lives on the group rather than on the addon, so that
36
+ # `this.element` is the group and the lookup matches upstream's
37
+ # `currentTarget.parentElement.querySelector("input")`.
38
+ def element_attributes(**defaults)
39
+ super(**{ role: "group", "data-controller" => "shadcn--input-group" }.merge(defaults))
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module InputGroup
5
+ module Input
6
+ # InputGroupInput renders the ported Input, stripped of its own border and
7
+ # ring so the group's box provides both.
8
+ #
9
+ # Its `data-slot` is `input-group-control`, which it shares with
10
+ # InputGroupTextarea. That is deliberate upstream: the group's
11
+ # `has-[[data-slot=input-group-control]:focus-visible]:…` classes have to
12
+ # match either one, so disambiguating them would break the focus ring.
13
+ class Component < Shadcn::Input::Component
14
+ slot_name :"input-group-control"
15
+
16
+ EXTRA_CLASSES = "flex-1 rounded-none border-0 bg-transparent shadow-none " \
17
+ "focus-visible:ring-0 dark:bg-transparent"
18
+
19
+ def css_classes(extra = nil)
20
+ super([ EXTRA_CLASSES, extra ].compact.join(" "))
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end