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,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module RadioGroup
5
+ module Item
6
+ # RadioGroupItem
7
+ class Component < ApplicationViewComponent
8
+ INDICATOR_CLASSES = "relative flex items-center justify-center"
9
+ DOT_CLASSES = "absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 " \
10
+ "fill-primary"
11
+
12
+ default_tag :button
13
+ slot_name :"radio-group-item"
14
+
15
+ style do
16
+ base {
17
+ "aspect-square size-4 shrink-0 rounded-full border border-input text-primary " \
18
+ "shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring " \
19
+ "focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed " \
20
+ "disabled:opacity-50 aria-invalid:border-destructive " \
21
+ "aria-invalid:ring-destructive/20 dark:bg-input/30 " \
22
+ "dark:aria-invalid:ring-destructive/40"
23
+ }
24
+ end
25
+
26
+ attr_reader :value, :checked, :disabled
27
+
28
+ def initialize(value:, checked: false, disabled: false, **attributes)
29
+ @value = value.to_s
30
+ @checked = checked
31
+ @disabled = disabled
32
+ super(**attributes)
33
+ end
34
+
35
+ def state
36
+ checked ? "checked" : "unchecked"
37
+ end
38
+
39
+ def element_attributes(**defaults)
40
+ super(**{
41
+ type: "button",
42
+ role: "radio",
43
+ value: value,
44
+ disabled: (true if disabled),
45
+ tabindex: (checked ? "0" : "-1"),
46
+ "aria-checked" => checked.to_s,
47
+ "data-state" => state,
48
+ "data-value" => value,
49
+ "data-disabled" => (true if disabled),
50
+ "data-shadcn--radio-group-target" => "item",
51
+ "data-action" => "click->shadcn--radio-group#select " \
52
+ "keydown->shadcn--radio-group#keydown"
53
+ }.merge(defaults))
54
+ end
55
+
56
+ def call
57
+ render_element(body: indicator)
58
+ end
59
+
60
+ private
61
+
62
+ def indicator
63
+ tag.span(
64
+ render(Icon::Component.new("circle", class: DOT_CLASSES)),
65
+ "data-slot": "radio-group-indicator",
66
+ "data-state": state,
67
+ hidden: (true unless checked),
68
+ class: INDICATOR_CLASSES
69
+ )
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module RadioGroup
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+
10
+ # Upstream's "Disabled" and "Invalid".
11
+ def states
12
+ render_with_template
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ <%# The items are rendered in the *block*, not through the `item` slot.
2
+
3
+ Slot content is emitted before block content, so an item added through the
4
+ slot is hoisted out of the row it was written in and lands above every
5
+ label — which is what this preview did until someone looked at the page.
6
+ `FormBuilder#shadcn_radio_group` renders them in the block for the same
7
+ reason and says so. %>
8
+ <%= render(Shadcn::RadioGroup::Component.new(name: "plan", value: "comfortable")) do %>
9
+ <% [["default", "Default"], ["comfortable", "Comfortable"], ["compact", "Compact"]].each do |value, text| %>
10
+ <div class="flex items-center gap-2">
11
+ <%= render(Shadcn::RadioGroup::Item::Component.new(value:, checked: value == "comfortable", id: "r-#{value}")) %>
12
+ <%= render(Shadcn::Label::Component.new(for: "r-#{value}")) { text } %>
13
+ </div>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,41 @@
1
+ <%# Upstream's "Disabled" and "Invalid". Items rendered in the block rather than
2
+ through the slot — see the default preview for why. Disabled comes in two sizes here — the
3
+ whole group, and one option of an otherwise usable group — because they are
4
+ different things and only the second is a choice you can see and not make. %>
5
+ <div class="flex flex-col gap-8">
6
+ <div class="flex flex-col gap-2">
7
+ <div class="text-sm font-medium">One option is unavailable</div>
8
+ <%= render(Shadcn::RadioGroup::Component.new(name: "shipping", value: "standard")) do %>
9
+ <% [["standard", "Standard", false], ["express", "Express", false], ["same-day", "Same day — sold out", true]].each do |value, text, disabled| %>
10
+ <div class="flex items-center gap-2">
11
+ <%= render(Shadcn::RadioGroup::Item::Component.new(value:, checked: value == "standard", disabled:, id: "rs-#{value}")) %>
12
+ <%= render(Shadcn::Label::Component.new(for: "rs-#{value}")) { text } %>
13
+ </div>
14
+ <% end %>
15
+ <% end %>
16
+ </div>
17
+
18
+ <div class="flex flex-col gap-2">
19
+ <div class="text-sm font-medium">The whole group is disabled</div>
20
+ <%= render(Shadcn::RadioGroup::Component.new(name: "billing", value: "monthly", disabled: true)) do %>
21
+ <% [["monthly", "Monthly"], ["yearly", "Yearly"]].each do |value, text| %>
22
+ <div class="flex items-center gap-2">
23
+ <%= render(Shadcn::RadioGroup::Item::Component.new(value:, checked: value == "monthly", id: "rb-#{value}")) %>
24
+ <%= render(Shadcn::Label::Component.new(for: "rb-#{value}")) { text } %>
25
+ </div>
26
+ <% end %>
27
+ <% end %>
28
+ </div>
29
+
30
+ <div class="flex flex-col gap-2">
31
+ <div class="text-sm font-medium">Nothing chosen, and something had to be</div>
32
+ <%= render(Shadcn::RadioGroup::Component.new(name: "size", "aria-invalid": "true")) do %>
33
+ <% [["s", "Small"], ["m", "Medium"]].each do |value, text| %>
34
+ <div class="flex items-center gap-2">
35
+ <%= render(Shadcn::RadioGroup::Item::Component.new(value:, id: "rz-#{value}", "aria-invalid": "true")) %>
36
+ <%= render(Shadcn::Label::Component.new(for: "rz-#{value}")) { text } %>
37
+ </div>
38
+ <% end %>
39
+ <% end %>
40
+ </div>
41
+ </div>
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Resizable
5
+ # Port of registry/new-york-v4/ui/resizable.tsx, whose behaviour is
6
+ # `react-resizable-panels` — 2,252 lines of compiled JavaScript and no
7
+ # dependencies of its own.
8
+ #
9
+ # What the package supplies is a drag, a keyboard and some arithmetic over
10
+ # `flex-grow`: panels are proportions of a flex container, so the layout is
11
+ # the browser's and the port only has to move two numbers when a handle is
12
+ # dragged. See [features/resizable.md](../../../../.claude/docs/features/resizable.md).
13
+ #
14
+ # Panels and handles are written in the block, in order, because that is
15
+ # what a group is — `panel, handle, panel` — and because slot content is
16
+ # emitted before block content, which would gather all the panels above all
17
+ # the handles.
18
+ class Component < ApplicationViewComponent
19
+ ORIENTATIONS = %i[horizontal vertical].freeze
20
+
21
+ default_tag :div
22
+ slot_name :"resizable-panel-group"
23
+
24
+ style do
25
+ base { "flex h-full w-full aria-[orientation=vertical]:flex-col" }
26
+ end
27
+
28
+ attr_reader :orientation
29
+
30
+ # `orientation:` is upstream's `direction`, renamed to what the DOM calls
31
+ # it: the class above reads `aria-orientation`, and a group that says
32
+ # `direction` in Ruby and `orientation` in HTML is one name too many.
33
+ def initialize(orientation: :horizontal, **attributes)
34
+ @orientation = ORIENTATIONS.include?(orientation&.to_sym) ? orientation.to_sym : :horizontal
35
+ super(**attributes)
36
+ end
37
+
38
+ # No `aria-orientation`, and the direction is an inline style — which is
39
+ # what upstream's own DOM does, and was arrived at the hard way.
40
+ #
41
+ # The class above reads `aria-[orientation=vertical]:flex-col`, so the
42
+ # obvious port writes that attribute; axe then refuses it, because no role
43
+ # a plain group can carry supports `aria-orientation`. Reading what
44
+ # `react-resizable-panels` v4 actually renders settles it: the group has
45
+ # no role and no `aria-orientation` at all, and sets `flex-direction`
46
+ # inline. **The class is vestigial upstream too** — it reads an attribute
47
+ # the package stopped rendering. It is kept because `parity_spec` compares
48
+ # what upstream emits, and dropping it would claim a divergence that is
49
+ # not one.
50
+ def element_attributes(**defaults)
51
+ super(**{
52
+ "data-controller" => "shadcn--resizable",
53
+ "data-shadcn--resizable-orientation-value" => orientation,
54
+ style: merged_style(("flex-direction:column" if orientation == :vertical))
55
+ }.compact.merge(defaults))
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Resizable
5
+ module Handle
6
+ # The bar between two panels: a `role="separator"` that can be dragged and
7
+ # driven from the keyboard.
8
+ #
9
+ # Its own orientation is the *opposite* of the group's — a row of panels
10
+ # is divided by a vertical line — and `role="separator"` defaults to
11
+ # horizontal, so the attribute is always written rather than left out.
12
+ class Component < ApplicationViewComponent
13
+ GRIP_CLASSES = "z-10 flex h-4 w-3 items-center justify-center rounded-xs border bg-border"
14
+
15
+ default_tag :div
16
+ slot_name :"resizable-handle"
17
+
18
+ style do
19
+ base {
20
+ "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 " \
21
+ "after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-ring " \
22
+ "focus-visible:ring-offset-1 focus-visible:outline-hidden aria-[orientation=horizontal]:h-px " \
23
+ "aria-[orientation=horizontal]:w-full aria-[orientation=horizontal]:after:left-0 " \
24
+ "aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full " \
25
+ "aria-[orientation=horizontal]:after:translate-x-0 " \
26
+ "aria-[orientation=horizontal]:after:-translate-y-1/2 " \
27
+ "[&[aria-orientation=horizontal]>div]:rotate-90"
28
+ }
29
+ end
30
+
31
+ attr_reader :with_handle, :orientation, :disabled
32
+
33
+ # `with_handle:` is upstream's `withHandle`: the grip a pointer can see,
34
+ # which is off by default there too.
35
+ def initialize(with_handle: false, orientation: :horizontal, disabled: false, **attributes)
36
+ @with_handle = with_handle
37
+ @orientation = orientation&.to_sym == :vertical ? :vertical : :horizontal
38
+ @disabled = disabled
39
+ super(**attributes)
40
+ end
41
+
42
+ def element_attributes(**defaults)
43
+ super(**{
44
+ role: "separator",
45
+ # The group's orientation decides the separator's, inverted.
46
+ "aria-orientation" => orientation == :vertical ? "horizontal" : "vertical",
47
+ "aria-disabled" => (disabled.presence && "true"),
48
+ tabindex: (disabled ? "-1" : "0"),
49
+ "data-separator" => "inactive",
50
+ "data-shadcn--resizable-target" => "handle",
51
+ "data-action" => "pointerdown->shadcn--resizable#press keydown->shadcn--resizable#keydown",
52
+ style: merged_style("flex-basis:auto;flex-grow:0;flex-shrink:0")
53
+ }.compact.merge(defaults))
54
+ end
55
+
56
+ def call
57
+ render_element(body: grip)
58
+ end
59
+
60
+ private
61
+
62
+ def grip
63
+ return unless with_handle
64
+
65
+ tag.div(render(Icon::Component.new("grip-vertical", class: "size-2.5")), class: GRIP_CLASSES)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Resizable
5
+ module Panel
6
+ # One panel. Upstream carries no classes at all — the package writes an
7
+ # inline style — so what is reproduced here is that style, minus the part
8
+ # of it that resets borders and padding the package cannot know a caller
9
+ # wants.
10
+ #
11
+ # A size is a share, not a pixel: `flex-grow` against `flex-basis: 0` is
12
+ # exactly the proportional layout the package computes, which is why the
13
+ # browser does the resizing and the controller only writes two numbers.
14
+ class Component < ApplicationViewComponent
15
+ default_tag :div
16
+ slot_name :"resizable-panel"
17
+
18
+ attr_reader :default_size, :min_size, :max_size
19
+
20
+ # Sizes are percentages, as upstream's are. Give every panel one or give
21
+ # none: with a mix, the ones without would each take a single share
22
+ # against another's twenty-five, which is not what anybody means.
23
+ def initialize(default_size: nil, min_size: nil, max_size: nil, **attributes)
24
+ @default_size = default_size
25
+ @min_size = min_size
26
+ @max_size = max_size
27
+ super(**attributes)
28
+ end
29
+
30
+ def element_attributes(**defaults)
31
+ super(**{
32
+ id: panel_id,
33
+ "data-shadcn--resizable-target" => "panel",
34
+ "data-min-size" => min_size,
35
+ "data-max-size" => max_size,
36
+ style: merged_style(
37
+ "flex-basis:0;flex-shrink:1;flex-grow:#{default_size || 1};overflow:hidden;" \
38
+ "display:flex;min-width:0;min-height:0"
39
+ )
40
+ }.compact.merge(defaults))
41
+ end
42
+
43
+ private
44
+
45
+ # A handle points at the panel before it with `aria-controls`, so a
46
+ # panel needs a name of its own even when the caller gives it none.
47
+ def panel_id
48
+ attributes[:id] || "shadcn-resizable-panel-#{SecureRandom.hex(4)}"
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Resizable
5
+ class Preview < ApplicationViewComponentPreview
6
+ # Upstream's own first example: two panels side by side.
7
+ def default
8
+ render_with_template
9
+ end
10
+
11
+ # Upstream's "Vertical".
12
+ def vertical
13
+ render_with_template
14
+ end
15
+
16
+ # Upstream's "With Handle", and a nested group — a row inside a column,
17
+ # which is the layout the docs use to show both at once.
18
+ def with_handle
19
+ render_with_template
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ <%# Upstream's own example. Panels and handles go in the block, in order — a
2
+ group *is* `panel, handle, panel` — and sizes are percentages. %>
3
+ <%= render(Shadcn::Resizable::Component.new(class: "max-w-md rounded-lg border md:min-w-[450px]")) do %>
4
+ <%= render(Shadcn::Resizable::Panel::Component.new(default_size: 50)) do %>
5
+ <div class="flex h-[200px] items-center justify-center p-6">
6
+ <span class="font-semibold">One</span>
7
+ </div>
8
+ <% end %>
9
+ <%= render(Shadcn::Resizable::Handle::Component.new) %>
10
+ <%= render(Shadcn::Resizable::Panel::Component.new(default_size: 50)) do %>
11
+ <%= render(Shadcn::Resizable::Component.new(orientation: :vertical)) do %>
12
+ <%= render(Shadcn::Resizable::Panel::Component.new(default_size: 25)) do %>
13
+ <div class="flex h-full items-center justify-center p-6">
14
+ <span class="font-semibold">Two</span>
15
+ </div>
16
+ <% end %>
17
+ <%= render(Shadcn::Resizable::Handle::Component.new(orientation: :vertical)) %>
18
+ <%= render(Shadcn::Resizable::Panel::Component.new(default_size: 75)) do %>
19
+ <div class="flex h-full items-center justify-center p-6">
20
+ <span class="font-semibold">Three</span>
21
+ </div>
22
+ <% end %>
23
+ <% end %>
24
+ <% end %>
25
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <%# Upstream's "Vertical": the group's orientation decides both the layout and
2
+ the separator's own, which is the opposite of it — a column of panels is
3
+ divided by a horizontal line. %>
4
+ <%= render(Shadcn::Resizable::Component.new(
5
+ orientation: :vertical,
6
+ class: "min-h-[200px] max-w-md rounded-lg border"
7
+ )) do %>
8
+ <%= render(Shadcn::Resizable::Panel::Component.new(default_size: 25)) do %>
9
+ <div class="flex h-full items-center justify-center p-6">
10
+ <span class="font-semibold">Header</span>
11
+ </div>
12
+ <% end %>
13
+ <%= render(Shadcn::Resizable::Handle::Component.new(orientation: :vertical)) %>
14
+ <%= render(Shadcn::Resizable::Panel::Component.new(default_size: 75)) do %>
15
+ <div class="flex h-full items-center justify-center p-6">
16
+ <span class="font-semibold">Content</span>
17
+ </div>
18
+ <% end %>
19
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <%# Upstream's "With Handle": the grip a pointer can see. Off by default there
2
+ too — a handle is a hit area whether or not it is drawn.
3
+
4
+ `min_size:` and `max_size:` are percentages, and the drag stops at them. %>
5
+ <%= render(Shadcn::Resizable::Component.new(class: "min-h-[200px] max-w-md rounded-lg border")) do %>
6
+ <%= render(Shadcn::Resizable::Panel::Component.new(default_size: 25, min_size: 15, max_size: 60)) do %>
7
+ <div class="flex h-full items-center justify-center p-6">
8
+ <span class="font-semibold">Sidebar</span>
9
+ </div>
10
+ <% end %>
11
+ <%= render(Shadcn::Resizable::Handle::Component.new(with_handle: true)) %>
12
+ <%= render(Shadcn::Resizable::Panel::Component.new(default_size: 75)) do %>
13
+ <div class="flex h-full items-center justify-center p-6">
14
+ <span class="font-semibold">Content</span>
15
+ </div>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module ScrollArea
5
+ # Port of registry/new-york-v4/ui/scroll-area.tsx, whose behaviour is Radix's
6
+ # `ScrollArea` — 1,189 lines against shadcn's 58, and vendored at
7
+ # `vendor/radix/ui/scroll-area.tsx`.
8
+ #
9
+ # Like shadcn's own, this composes the whole thing rather than exposing the
10
+ # parts: the viewport, one scrollbar per requested axis, and the corner where
11
+ # two of them meet. `orientation:` is this port's way of asking for the
12
+ # second bar, which shadcn does by passing a second `<ScrollBar>` as a child
13
+ # — a shape that relies on React's Slottable to hoist it out of the viewport
14
+ # and has no counterpart here.
15
+ #
16
+ # Two of Radix's four `type` strategies are reproduced: `hover`, its default,
17
+ # and `always`. See features/scroll-area.md.
18
+ class Component < ApplicationViewComponent
19
+ slot_name :"scroll-area"
20
+
21
+ style do
22
+ base { "relative" }
23
+ end
24
+
25
+ attr_reader :orientation, :type, :scroll_hide_delay
26
+
27
+ def initialize(orientation: :vertical, type: :hover, scroll_hide_delay: 600, **attributes)
28
+ @orientation = orientation&.to_sym || :vertical
29
+ @type = type&.to_sym || :hover
30
+ @scroll_hide_delay = scroll_hide_delay
31
+ super(**attributes)
32
+ end
33
+
34
+ def element_attributes(**defaults)
35
+ super(**{
36
+ "data-controller" => "shadcn--scroll-area",
37
+ "data-shadcn--scroll-area-type-value" => type,
38
+ "data-shadcn--scroll-area-hide-delay-value" => scroll_hide_delay,
39
+ "data-action" => "pointerenter->shadcn--scroll-area#pointerEnter " \
40
+ "pointerleave->shadcn--scroll-area#pointerLeave"
41
+ }.merge(defaults))
42
+ end
43
+
44
+ def call
45
+ render_element(body: safe_join([ viewport, *scrollbars, corner ].compact))
46
+ end
47
+
48
+ private
49
+
50
+ def horizontal? = [ :horizontal, :both ].include?(orientation)
51
+ def vertical? = [ :vertical, :both ].include?(orientation)
52
+
53
+ def viewport
54
+ render(
55
+ Shadcn::ScrollArea::Viewport::Component.new(horizontal: horizontal?, vertical: vertical?)
56
+ ) { content }
57
+ end
58
+
59
+ def scrollbars
60
+ [
61
+ (bar(:vertical) if vertical?),
62
+ (bar(:horizontal) if horizontal?)
63
+ ]
64
+ end
65
+
66
+ def bar(axis)
67
+ render(Shadcn::ScrollArea::Scrollbar::Component.new(orientation: axis))
68
+ end
69
+
70
+ # Only where two bars meet. Radix sizes it from the two bars' thicknesses
71
+ # and publishes them as the custom properties each bar stops short at.
72
+ def corner
73
+ return unless horizontal? && vertical?
74
+
75
+ tag.div(
76
+ style: "position: absolute; right: 0; bottom: 0; " \
77
+ "width: var(--radix-scroll-area-corner-width, 0px); " \
78
+ "height: var(--radix-scroll-area-corner-height, 0px);",
79
+ "data-shadcn--scroll-area-target": "corner"
80
+ )
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module ScrollArea
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ <%# Upstream's example: a bordered box of tags, taller than it is allowed to be.
2
+
3
+ The scrollbar is `type: :hover` by default — Radix's default too — so it is
4
+ not there until the pointer is. Radix *unmounts* it; nothing is unmounted
5
+ here, so it ships with `data-state="hidden"` and the controller moves the
6
+ attribute.
7
+
8
+ The second one asks for both axes, which is what puts a corner between the
9
+ two bars: each stops short of the other by exactly its thickness, and the
10
+ corner fills what is left. `always` on that one so both are visible in a
11
+ screenshot without a pointer in it. %>
12
+ <div class="flex gap-6">
13
+ <%= render(Shadcn::ScrollArea::Component.new(class: "h-64 w-48 rounded-md border")) do %>
14
+ <div class="p-4">
15
+ <h4 class="mb-4 text-sm leading-none font-medium">Tags</h4>
16
+ <% 24.times do |index| %>
17
+ <div class="text-sm">v1.2.0-beta.<%= 24 - index %></div>
18
+ <%= render(Shadcn::Separator::Component.new(class: "my-2")) %>
19
+ <% end %>
20
+ </div>
21
+ <% end %>
22
+
23
+ <%= render(Shadcn::ScrollArea::Component.new(
24
+ orientation: :both, type: :always, class: "h-64 w-72 rounded-md border"
25
+ )) do %>
26
+ <div class="w-max p-4">
27
+ <% 20.times do |row| %>
28
+ <div class="flex gap-4 py-1 text-sm whitespace-nowrap">
29
+ <% 6.times do |column| %>
30
+ <span class="w-24">cell <%= row + 1 %>.<%= column + 1 %></span>
31
+ <% end %>
32
+ </div>
33
+ <% end %>
34
+ </div>
35
+ <% end %>
36
+ </div>
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module ScrollArea
5
+ module Scrollbar
6
+ # Radix's `ScrollAreaScrollbar`, and shadcn's `ScrollBar`.
7
+ #
8
+ # `data-state` is `hidden` from the server and the controller moves it.
9
+ # Radix *unmounts* the bar instead — with `type="hover"`, the default, it
10
+ # is not in the DOM until the pointer arrives (measured on the live demo).
11
+ # Nothing is unmounted here for the same reason nothing is portalled, so
12
+ # the state attribute carries what a missing element carried there.
13
+ class Component < ApplicationViewComponent
14
+ slot_name :"scroll-area-scrollbar"
15
+
16
+ style do
17
+ base { "flex touch-none p-px transition-colors select-none" }
18
+
19
+ variants {
20
+ orientation {
21
+ vertical { "h-full w-2.5 border-l border-l-transparent" }
22
+ horizontal { "h-2.5 flex-col border-t border-t-transparent" }
23
+ }
24
+ }
25
+
26
+ defaults { { orientation: :vertical } }
27
+ end
28
+
29
+ attr_reader :orientation
30
+
31
+ def initialize(orientation: :vertical, **attributes)
32
+ @orientation = orientation&.to_sym || :vertical
33
+ super(**attributes)
34
+ end
35
+
36
+ def style_variants
37
+ { orientation: }
38
+ end
39
+
40
+ # The inline geometry is Radix's: the bar is absolutely positioned
41
+ # against the root and stops short of the corner, and the thumb's size
42
+ # travels as a custom property rather than as a width the controller
43
+ # writes on the thumb itself (measured on the live demo).
44
+ def element_attributes(**defaults)
45
+ super(**{
46
+ style: merged_style(position_style),
47
+ "data-orientation" => orientation,
48
+ "data-state" => "hidden",
49
+ "data-shadcn--scroll-area-target" => "scrollbar",
50
+ "data-action" => "pointerdown->shadcn--scroll-area#startDrag"
51
+ }.merge(defaults))
52
+ end
53
+
54
+ def call
55
+ render_element(body: content.presence || render(Shadcn::ScrollArea::Thumb::Component.new))
56
+ end
57
+
58
+ private
59
+
60
+ def position_style
61
+ if orientation == :vertical
62
+ "position: absolute; top: 0; right: 0; " \
63
+ "bottom: var(--radix-scroll-area-corner-height, 0px);"
64
+ else
65
+ "position: absolute; bottom: 0; left: 0; " \
66
+ "right: var(--radix-scroll-area-corner-width, 0px);"
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end