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,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Sidebar
5
+ module Rail
6
+ # SidebarRail — the thin strip along the panel's edge that toggles it, and
7
+ # shows a resize cursor while doing so. `tabindex="-1"` is upstream's:
8
+ # the rail duplicates the trigger, so it is deliberately out of the tab
9
+ # order (vendor/shadcn/ui/sidebar.tsx:282-305).
10
+ class Component < ApplicationViewComponent
11
+ default_tag :button
12
+ slot_name :"sidebar-rail"
13
+
14
+ style do
15
+ base {
16
+ "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear " \
17
+ "group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute " \
18
+ "after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex " \
19
+ "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize " \
20
+ "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize " \
21
+ "[[data-side=right][data-state=collapsed]_&]:cursor-w-resize " \
22
+ "group-data-[collapsible=offcanvas]:translate-x-0 " \
23
+ "group-data-[collapsible=offcanvas]:after:left-full " \
24
+ "hover:group-data-[collapsible=offcanvas]:bg-sidebar " \
25
+ "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2 " \
26
+ "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2"
27
+ }
28
+ end
29
+
30
+ def element_attributes(**defaults)
31
+ super(**{
32
+ type: "button",
33
+ "data-sidebar" => "rail",
34
+ "aria-label" => shadcn_t("sidebar.toggle"),
35
+ "tabindex" => "-1",
36
+ title: shadcn_t("sidebar.toggle"),
37
+ "data-action" => "click->shadcn--sidebar#toggle"
38
+ }.merge(defaults))
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Sidebar
5
+ module Separator
6
+ # SidebarSeparator — the ported Separator, restamped and tinted to the
7
+ # sidebar's own border colour.
8
+ class Component < Shadcn::Separator::Component
9
+ slot_name :"sidebar-separator"
10
+
11
+ EXTRA_CLASSES = "mx-2 w-auto bg-sidebar-border"
12
+
13
+ def css_classes(extra = nil)
14
+ super([ EXTRA_CLASSES, extra ].compact.join(" "))
15
+ end
16
+
17
+ def element_attributes(**defaults)
18
+ super(**{ "data-sidebar" => "separator" }.merge(defaults))
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Sidebar
5
+ module Trigger
6
+ # SidebarTrigger — upstream renders a ghost icon Button
7
+ # (vendor/shadcn/ui/sidebar.tsx:256-280), so this borrows the button's own
8
+ # compiled variants rather than restating them, as PaginationLink does.
9
+ class Component < ApplicationViewComponent
10
+ default_tag :button
11
+ slot_name :"sidebar-trigger"
12
+
13
+ def element_attributes(**defaults)
14
+ super(**{
15
+ type: "button",
16
+ "data-sidebar" => "trigger",
17
+ "data-shadcn--sidebar-target" => "trigger",
18
+ "data-action" => "click->shadcn--sidebar#toggle"
19
+ }.merge(defaults))
20
+ end
21
+
22
+ def css_classes(extra = nil)
23
+ Button::Component.variant_classes(variant: :ghost, size: :icon, class: [ "size-7", extra ].compact.join(" "))
24
+ end
25
+
26
+ # The label is `sr-only` rather than an `aria-label`: upstream puts it in
27
+ # the button's content, and a visible-to-screen-readers span survives
28
+ # translation tooling that an attribute does not.
29
+ def call
30
+ render_element(body: safe_join([
31
+ render(Icon::Component.new("panel-left")),
32
+ tag.span(shadcn_t("sidebar.toggle"), class: "sr-only"),
33
+ content
34
+ ].compact))
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ # The parts of the Sidebar family that are an element with its two attributes
5
+ # and a fixed set of classes. Anything with variants, slots or markup of its
6
+ # own keeps its own `component.rb`.
7
+ #
8
+ # This family needs a macro of its own because shadcn stamps a *second*
9
+ # attribute on every one of its 24 parts — `data-sidebar="header"` beside
10
+ # `data-slot="sidebar-header"` — and `part` declares a slot, classes and a tag
11
+ # and deliberately nothing else. Taken literally that meant 23 files of eleven
12
+ # lines each, which is precisely what `part` was written to remove.
13
+ #
14
+ # `sidebar_part` is not `part` widened to take attributes; that was considered
15
+ # and rejected in decisions/01-architecture.md, on the grounds that it turns a
16
+ # lookup table into a configuration language. It adds exactly one attribute and
17
+ # *derives* it: measured across the whole vendored source, `data-sidebar` is
18
+ # `data-slot` without its `sidebar-` prefix in 21 of 21 cases, no exceptions.
19
+ # A rule, not a parameter. If upstream ever breaks it, one file changes.
20
+ module Sidebar
21
+ extend Parts
22
+
23
+ # Declares a part and stamps the derived `data-sidebar` on it. Caller
24
+ # attributes still win, as they do everywhere else here.
25
+ def self.sidebar_part(name, slot:, **options)
26
+ marker = slot.to_s.delete_prefix("sidebar-")
27
+
28
+ part(name, slot:, **options).tap do |component|
29
+ component.define_method(:element_attributes) do |**defaults|
30
+ super(**{ "data-sidebar" => marker }.merge(defaults))
31
+ end
32
+ end
33
+ end
34
+
35
+ sidebar_part :header, slot: "sidebar-header", classes: "flex flex-col gap-2 p-2"
36
+
37
+ sidebar_part :footer, slot: "sidebar-footer", classes: "flex flex-col gap-2 p-2"
38
+
39
+ sidebar_part :content, slot: "sidebar-content",
40
+ classes: "flex min-h-0 flex-1 flex-col gap-2 overflow-auto " \
41
+ "group-data-[collapsible=icon]:overflow-hidden"
42
+
43
+ sidebar_part :group, slot: "sidebar-group",
44
+ classes: "relative flex w-full min-w-0 flex-col p-2"
45
+
46
+ sidebar_part :group_label, slot: "sidebar-group-label",
47
+ classes: "flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium " \
48
+ "text-sidebar-foreground/70 ring-sidebar-ring outline-hidden " \
49
+ "transition-[margin,opacity] duration-200 ease-linear " \
50
+ "focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0 " \
51
+ "group-data-[collapsible=icon]:-mt-8 " \
52
+ "group-data-[collapsible=icon]:opacity-0"
53
+
54
+ sidebar_part :group_action, slot: "sidebar-group-action", tag: :button,
55
+ classes: "absolute top-3.5 right-3 flex aspect-square w-5 items-center " \
56
+ "justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring " \
57
+ "outline-hidden transition-transform hover:bg-sidebar-accent " \
58
+ "hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 " \
59
+ "[&>svg]:shrink-0 after:absolute after:-inset-2 md:after:hidden " \
60
+ "group-data-[collapsible=icon]:hidden"
61
+
62
+ sidebar_part :group_content, slot: "sidebar-group-content",
63
+ classes: "w-full text-sm"
64
+
65
+ sidebar_part :menu, slot: "sidebar-menu", tag: :ul,
66
+ classes: "flex w-full min-w-0 flex-col gap-1"
67
+
68
+ sidebar_part :menu_item, slot: "sidebar-menu-item", tag: :li,
69
+ classes: "group/menu-item relative"
70
+
71
+ sidebar_part :menu_badge, slot: "sidebar-menu-badge",
72
+ classes: "pointer-events-none absolute right-1 flex h-5 min-w-5 items-center " \
73
+ "justify-center rounded-md px-1 text-xs font-medium " \
74
+ "text-sidebar-foreground tabular-nums select-none " \
75
+ "peer-hover/menu-button:text-sidebar-accent-foreground " \
76
+ "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground " \
77
+ "peer-data-[size=sm]/menu-button:top-1 " \
78
+ "peer-data-[size=default]/menu-button:top-1.5 " \
79
+ "peer-data-[size=lg]/menu-button:top-2.5 " \
80
+ "group-data-[collapsible=icon]:hidden"
81
+
82
+ sidebar_part :menu_sub, slot: "sidebar-menu-sub", tag: :ul,
83
+ classes: "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l " \
84
+ "border-sidebar-border px-2.5 py-0.5 group-data-[collapsible=icon]:hidden"
85
+
86
+ sidebar_part :menu_sub_item, slot: "sidebar-menu-sub-item", tag: :li,
87
+ classes: "group/menu-sub-item relative"
88
+
89
+ # The one part upstream stamps with `data-slot` alone, so it takes the plain
90
+ # macro and must not gain a `data-sidebar`.
91
+ part :inset, slot: "sidebar-inset", tag: :main,
92
+ classes: "relative flex w-full flex-1 flex-col bg-background " \
93
+ "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 " \
94
+ "md:peer-data-[variant=inset]:rounded-xl " \
95
+ "md:peer-data-[variant=inset]:shadow-sm " \
96
+ "md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2"
97
+ end
98
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Skeleton
5
+ # Port of registry/new-york-v4/ui/skeleton.tsx
6
+ class Component < ApplicationViewComponent
7
+ slot_name :skeleton
8
+
9
+ style do
10
+ base { "animate-pulse rounded-md bg-accent" }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Skeleton
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ <div class="flex items-center gap-4">
2
+ <%= render(Shadcn::Skeleton::Component.new(class: "size-12 rounded-full")) %>
3
+ <div class="flex flex-col gap-2">
4
+ <%= render(Shadcn::Skeleton::Component.new(class: "h-4 w-[250px]")) %>
5
+ <%= render(Shadcn::Skeleton::Component.new(class: "h-4 w-[200px]")) %>
6
+ </div>
7
+ </div>
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Slider
5
+ # Port of registry/new-york-v4/ui/slider.tsx, whose behaviour is Radix's
6
+ # `Slider` — 1,044 lines against shadcn's 63, vendored at
7
+ # `vendor/radix/ui/slider.tsx`.
8
+ #
9
+ # Composes the whole thing, as shadcn's own does: a track holding the filled
10
+ # range, and one thumb per value. Every position is computed here as well as
11
+ # in the controller, so the slider is in the right place before any
12
+ # JavaScript runs — the server knows the values, and a slider that starts at
13
+ # zero and jumps is worse than one that never moves.
14
+ class Component < ApplicationViewComponent
15
+ slot_name :slider
16
+
17
+ style do
18
+ base {
19
+ "relative flex w-full touch-none items-center select-none " \
20
+ "data-[disabled]:opacity-50 data-[orientation=vertical]:h-full " \
21
+ "data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto " \
22
+ "data-[orientation=vertical]:flex-col"
23
+ }
24
+ end
25
+
26
+ attr_reader :values, :min, :max, :step, :orientation, :disabled,
27
+ :min_steps_between_thumbs, :name, :aria_label
28
+
29
+ # `value:` takes a number or a list. Upstream's default when neither
30
+ # `value` nor `defaultValue` is given is `[min, max]` — two thumbs, which
31
+ # is surprising until you notice it is what makes the component's own
32
+ # example a range (slider.tsx:17-24).
33
+ def initialize(value: nil, min: 0, max: 100, step: 1, orientation: :horizontal,
34
+ disabled: false, min_steps_between_thumbs: 0, name: nil,
35
+ aria_label: nil, **attributes)
36
+ @min = min
37
+ @max = max
38
+ @step = step
39
+ @orientation = orientation&.to_sym || :horizontal
40
+ @disabled = disabled
41
+ @min_steps_between_thumbs = min_steps_between_thumbs
42
+ @name = name
43
+ @aria_label = aria_label
44
+ @values = Array(value.nil? ? [ min, max ] : value)
45
+ super(**attributes)
46
+ end
47
+
48
+ def element_attributes(**defaults)
49
+ super(**{
50
+ "data-orientation" => orientation,
51
+ "data-disabled" => (disabled.presence && ""),
52
+ "aria-disabled" => (disabled.presence && "true"),
53
+ "data-controller" => "shadcn--slider",
54
+ "data-shadcn--slider-min-value" => min,
55
+ "data-shadcn--slider-max-value" => max,
56
+ "data-shadcn--slider-step-value" => step,
57
+ "data-shadcn--slider-orientation-value" => orientation,
58
+ "data-shadcn--slider-disabled-value" => disabled,
59
+ "data-shadcn--slider-min-steps-value" => min_steps_between_thumbs,
60
+ "data-action" => "pointerdown->shadcn--slider#trackDown",
61
+ # The transform every thumb's wrapper reads. Radix publishes it on the
62
+ # root so one declaration serves every thumb and the axis is decided
63
+ # in one place.
64
+ style: merged_style(
65
+ "--radix-slider-thumb-transform: #{orientation == :vertical ? 'translateY(50%)' : 'translateX(-50%)'};"
66
+ )
67
+ }.compact.merge(defaults))
68
+ end
69
+
70
+ def call
71
+ render_element(body: safe_join([ track, *thumbs, *inputs ]))
72
+ end
73
+
74
+ private
75
+
76
+ def percent_of(value)
77
+ return 0 if max == min
78
+
79
+ ((value.to_f - min) / (max - min) * 100).clamp(0, 100)
80
+ end
81
+
82
+ # A single-thumb slider fills from the start of the scale, not from its own
83
+ # value — otherwise the fill has zero width and there is nothing to see.
84
+ # Radix's rule: `valuesCount > 1 ? Math.min(...percentages) : 0`
85
+ # (slider.tsx:592).
86
+ def range_start
87
+ values.size > 1 ? percent_of(values.min) : 0
88
+ end
89
+
90
+ def track
91
+ render(Shadcn::Slider::Track::Component.new(orientation:)) do
92
+ render(Shadcn::Slider::Range::Component.new(
93
+ orientation:,
94
+ start_percent: range_start,
95
+ end_percent: percent_of(values.max)
96
+ ))
97
+ end
98
+ end
99
+
100
+ # A bare wrapper carries the position so the thumb itself can keep a
101
+ # transition on colour alone. `calc(P% - 8px)` is the flat half of Radix's
102
+ # `getThumbInBoundsOffset` (slider.tsx:908): the exact version slides the
103
+ # correction from +half to −half across the track so the thumb stays
104
+ # inside at both ends, and the controller applies that once it knows the
105
+ # thumb's measured width.
106
+ def thumbs
107
+ values.each_with_index.map do |value, index|
108
+ tag.span(
109
+ render(Shadcn::Slider::Thumb::Component.new(
110
+ orientation:, value:, min:, max:, disabled:, index:,
111
+ "aria-label": label_for(index)
112
+ )),
113
+ style: "transform: var(--radix-slider-thumb-transform); position: absolute; " \
114
+ "#{orientation == :vertical ? 'bottom' : 'left'}: calc(#{percent_of(value)}% - 8px);",
115
+ "data-shadcn--slider-target": "wrapper"
116
+ )
117
+ end
118
+ end
119
+
120
+ # A thumb is a `role="slider"`, and a role does not take its name from the
121
+ # markup around it — the same trap Select, Checkbox and Switch fall into
122
+ # here, and axe fails the page without one. `aria_label:` takes a string
123
+ # or one per thumb, because a two-handled range is two controls and
124
+ # "Price" twice is not a name for either of them.
125
+ def label_for(index)
126
+ return nil if aria_label.blank?
127
+
128
+ aria_label.is_a?(Array) ? aria_label[index] : aria_label
129
+ end
130
+
131
+ # What a Rails form actually submits. Radix renders these too — shadcn's
132
+ # file does not, because in React the value is state rather than markup.
133
+ def inputs
134
+ return [] if name.blank?
135
+
136
+ values.map do |value|
137
+ tag.input(
138
+ type: "hidden", name: values.size > 1 ? "#{name}[]" : name, value:,
139
+ "data-shadcn--slider-target": "input"
140
+ )
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Slider
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+
10
+ # Upstream's "Vertical".
11
+ def vertical
12
+ render_with_template
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ <%# Three, because the component behaves differently in each.
2
+
3
+ One thumb is the ordinary case. Two is what upstream's own default gives
4
+ you when you pass no value at all — `[min, max]` — and is where
5
+ `min_steps_between_thumbs` and "which thumb does a press move" start to
6
+ mean something. The third is disabled, which is a state and not a variant:
7
+ the root carries `data-disabled` and the thumbs leave the tab order. %>
8
+ <div class="flex w-[420px] flex-col gap-8">
9
+ <%= render(Shadcn::Slider::Component.new(value: 50, name: "volume", aria_label: "Volume")) %>
10
+
11
+ <%= render(Shadcn::Slider::Component.new(
12
+ value: [ 25, 75 ], min_steps_between_thumbs: 1, name: "range",
13
+ aria_label: [ "Minimum price", "Maximum price" ]
14
+ )) %>
15
+
16
+ <%= render(Shadcn::Slider::Component.new(value: 30, disabled: true, aria_label: "Brightness")) %>
17
+ </div>
@@ -0,0 +1,7 @@
1
+ <%# Upstream's "Vertical". The orientation is the root's argument and the
2
+ thumbs' arrow keys follow it, so Up and Down move a vertical slider where
3
+ Left and Right move a horizontal one. %>
4
+ <div class="flex h-56 items-start gap-10">
5
+ <%= render(Shadcn::Slider::Component.new(value: 40, orientation: :vertical, class: "h-48", aria_label: "Volume")) %>
6
+ <%= render(Shadcn::Slider::Component.new(value: [ 20, 80 ], orientation: :vertical, class: "h-48", aria_label: [ "Lowest", "Highest" ])) %>
7
+ </div>
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Slider
5
+ module Range
6
+ # The filled part of the groove, between the lowest and highest thumb.
7
+ # Two inline percentages rather than a width, which is Radix's own
8
+ # (measured: `left:25%;right:50%` for values 25 and 50) and is what makes
9
+ # a two-thumb range one element instead of two.
10
+ class Component < ApplicationViewComponent
11
+ slot_name :"slider-range"
12
+
13
+ style do
14
+ base {
15
+ "absolute bg-primary data-[orientation=horizontal]:h-full " \
16
+ "data-[orientation=vertical]:w-full"
17
+ }
18
+ end
19
+
20
+ attr_reader :orientation, :start_percent, :end_percent
21
+
22
+ def initialize(orientation: :horizontal, start_percent: 0, end_percent: 100, **attributes)
23
+ @orientation = orientation
24
+ @start_percent = start_percent
25
+ @end_percent = end_percent
26
+ super(**attributes)
27
+ end
28
+
29
+ def element_attributes(**defaults)
30
+ super(**{
31
+ "data-orientation" => orientation,
32
+ "data-shadcn--slider-target" => "range",
33
+ style: merged_style(edges)
34
+ }.merge(defaults))
35
+ end
36
+
37
+ private
38
+
39
+ # Vertical sliders fill from the bottom, so the pair of edges changes
40
+ # with the axis rather than the numbers doing.
41
+ def edges
42
+ if orientation.to_sym == :vertical
43
+ "bottom: #{start_percent}%; top: #{100 - end_percent}%;"
44
+ else
45
+ "left: #{start_percent}%; right: #{100 - end_percent}%;"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Slider
5
+ module Thumb
6
+ # The handle, and the only focusable thing in the component: `role="slider"`
7
+ # with its own `aria-valuenow`, so a two-thumb range is two controls rather
8
+ # than one with two numbers.
9
+ #
10
+ # Positioning lives on a wrapper the root renders, not here — Radix does
11
+ # the same, and it is what lets the thumb keep `transition-[color,box-shadow]`
12
+ # without the position transitioning too.
13
+ class Component < ApplicationViewComponent
14
+ default_tag :span
15
+ slot_name :"slider-thumb"
16
+
17
+ style do
18
+ base {
19
+ "block size-4 shrink-0 rounded-full border border-primary bg-white " \
20
+ "shadow-sm ring-ring/50 transition-[color,box-shadow] hover:ring-4 " \
21
+ "focus-visible:ring-4 focus-visible:outline-hidden " \
22
+ "disabled:pointer-events-none disabled:opacity-50"
23
+ }
24
+ end
25
+
26
+ attr_reader :orientation, :value, :min, :max, :disabled, :index
27
+
28
+ def initialize(orientation: :horizontal, value: 0, min: 0, max: 100,
29
+ disabled: false, index: 0, **attributes)
30
+ @orientation = orientation
31
+ @value = value
32
+ @min = min
33
+ @max = max
34
+ @disabled = disabled
35
+ @index = index
36
+ super(**attributes)
37
+ end
38
+
39
+ def element_attributes(**defaults)
40
+ super(**{
41
+ role: "slider",
42
+ tabindex: disabled ? -1 : 0,
43
+ "aria-valuemin" => min,
44
+ "aria-valuemax" => max,
45
+ "aria-valuenow" => value,
46
+ "aria-orientation" => orientation,
47
+ "aria-disabled" => (disabled.presence && "true"),
48
+ "data-orientation" => orientation,
49
+ "data-index" => index,
50
+ "data-shadcn--slider-target" => "thumb",
51
+ "data-action" => "keydown->shadcn--slider#keydown " \
52
+ "pointerdown->shadcn--slider#thumbDown"
53
+ }.compact.merge(defaults))
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Slider
5
+ module Track
6
+ # The groove. Its own file only for `data-orientation`, which every part
7
+ # of this family carries and which `part` does not do.
8
+ class Component < ApplicationViewComponent
9
+ slot_name :"slider-track"
10
+
11
+ style do
12
+ base {
13
+ "relative grow overflow-hidden rounded-full bg-muted " \
14
+ "data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full " \
15
+ "data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
16
+ }
17
+ end
18
+
19
+ attr_reader :orientation
20
+
21
+ def initialize(orientation: :horizontal, **attributes)
22
+ @orientation = orientation
23
+ super(**attributes)
24
+ end
25
+
26
+ def element_attributes(**defaults)
27
+ super(**{ "data-orientation" => orientation }.merge(defaults))
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Spinner
5
+ # Port of registry/new-york-v4/ui/spinner.tsx — the lucide Loader2 icon
6
+ # with a status role.
7
+ class Component < Icon::Component
8
+ def initialize(**attributes)
9
+ super("loader-2", **attributes)
10
+ end
11
+
12
+ def element_attributes(**defaults)
13
+ super(**{ role: "status", "aria-label" => shadcn_t("spinner.loading") }.merge(defaults))
14
+ end
15
+
16
+ def css_classes(extra = nil)
17
+ super(ShadcnViewComponent.cn("size-4 animate-spin", extra))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Spinner
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ <div class="flex items-center gap-4">
2
+ <%= render(Shadcn::Spinner::Component.new) %>
3
+ <%= render(Shadcn::Spinner::Component.new(class: "size-8")) %>
4
+ <%= render(Shadcn::Button::Component.new(disabled: true)) do %>
5
+ <%= render(Shadcn::Spinner::Component.new) %> Loading
6
+ <% end %>
7
+ </div>