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,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module ModeSwitcher
5
+ # Port of apps/v4/components/mode-switcher.tsx — the single button used in
6
+ # shadcn's own site header, which flips straight between light and dark
7
+ # rather than offering the three-way menu.
8
+ #
9
+ # The icon is the same half-filled circle upstream draws inline.
10
+ class Component < ApplicationViewComponent
11
+ ICON = %(<path stroke="none" d="M0 0h24v24H0z" fill="none"/>) +
12
+ %(<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"/>) +
13
+ %(<path d="M12 3l0 18"/><path d="M12 9l4.65 -4.65"/>) +
14
+ %(<path d="M12 14.3l7.37 -7.37"/><path d="M12 19.6l8.85 -8.85"/>)
15
+
16
+ ICON_ATTRIBUTES = {
17
+ "xmlns" => "http://www.w3.org/2000/svg",
18
+ "width" => "24",
19
+ "height" => "24",
20
+ "viewBox" => "0 0 24 24",
21
+ "fill" => "none",
22
+ "stroke" => "currentColor",
23
+ "stroke-width" => "2",
24
+ "stroke-linecap" => "round",
25
+ "stroke-linejoin" => "round",
26
+ "class" => "size-4.5"
27
+ }.freeze
28
+
29
+ slot_name :"mode-switcher"
30
+
31
+ attr_reader :variant
32
+
33
+ def initialize(variant: :ghost, **attributes)
34
+ @variant = variant&.to_sym || :ghost
35
+ super(**attributes)
36
+ end
37
+
38
+ def element_attributes(**defaults)
39
+ super(**{
40
+ style: merged_style(CONTENTS_STYLE),
41
+ "data-controller" => "shadcn--theme"
42
+ }.merge(defaults))
43
+ end
44
+
45
+ def call
46
+ render_element(body: button)
47
+ end
48
+
49
+ private
50
+
51
+ def button
52
+ render(Button::Component.new(
53
+ variant:,
54
+ size: :icon,
55
+ class: "group/toggle extend-touch-target size-8",
56
+ "data-action": "shadcn--theme#toggle"
57
+ )) do
58
+ safe_join([ tag.svg(raw(ICON), **ICON_ATTRIBUTES), label ])
59
+ end
60
+ end
61
+
62
+ def label
63
+ tag.span(shadcn_t("theme.toggle"), class: "sr-only")
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module ModeSwitcher
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ <div class="flex items-center gap-3">
2
+ <%= render(Shadcn::ModeSwitcher::Component.new) %>
3
+ <%= render(Shadcn::ModeSwitcher::Component.new(variant: :outline)) %>
4
+ <span class="text-sm text-muted-foreground">Flips straight between light and dark.</span>
5
+ </div>
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module ModeToggle
5
+ # Port of registry/new-york-v4/examples/mode-toggle.tsx — the dropdown
6
+ # shadcn documents for switching between light, dark and system.
7
+ #
8
+ # In React the items call next-themes' `setTheme`; here they hand the mode
9
+ # to the `shadcn--theme` Stimulus controller wrapping them.
10
+ class Component < ApplicationViewComponent
11
+ SUN_CLASSES = "h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all " \
12
+ "dark:scale-0 dark:-rotate-90"
13
+
14
+ MOON_CLASSES = "absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all " \
15
+ "dark:scale-100 dark:rotate-0"
16
+
17
+ MODES = %w[light dark system].freeze
18
+
19
+ slot_name :"mode-toggle"
20
+
21
+ attr_reader :variant, :align
22
+
23
+ def initialize(variant: :outline, align: :end, **attributes)
24
+ @variant = variant&.to_sym || :outline
25
+ @align = align&.to_sym || :end
26
+ super(**attributes)
27
+ end
28
+
29
+ def element_attributes(**defaults)
30
+ super(**{
31
+ style: merged_style(CONTENTS_STYLE),
32
+ "data-controller" => "shadcn--theme"
33
+ }.merge(defaults))
34
+ end
35
+
36
+ def call
37
+ render_element(body: menu)
38
+ end
39
+
40
+ private
41
+
42
+ def menu
43
+ render(DropdownMenu::Component.new(align:)) do |dropdown|
44
+ dropdown.with_trigger(class: trigger_classes) { trigger_content }
45
+ dropdown.with_menu_content { items }
46
+ end
47
+ end
48
+
49
+ def trigger_classes
50
+ Button::Component.variant_classes(variant:, size: :icon)
51
+ end
52
+
53
+ def trigger_content
54
+ safe_join([
55
+ render(Icon::Component.new("sun", class: SUN_CLASSES)),
56
+ render(Icon::Component.new("moon", class: MOON_CLASSES)),
57
+ tag.span(shadcn_t("theme.toggle"), class: "sr-only")
58
+ ])
59
+ end
60
+
61
+ def items
62
+ safe_join(MODES.map { |mode| item(mode) })
63
+ end
64
+
65
+ # Only the extra action goes here: the item already carries the dropdown's
66
+ # own, and `data-action` is concatenated rather than replaced.
67
+ def item(mode)
68
+ render(DropdownMenu::Item::Component.new(
69
+ "data-mode": mode,
70
+ "data-action": "click->shadcn--theme#setMode"
71
+ )) { shadcn_t("theme.#{mode}") }
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module ModeToggle
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ <div class="flex flex-col gap-6">
2
+ <div class="flex items-center gap-3">
3
+ <%= render(Shadcn::ModeToggle::Component.new) %>
4
+ <span class="text-sm text-muted-foreground">Light / Dark / System</span>
5
+ </div>
6
+
7
+ <%= render(Shadcn::Card::Component.new(class: "w-full max-w-sm")) do |card| %>
8
+ <% card.with_header do |header| %>
9
+ <% header.with_title { "It follows the mode" } %>
10
+ <% header.with_description { "Every token swaps with the .dark class." } %>
11
+ <% end %>
12
+ <% card.with_card_content do %>
13
+ <div class="flex flex-wrap gap-2">
14
+ <%= render(Shadcn::Button::Component.new) { "Primary" } %>
15
+ <%= render(Shadcn::Button::Component.new(variant: :secondary)) { "Secondary" } %>
16
+ <%= render(Shadcn::Badge::Component.new) { "Badge" } %>
17
+ </div>
18
+ <% end %>
19
+ <% end %>
20
+ </div>
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module NativeSelect
5
+ # Port of registry/new-york-v4/ui/native-select.tsx — a real <select> with
6
+ # the native chevron replaced by a lucide icon.
7
+ class Component < ApplicationViewComponent
8
+ WRAPPER_CLASSES = "group/native-select relative w-fit has-[select:disabled]:opacity-50"
9
+
10
+ ICON_CLASSES = "pointer-events-none absolute top-1/2 right-3.5 size-4 " \
11
+ "-translate-y-1/2 text-muted-foreground opacity-50 select-none"
12
+
13
+ default_tag :select
14
+ slot_name :"native-select"
15
+
16
+ style do
17
+ base {
18
+ "h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-transparent " \
19
+ "px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none " \
20
+ "selection:bg-primary selection:text-primary-foreground " \
21
+ "placeholder:text-muted-foreground disabled:pointer-events-none " \
22
+ "disabled:cursor-not-allowed data-[size=sm]:h-8 data-[size=sm]:py-1 " \
23
+ "dark:bg-input/30 dark:hover:bg-input/50 " \
24
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 " \
25
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 " \
26
+ "dark:aria-invalid:ring-destructive/40"
27
+ }
28
+ end
29
+
30
+ attr_reader :size
31
+
32
+ def initialize(size: :default, **attributes)
33
+ @size = size&.to_sym || :default
34
+ super(**attributes)
35
+ end
36
+
37
+ def element_attributes(**defaults)
38
+ super(**{ "data-size" => size }.merge(defaults))
39
+ end
40
+
41
+ def call
42
+ tag.div(class: WRAPPER_CLASSES, "data-slot": "native-select-wrapper") do
43
+ safe_join([ render_element(body: content), chevron ])
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def chevron
50
+ render(Icon::Component.new(
51
+ "chevron-down",
52
+ class: ICON_CLASSES,
53
+ "aria-hidden": "true",
54
+ "data-slot": "native-select-icon"
55
+ ))
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module NativeSelect
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+
10
+ # Upstream's "With Groups", "Disabled" and "Invalid".
11
+ def groups
12
+ render_with_template
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ <div class="flex items-center gap-4">
2
+ <% %i[default sm].each do |size| %>
3
+ <%= render(Shadcn::NativeSelect::Component.new(size:, "aria-label": "Favourite fruit")) do %>
4
+ <% %w[Apple Banana Blueberry].each do |fruit| %>
5
+ <%= render(Shadcn::NativeSelect::Option::Component.new(value: fruit.downcase)) { fruit } %>
6
+ <% end %>
7
+ <% end %>
8
+ <% end %>
9
+ </div>
@@ -0,0 +1,26 @@
1
+ <%# Upstream's "With Groups", "Disabled" and "Invalid". The optgroup is the one
2
+ thing the styled Select cannot do with a real element — this is a `<select>`,
3
+ so the browser draws the grouping itself. %>
4
+ <div class="flex items-start gap-4">
5
+ <%= render(Shadcn::NativeSelect::Component.new("aria-label": "Timezone")) do %>
6
+ <%= render(Shadcn::NativeSelect::OptGroup::Component.new(label: "Europe")) do %>
7
+ <% %w[London Paris Berlin].each do |city| %>
8
+ <%= render(Shadcn::NativeSelect::Option::Component.new(value: city.downcase)) { city } %>
9
+ <% end %>
10
+ <% end %>
11
+ <%= render(Shadcn::NativeSelect::OptGroup::Component.new(label: "Americas")) do %>
12
+ <% %w[Toronto Chicago].each do |city| %>
13
+ <%= render(Shadcn::NativeSelect::Option::Component.new(value: city.downcase)) { city } %>
14
+ <% end %>
15
+ <% end %>
16
+ <% end %>
17
+
18
+ <%= render(Shadcn::NativeSelect::Component.new("aria-label": "Disabled", disabled: true)) do %>
19
+ <%= render(Shadcn::NativeSelect::Option::Component.new(value: "one")) { "Not available" } %>
20
+ <% end %>
21
+
22
+ <%= render(Shadcn::NativeSelect::Component.new("aria-label": "Invalid", "aria-invalid": "true")) do %>
23
+ <%= render(Shadcn::NativeSelect::Option::Component.new(value: "")) { "Pick one" } %>
24
+ <%= render(Shadcn::NativeSelect::Option::Component.new(value: "one")) { "One" } %>
25
+ <% end %>
26
+ </div>
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ # The parts of the NativeSelect 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 NativeSelect
8
+ extend Parts
9
+
10
+ part :opt_group, tag: :optgroup, slot: "native-select-optgroup",
11
+ classes: "bg-[Canvas] text-[CanvasText]"
12
+
13
+ part :option, tag: :option, slot: "native-select-option",
14
+ classes: "bg-[Canvas] text-[CanvasText]"
15
+ end
16
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module NavigationMenu
5
+ # Port of registry/new-york-v4/ui/navigation-menu.tsx, whose behaviour is
6
+ # Radix's `NavigationMenu` — 1,403 lines against shadcn's 168, vendored at
7
+ # `vendor/radix/ui/navigation-menu.tsx`.
8
+ #
9
+ # **`data-viewport` is always `false` here, and that is the decision this
10
+ # port turns on.** Upstream's default renders every panel into one shared
11
+ # box that animates between their sizes — measured on the live demo, the
12
+ # content's parent is `navigation-menu-viewport`, which means React has
13
+ # moved it there through a portal. Nothing is portalled in this gem, for the
14
+ # reason in decisions/02-javascript.md, so that mode cannot be reproduced
15
+ # without giving up the rule every other component here was built on.
16
+ #
17
+ # `viewport={false}` is not a workaround: it is a configuration shadcn
18
+ # supports and ships classes for, and in it each panel stays inside its own
19
+ # item with its own border, background and shadow. See
20
+ # features/navigation-menu.md.
21
+ class Component < ApplicationViewComponent
22
+ slot_name :"navigation-menu"
23
+
24
+ style do
25
+ base {
26
+ "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center"
27
+ }
28
+ end
29
+
30
+ attr_reader :delay_duration, :skip_delay_duration
31
+
32
+ # Radix's own defaults (navigation-menu.tsx:136-137). `skip_delay_duration`
33
+ # is the grace period after a panel closes in which the next one opens at
34
+ # once — it is what stops a menu feeling sticky when you sweep across it.
35
+ def initialize(delay_duration: 200, skip_delay_duration: 300, **attributes)
36
+ @delay_duration = delay_duration
37
+ @skip_delay_duration = skip_delay_duration
38
+ super(**attributes)
39
+ end
40
+
41
+ def element_attributes(**defaults)
42
+ super(**{
43
+ "data-viewport" => "false",
44
+ "data-controller" => "shadcn--navigation-menu",
45
+ "data-shadcn--navigation-menu-delay-value" => delay_duration,
46
+ "data-shadcn--navigation-menu-skip-delay-value" => skip_delay_duration
47
+ }.merge(defaults))
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module NavigationMenu
5
+ module Content
6
+ # A panel. It stays inside its own item — see the root for why — so every
7
+ # class here that reads `group-data-[viewport=false]/navigation-menu:` is
8
+ # the one doing the work: the border, the surface, the shadow and the
9
+ # zoom are all in that half of the string.
10
+ #
11
+ # `data-motion` is the direction the panel arrives from, which the
12
+ # controller sets by comparing the item you left with the one you reached.
13
+ class Component < ApplicationViewComponent
14
+ slot_name :"navigation-menu-content"
15
+
16
+ style do
17
+ base {
18
+ "top-0 left-0 w-full p-2 pr-2.5 " \
19
+ "data-[motion=from-end]:slide-in-from-right-52 " \
20
+ "data-[motion=from-start]:slide-in-from-left-52 " \
21
+ "data-[motion=to-end]:slide-out-to-right-52 " \
22
+ "data-[motion=to-start]:slide-out-to-left-52 " \
23
+ "data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in " \
24
+ "data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out " \
25
+ "md:absolute md:w-auto " \
26
+ "group-data-[viewport=false]/navigation-menu:top-full " \
27
+ "group-data-[viewport=false]/navigation-menu:mt-1.5 " \
28
+ "group-data-[viewport=false]/navigation-menu:overflow-hidden " \
29
+ "group-data-[viewport=false]/navigation-menu:rounded-md " \
30
+ "group-data-[viewport=false]/navigation-menu:border " \
31
+ "group-data-[viewport=false]/navigation-menu:bg-popover " \
32
+ "group-data-[viewport=false]/navigation-menu:text-popover-foreground " \
33
+ "group-data-[viewport=false]/navigation-menu:shadow " \
34
+ "group-data-[viewport=false]/navigation-menu:duration-200 " \
35
+ "**:data-[slot=navigation-menu-link]:focus:ring-0 " \
36
+ "**:data-[slot=navigation-menu-link]:focus:outline-none " \
37
+ "group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out " \
38
+ "group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 " \
39
+ "group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 " \
40
+ "group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in " \
41
+ "group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 " \
42
+ "group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95"
43
+ }
44
+ end
45
+
46
+ def element_attributes(**defaults)
47
+ super(**{
48
+ hidden: true,
49
+ "data-state" => "closed",
50
+ "data-shadcn--navigation-menu-target" => "content",
51
+ "data-action" => "pointerenter->shadcn--navigation-menu#pointerEnter " \
52
+ "pointerleave->shadcn--navigation-menu#pointerLeave"
53
+ }.merge(defaults))
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module NavigationMenu
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,49 @@
1
+ <%# Upstream's example, in the one configuration this port reproduces: each
2
+ panel stays inside its own item rather than being moved into a shared
3
+ viewport. shadcn supports both and ships the classes for this one — see
4
+ `features/navigation-menu.md` for why the other cannot be done here.
5
+
6
+ Three items so the panels have somewhere to slide *from*: `data-motion` is
7
+ computed by comparing the item you left with the one you reached, and with
8
+ two the direction is the same either way. The third item is a plain link,
9
+ which is what `Trigger::Component.new.css_classes` is for — upstream
10
+ exports the same string as `navigationMenuTriggerStyle`. %>
11
+ <div class="flex h-72 items-start justify-center pt-6">
12
+ <%= render(Shadcn::NavigationMenu::Component.new) do %>
13
+ <%= render(Shadcn::NavigationMenu::List::Component.new) do %>
14
+ <% {
15
+ "Getting started" => [
16
+ [ "Introduction", "Re-usable components built with Radix and Tailwind." ],
17
+ [ "Installation", "How to install dependencies and structure your app." ]
18
+ ],
19
+ "Components" => [
20
+ [ "Accordion", "A vertically stacked set of interactive headings." ],
21
+ [ "Dialog", "A window overlaid on the primary window." ]
22
+ ]
23
+ }.each do |label, links| %>
24
+ <%= render(Shadcn::NavigationMenu::Item::Component.new) do %>
25
+ <%= render(Shadcn::NavigationMenu::Trigger::Component.new) { label } %>
26
+ <%= render(Shadcn::NavigationMenu::Content::Component.new(class: "md:w-[400px]")) do %>
27
+ <ul class="grid gap-1">
28
+ <% links.each do |title, blurb| %>
29
+ <li>
30
+ <%= render(Shadcn::NavigationMenu::Link::Component.new(as: :a, href: "#")) do %>
31
+ <div class="text-sm leading-none font-medium"><%= title %></div>
32
+ <p class="text-sm leading-snug text-muted-foreground"><%= blurb %></p>
33
+ <% end %>
34
+ </li>
35
+ <% end %>
36
+ </ul>
37
+ <% end %>
38
+ <% end %>
39
+ <% end %>
40
+
41
+ <%= render(Shadcn::NavigationMenu::Item::Component.new) do %>
42
+ <%= render(Shadcn::NavigationMenu::Link::Component.new(
43
+ as: :a, href: "#",
44
+ class: Shadcn::NavigationMenu::Trigger::Component.new.css_classes
45
+ )) { "Documentation" } %>
46
+ <% end %>
47
+ <% end %>
48
+ <% end %>
49
+ </div>
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module NavigationMenu
5
+ module Trigger
6
+ # The button that opens a panel. Its classes are exported upstream as
7
+ # `navigationMenuTriggerStyle` so a plain link can borrow them; the same
8
+ # is available here as `Component.new.css_classes`, which is how the
9
+ # sidebar preview borrows the menu button's.
10
+ class Component < ApplicationViewComponent
11
+ default_tag :button
12
+ slot_name :"navigation-menu-trigger"
13
+
14
+ style do
15
+ base {
16
+ "group inline-flex h-9 w-max items-center justify-center rounded-md " \
17
+ "bg-background px-4 py-2 text-sm font-medium transition-[color,box-shadow] " \
18
+ "outline-none hover:bg-accent hover:text-accent-foreground " \
19
+ "focus:bg-accent focus:text-accent-foreground focus-visible:ring-[3px] " \
20
+ "focus-visible:ring-ring/50 focus-visible:outline-1 " \
21
+ "disabled:pointer-events-none disabled:opacity-50 " \
22
+ "data-[state=open]:bg-accent/50 data-[state=open]:text-accent-foreground " \
23
+ "data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent"
24
+ }
25
+ end
26
+
27
+ def element_attributes(**defaults)
28
+ super(**{
29
+ type: "button",
30
+ "data-state" => "closed",
31
+ "aria-expanded" => "false",
32
+ "data-shadcn--navigation-menu-target" => "trigger",
33
+ "data-action" => "pointerenter->shadcn--navigation-menu#pointerEnter " \
34
+ "pointerleave->shadcn--navigation-menu#pointerLeave " \
35
+ "click->shadcn--navigation-menu#toggle " \
36
+ "keydown->shadcn--navigation-menu#keydown"
37
+ }.merge(defaults))
38
+ end
39
+
40
+ # The chevron is upstream's, and so is the space before it: shadcn
41
+ # writes `{children}{" "}` rather than a gap utility (navigation-menu.tsx:76).
42
+ def call
43
+ render_element(
44
+ body: safe_join([
45
+ content,
46
+ " ",
47
+ render(Shadcn::Icon::Component.new(
48
+ "chevron-down",
49
+ class: "relative top-[1px] ml-1 size-3 transition duration-300 " \
50
+ "group-data-[state=open]:rotate-180",
51
+ "aria-hidden": true
52
+ ))
53
+ ])
54
+ )
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ # The parts of the NavigationMenu family that are an element with a
5
+ # `data-slot` and a fixed set of classes.
6
+ module NavigationMenu
7
+ extend Parts
8
+
9
+ part :list, slot: "navigation-menu-list", tag: :ul,
10
+ classes: "group flex flex-1 list-none items-center justify-center gap-1"
11
+
12
+ part :item, slot: "navigation-menu-item", tag: :li, classes: "relative"
13
+
14
+ # The rows inside a panel. `data-active` is the caller's — this is a nav, so
15
+ # which link is current is the application's business, not the menu's.
16
+ part :link, slot: "navigation-menu-link",
17
+ classes: "flex flex-col gap-1 rounded-sm p-2 text-sm transition-all " \
18
+ "outline-none hover:bg-accent hover:text-accent-foreground " \
19
+ "focus:bg-accent focus:text-accent-foreground " \
20
+ "focus-visible:ring-[3px] focus-visible:ring-ring/50 " \
21
+ "focus-visible:outline-1 data-[active=true]:bg-accent/50 " \
22
+ "data-[active=true]:text-accent-foreground " \
23
+ "data-[active=true]:hover:bg-accent " \
24
+ "data-[active=true]:focus:bg-accent " \
25
+ "[&_svg:not([class*='size-'])]:size-4 " \
26
+ "[&_svg:not([class*='text-'])]:text-muted-foreground"
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Pagination
5
+ # Port of registry/new-york-v4/ui/pagination.tsx
6
+ class Component < ApplicationViewComponent
7
+ default_tag :nav
8
+ slot_name :pagination
9
+
10
+ style do
11
+ base { "mx-auto flex w-full justify-center" }
12
+ end
13
+
14
+ def element_attributes(**defaults)
15
+ super(**{ role: "navigation", "aria-label" => shadcn_t("pagination.label") }.merge(defaults))
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Pagination
5
+ module Ellipsis
6
+ # PaginationEllipsis
7
+ class Component < ApplicationViewComponent
8
+ default_tag :span
9
+ slot_name :"pagination-ellipsis"
10
+
11
+ style do
12
+ base { "flex size-9 items-center justify-center" }
13
+ end
14
+
15
+ def element_attributes(**defaults)
16
+ super(**{ "aria-hidden" => true }.merge(defaults))
17
+ end
18
+
19
+ def call
20
+ render_element(body: safe_join([
21
+ render(Icon::Component.new("more-horizontal", class: "size-4")),
22
+ tag.span(shadcn_t("pagination.more"), class: "sr-only")
23
+ ]))
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end