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,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Select
5
+ module Trigger
6
+ # SelectTrigger
7
+ class Component < ApplicationViewComponent
8
+ CHEVRON_CLASSES = "size-4 opacity-50"
9
+
10
+ renders_one :value, "Shadcn::Select::Value::Component"
11
+
12
+ default_tag :button
13
+ slot_name :"select-trigger"
14
+
15
+ style do
16
+ base {
17
+ "flex w-fit items-center justify-between gap-2 rounded-md border border-input " \
18
+ "bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs " \
19
+ "transition-[color,box-shadow] outline-none focus-visible:border-ring " \
20
+ "focus-visible:ring-[3px] focus-visible:ring-ring/50 " \
21
+ "disabled:cursor-not-allowed disabled:opacity-50 " \
22
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 " \
23
+ "data-[placeholder]:text-muted-foreground data-[size=default]:h-9 " \
24
+ "data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 " \
25
+ "*:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center " \
26
+ "*:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 " \
27
+ "dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none " \
28
+ "[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 " \
29
+ "[&_svg:not([class*='text-'])]:text-muted-foreground"
30
+ }
31
+ end
32
+
33
+ attr_reader :size, :placeholder, :disabled, :searchable
34
+
35
+ def initialize(size: :default, placeholder: true, disabled: false,
36
+ searchable: false, **attributes)
37
+ @size = size&.to_sym || :default
38
+ @placeholder = placeholder
39
+ @disabled = disabled
40
+ @searchable = searchable
41
+ super(**attributes)
42
+ end
43
+
44
+ def element_attributes(**defaults)
45
+ super(**{
46
+ type: "button",
47
+ # A searchable select's trigger is a plain button announcing a
48
+ # listbox popup: the typing happens in the field inside the popover,
49
+ # not here. shadcn's aria variant emits it that way, and it has the
50
+ # side benefit that a role-less button can take its accessible name
51
+ # from its own text, which `role="combobox"` cannot.
52
+ role: ("combobox" unless searchable),
53
+ disabled: (true if disabled),
54
+ "aria-expanded" => "false",
55
+ "aria-haspopup" => ("listbox" if searchable),
56
+ "aria-autocomplete" => ("none" unless searchable),
57
+ "data-size" => size,
58
+ "data-state" => "closed",
59
+ "data-placeholder" => (true if placeholder),
60
+ "data-shadcn--select-target" => "trigger",
61
+ "data-action" => "click->shadcn--select#toggle " \
62
+ "keydown->shadcn--select#triggerKeydown"
63
+ }.merge(defaults))
64
+ end
65
+
66
+ def call
67
+ render_element(body: safe_join([ value, content, chevron ].compact))
68
+ end
69
+
70
+ private
71
+
72
+ def chevron
73
+ render(Icon::Component.new("chevron-down", class: CHEVRON_CLASSES))
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Select
5
+ module Value
6
+ # SelectValue — the controller writes the chosen item's label in here.
7
+ class Component < ApplicationViewComponent
8
+ default_tag :span
9
+ slot_name :"select-value"
10
+
11
+ attr_reader :placeholder
12
+
13
+ def initialize(placeholder: nil, **attributes)
14
+ @placeholder = placeholder
15
+ super(**attributes)
16
+ end
17
+
18
+ def element_attributes(**defaults)
19
+ super(**{ "data-shadcn--select-target" => "value" }.merge(defaults))
20
+ end
21
+
22
+ def call
23
+ render_element(body: content.presence || placeholder)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ # The parts of the Select family that are just an element with a
5
+ # `data-slot` and a fixed set of classes. Anything with variants, slots or
6
+ # markup of its own keeps its own `component.rb`.
7
+ module Select
8
+ extend Parts
9
+
10
+ part :label, slot: "select-label", classes: "px-2 py-1.5 text-xs text-muted-foreground"
11
+
12
+ part :separator, slot: "select-separator",
13
+ classes: "pointer-events-none -mx-1 my-1 h-px bg-border"
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Separator
5
+ # Port of registry/new-york-v4/ui/separator.tsx
6
+ #
7
+ # Reproduces Radix's semantics: a decorative separator is `role="none"`,
8
+ # a meaningful one is `role="separator"` and only advertises
9
+ # `aria-orientation` when vertical.
10
+ class Component < ApplicationViewComponent
11
+ slot_name :separator
12
+
13
+ style do
14
+ base {
15
+ "shrink-0 bg-border data-[orientation=horizontal]:h-px " \
16
+ "data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full " \
17
+ "data-[orientation=vertical]:w-px"
18
+ }
19
+ end
20
+
21
+ attr_reader :orientation, :decorative
22
+
23
+ def initialize(orientation: :horizontal, decorative: true, **attributes)
24
+ @orientation = orientation.to_sym
25
+ @decorative = decorative
26
+ super(**attributes)
27
+ end
28
+
29
+ def element_attributes(**defaults)
30
+ semantics =
31
+ if decorative
32
+ { role: "none" }
33
+ else
34
+ { role: "separator", "aria-orientation" => (orientation.to_s if orientation == :vertical) }
35
+ end
36
+
37
+ super(**{ "data-orientation" => orientation }.merge(semantics).merge(defaults))
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Separator
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ <div class="max-w-sm">
2
+ <div class="text-sm font-medium">Radix Primitives</div>
3
+ <%= render(Shadcn::Separator::Component.new(class: "my-4")) %>
4
+ <div class="flex h-5 items-center gap-4 text-sm">
5
+ <div>Blog</div>
6
+ <%= render(Shadcn::Separator::Component.new(orientation: :vertical)) %>
7
+ <div>Docs</div>
8
+ </div>
9
+ </div>
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Sheet
5
+ # Port of registry/new-york-v4/ui/sheet.tsx — shadcn builds the Sheet on the
6
+ # very same Radix Dialog primitive, so the port shares its behaviour.
7
+ class Component < ApplicationViewComponent
8
+ include Concerns::ModalRoot
9
+
10
+ renders_one :trigger, "Shadcn::Sheet::Trigger::Component"
11
+ renders_one :dialog_content, "Shadcn::Sheet::Content::Component"
12
+
13
+ slot_name :sheet
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Sheet
5
+ module Content
6
+ # SheetContent — slides in from one of the four edges.
7
+ class Component < ApplicationViewComponent
8
+ CLOSE_CLASSES = "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background " \
9
+ "transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring " \
10
+ "focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none " \
11
+ "data-[state=open]:bg-secondary"
12
+
13
+ renders_one :header, "Shadcn::Sheet::Header::Component"
14
+ renders_one :footer, "Shadcn::Sheet::Footer::Component"
15
+
16
+ slot_name :"sheet-content"
17
+
18
+ style do
19
+ base {
20
+ "fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out " \
21
+ "data-[state=closed]:animate-out data-[state=closed]:duration-300 " \
22
+ "data-[state=open]:animate-in data-[state=open]:duration-500"
23
+ }
24
+
25
+ variants {
26
+ side {
27
+ right {
28
+ "inset-y-0 right-0 h-full w-3/4 border-l " \
29
+ "data-[state=closed]:slide-out-to-right " \
30
+ "data-[state=open]:slide-in-from-right sm:max-w-sm"
31
+ }
32
+ left {
33
+ "inset-y-0 left-0 h-full w-3/4 border-r " \
34
+ "data-[state=closed]:slide-out-to-left " \
35
+ "data-[state=open]:slide-in-from-left sm:max-w-sm"
36
+ }
37
+ top {
38
+ "inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top " \
39
+ "data-[state=open]:slide-in-from-top"
40
+ }
41
+ bottom {
42
+ "inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom " \
43
+ "data-[state=open]:slide-in-from-bottom"
44
+ }
45
+ }
46
+ }
47
+
48
+ defaults { { side: :right } }
49
+ end
50
+
51
+ attr_reader :side, :show_close_button
52
+
53
+ def initialize(side: :right, show_close_button: true, **attributes)
54
+ @side = side&.to_sym || :right
55
+ @show_close_button = show_close_button
56
+ super(**attributes)
57
+ end
58
+
59
+ def style_variants
60
+ { side: }
61
+ end
62
+
63
+ def element_attributes(**defaults)
64
+ super(**{
65
+ role: "dialog",
66
+ "aria-modal" => "true",
67
+ tabindex: "-1",
68
+ "data-state" => "closed",
69
+ hidden: true,
70
+ "data-shadcn--dialog-target" => "content"
71
+ }.merge(defaults))
72
+ end
73
+
74
+ def call
75
+ tag.div("data-slot": "sheet-portal", style: CONTENTS_STYLE) do
76
+ safe_join([
77
+ render(Overlay::Component.new),
78
+ render_element(body: safe_join([ header, content, footer, close_button ].compact))
79
+ ])
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def close_button
86
+ return unless show_close_button
87
+
88
+ render(Close::Component.new(class: CLOSE_CLASSES)) do
89
+ safe_join([
90
+ render(Icon::Component.new("x", class: "size-4")),
91
+ tag.span(shadcn_t("dialog.close"), class: "sr-only")
92
+ ])
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Sheet
5
+ module Header
6
+ # SheetHeader
7
+ class Component < ApplicationViewComponent
8
+ renders_one :title, "Shadcn::Sheet::Title::Component"
9
+ renders_one :description, "Shadcn::Sheet::Description::Component"
10
+
11
+ slot_name :"sheet-header"
12
+
13
+ style do
14
+ base { "flex flex-col gap-1.5 p-4" }
15
+ end
16
+
17
+ def call
18
+ render_element(body: safe_join([ title, description, content ].compact))
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Sheet
5
+ class Preview < ApplicationViewComponentPreview
6
+ def default
7
+ render_with_template
8
+ end
9
+
10
+ # Upstream's "No Close Button".
11
+ def no_close_button
12
+ render_with_template
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ <div class="flex items-center gap-2">
2
+ <% %i[right left top bottom].each do |side| %>
3
+ <%= render(Shadcn::Sheet::Component.new) do |s| %>
4
+ <% s.with_trigger(class: Shadcn::Button::Component.variant_classes(variant: :outline)) { side.to_s.capitalize } %>
5
+ <% s.with_dialog_content(side:) do |c| %>
6
+ <% c.with_header do |h| %>
7
+ <% h.with_title { "Edit profile" } %>
8
+ <% h.with_description { "Make changes to your profile here." } %>
9
+ <% end %>
10
+ <% c.with_footer do %>
11
+ <%= render(Shadcn::Sheet::Close::Component.new(class: Shadcn::Button::Component.variant_classes(variant: :outline))) { "Close" } %>
12
+ <% end %>
13
+ <% end %>
14
+ <% end %>
15
+ <% end %>
16
+ </div>
@@ -0,0 +1,14 @@
1
+ <%# Upstream's "No Close Button": the corner dismiss left out, so the panel is
2
+ closed from its own footer, from Escape, or from outside. %>
3
+ <%= render(Shadcn::Sheet::Component.new) do |s| %>
4
+ <% s.with_trigger(class: Shadcn::Button::Component.variant_classes(variant: :outline)) { "Open" } %>
5
+ <% s.with_dialog_content(show_close_button: false) do |c| %>
6
+ <% c.with_header do |h| %>
7
+ <% h.with_title { "Filters" } %>
8
+ <% h.with_description { "Narrow the list down." } %>
9
+ <% end %>
10
+ <% c.with_footer do %>
11
+ <%= render(Shadcn::Sheet::Close::Component.new(class: Shadcn::Button::Component.variant_classes(variant: :outline))) { "Done" } %>
12
+ <% end %>
13
+ <% end %>
14
+ <% end %>
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ # The parts of the Sheet family that are just an element with a
5
+ # `data-slot` and a fixed set of classes. Anything with variants, slots or
6
+ # markup of its own keeps its own `component.rb`.
7
+ module Sheet
8
+ extend Parts
9
+
10
+ part :close, slot: "sheet-close", from: Dialog::Close::Component
11
+
12
+ part :description, tag: :p, slot: "sheet-description", classes: "text-sm text-muted-foreground"
13
+
14
+ part :footer, slot: "sheet-footer", classes: "mt-auto flex flex-col gap-2 p-4"
15
+
16
+ part :overlay, slot: "sheet-overlay", from: Dialog::Overlay::Component
17
+
18
+ part :title, tag: :h2, slot: "sheet-title", classes: "font-semibold text-foreground"
19
+
20
+ part :trigger, slot: "sheet-trigger", from: Dialog::Trigger::Component
21
+ end
22
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Sidebar
5
+ # The panel itself — upstream's `Sidebar` (vendor/shadcn/ui/sidebar.tsx:154),
6
+ # not its provider, which is `Sidebar::Provider::Component`.
7
+ #
8
+ # Upstream renders three different trees here and picks between them while
9
+ # rendering. Two of those choices are knowable on a server and one is not:
10
+ #
11
+ # collapsible: :none a bare panel, decided here
12
+ # isMobile a Sheet, decided by `matchMedia` — so not here
13
+ #
14
+ # The mobile branch is the controller's, applied to this same DOM without
15
+ # moving it. See decisions/02-javascript.md.
16
+ class Component < ApplicationViewComponent
17
+ slot_name :sidebar
18
+
19
+ style do
20
+ base { "group peer hidden text-sidebar-foreground md:block" }
21
+ end
22
+
23
+ attr_reader :side, :variant, :collapsible
24
+
25
+ def initialize(side: :left, variant: :sidebar, collapsible: :offcanvas, **attributes)
26
+ @side = side&.to_sym || :left
27
+ @variant = variant&.to_sym || :sidebar
28
+ @collapsible = collapsible&.to_sym || :offcanvas
29
+ super(**attributes)
30
+ end
31
+
32
+ def element_attributes(**defaults)
33
+ return super(**defaults) if bare?
34
+
35
+ super(**{
36
+ "data-shadcn--sidebar-target" => "sidebar",
37
+ "data-state" => "expanded",
38
+ # Upstream fills this only while collapsed (sidebar.tsx:212) — the
39
+ # classes match `group-data-[collapsible=offcanvas]:…`, so a value here
40
+ # while expanded would style an open panel as a closed one. The value
41
+ # to collapse *to* therefore has to live somewhere else.
42
+ "data-collapsible" => "",
43
+ "data-sidebar-collapsible" => collapsible,
44
+ "data-variant" => variant,
45
+ "data-side" => side
46
+ }.merge(defaults))
47
+ end
48
+
49
+ # `collapsible: :none` is a different element with different classes, not
50
+ # the same one with a flag (sidebar.tsx:166-180).
51
+ def css_classes(extra = nil)
52
+ return super if !bare?
53
+
54
+ ShadcnViewComponent.cn(
55
+ "flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground", extra
56
+ )
57
+ end
58
+
59
+ def call
60
+ return render_element(body: content) if bare?
61
+
62
+ render_element(body: safe_join([ overlay, gap, container ]))
63
+ end
64
+
65
+ private
66
+
67
+ def bare? = collapsible == :none
68
+
69
+ # The dimmed backdrop upstream gets for free by rendering a real `Sheet`
70
+ # below `md` (sidebar.tsx:184). There is no Sheet here — this panel *is*
71
+ # the sheet — so the element it would have portalled has to be in the
72
+ # markup, hidden, from the start. It is the gem's own `sheet-overlay`,
73
+ # classes and all, rather than a new slot: a host styling `sheet-overlay`
74
+ # should reach this one too.
75
+ #
76
+ # `hidden` until the controller opens the sheet, and it stays in the
77
+ # server's markup on desktop, where nothing ever unhides it. The dialog
78
+ # target it inherits is cleared: this one answers to the sidebar.
79
+ def overlay
80
+ render(
81
+ Shadcn::Sheet::Overlay::Component.new(
82
+ "data-shadcn--dialog-target": nil,
83
+ "data-shadcn--sidebar-target": "overlay"
84
+ )
85
+ )
86
+ end
87
+
88
+ def gap
89
+ tag.div("data-slot": "sidebar-gap", class: gap_classes)
90
+ end
91
+
92
+ def gap_classes
93
+ floating = "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
94
+ plain = "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
95
+
96
+ ShadcnViewComponent.cn(
97
+ "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
98
+ "group-data-[collapsible=offcanvas]:w-0",
99
+ "group-data-[side=right]:rotate-180",
100
+ inset_like? ? floating : plain,
101
+ # This element exists to reserve the panel's width in the page's flow.
102
+ # A sheet overlays the page instead, so on the mobile branch it has to
103
+ # reserve nothing. The Popover API already takes the panel out of flow
104
+ # while the sheet is open, which makes this redundant there and load
105
+ # -bearing everywhere else: `top_layer.js` is feature-detected, and the
106
+ # fallback leaves the panel in flow.
107
+ "group-data-[mobile=true]:w-0"
108
+ )
109
+ end
110
+
111
+ def container
112
+ tag.div(
113
+ safe_join([ sheet_header, inner ]),
114
+ "data-slot": "sidebar-container",
115
+ # The element that animates, so the controller has to be able to write
116
+ # `data-state` on it and wait for its animations to settle.
117
+ "data-shadcn--sidebar-target": "container",
118
+ # Read by the controller when it turns this panel into a sheet: a
119
+ # dialog needs a name, and the name has to exist before it is pointed
120
+ # at.
121
+ "data-sheet-title" => sheet_title_id,
122
+ "data-sheet-description" => sheet_description_id,
123
+ class: container_classes
124
+ )
125
+ end
126
+
127
+ # The name and the description a Sheet gives the mobile sidebar
128
+ # (sidebar.tsx:198-201), which upstream renders only in its mobile tree.
129
+ # There is one tree here, so they are always in the markup and `hidden`
130
+ # until the controller opens the sheet: `hidden` keeps them out of the
131
+ # accessibility tree entirely, where `sr-only` alone would have a desktop
132
+ # screen reader read "Sidebar. Displays the mobile sidebar." in the middle
133
+ # of a page with no sheet on it.
134
+ #
135
+ # The ids are here rather than generated in JavaScript because the
136
+ # elements are: `aria-labelledby` has to point at something the server
137
+ # rendered.
138
+ def sheet_header
139
+ tag.div(
140
+ safe_join([
141
+ tag.h2(shadcn_t("sidebar.title"), id: sheet_title_id, "data-slot": "sheet-title"),
142
+ tag.p(shadcn_t("sidebar.description"), id: sheet_description_id, "data-slot": "sheet-description")
143
+ ]),
144
+ class: "sr-only",
145
+ hidden: true,
146
+ "data-slot": "sheet-header",
147
+ "data-shadcn--sidebar-target": "sheetHeader"
148
+ )
149
+ end
150
+
151
+ def sheet_title_id = @sheet_title_id ||= "shadcn-sidebar-title-#{SecureRandom.hex(4)}"
152
+ def sheet_description_id = @sheet_description_id ||= "shadcn-sidebar-description-#{SecureRandom.hex(4)}"
153
+
154
+ def container_classes
155
+ ShadcnViewComponent.cn(
156
+ "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) " \
157
+ "transition-[left,right,width] duration-200 ease-linear md:flex",
158
+ side_classes,
159
+ padding_classes,
160
+ # Upstream renders a Sheet below `md` and this panel above it, so its
161
+ # `hidden … md:flex` is never asked to show anything on a phone. Here
162
+ # there is one tree and the sheet *is* this element, so the mobile
163
+ # branch has to turn both of those off. The controller does that for
164
+ # the panel's own `hidden … md:block`; nothing was doing it for the
165
+ # container inside, which is why the sheet opened onto an empty strip.
166
+ #
167
+ # A `group-data-` variant rather than the inline `display` used on the
168
+ # panel: both are two-class selectors, so they outrank `md:flex` at
169
+ # every width without the specificity fight a bare class would lose.
170
+ "group-data-[mobile=true]:flex group-data-[mobile=true]:w-(--sidebar-width-mobile)",
171
+ # `z-50` and `shadow-lg` are `sheet-content`'s (sheet.tsx:63), and the
172
+ # overlay above carries `z-50` too — so the two match upstream's pair
173
+ # and DOM order decides, which puts the panel over its own backdrop.
174
+ # The desktop `z-10` would put it under.
175
+ "group-data-[mobile=true]:z-50 group-data-[mobile=true]:shadow-lg",
176
+ sheet_animation_classes
177
+ )
178
+ end
179
+
180
+ # `sheet-content`'s own entrance and exit (sheet.tsx:63-68), keyed the way
181
+ # upstream keys them: on the element's *own* `data-state`, which the
182
+ # controller writes only on the mobile branch. On desktop the attribute is
183
+ # absent, so none of this matches and the panel keeps sliding on the
184
+ # `transition-[left,right,width]` above instead.
185
+ #
186
+ # The panel's `data-state` is a different axis — expanded or collapsed —
187
+ # which is why this one is on the container rather than up there.
188
+ def sheet_animation_classes
189
+ slide =
190
+ if side == :left
191
+ "data-[state=open]:slide-in-from-left data-[state=closed]:slide-out-to-left"
192
+ else
193
+ "data-[state=open]:slide-in-from-right data-[state=closed]:slide-out-to-right"
194
+ end
195
+
196
+ "data-[state=open]:animate-in data-[state=closed]:animate-out " \
197
+ "data-[state=open]:duration-500 data-[state=closed]:duration-300 #{slide}"
198
+ end
199
+
200
+ def side_classes
201
+ if side == :left
202
+ "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
203
+ else
204
+ "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]"
205
+ end
206
+ end
207
+
208
+ def padding_classes
209
+ if inset_like?
210
+ "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
211
+ else
212
+ "group-data-[collapsible=icon]:w-(--sidebar-width-icon) " \
213
+ "group-data-[side=left]:border-r group-data-[side=right]:border-l"
214
+ end
215
+ end
216
+
217
+ def inner
218
+ tag.div(
219
+ content,
220
+ "data-sidebar": "sidebar",
221
+ "data-slot": "sidebar-inner",
222
+ class: "flex h-full w-full flex-col bg-sidebar " \
223
+ "group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border " \
224
+ "group-data-[variant=floating]:border-sidebar-border " \
225
+ "group-data-[variant=floating]:shadow-sm"
226
+ )
227
+ end
228
+
229
+ def inset_like? = [ :floating, :inset ].include?(variant)
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Sidebar
5
+ module Input
6
+ # SidebarInput — the ported Input under another `data-slot`, with the
7
+ # sidebar's own sizing on top. Inherit-and-restamp, as ButtonGroupSeparator
8
+ # does to Separator, so the parent's classes are not restated and cannot
9
+ # rot.
10
+ class Component < Shadcn::Input::Component
11
+ slot_name :"sidebar-input"
12
+
13
+ EXTRA_CLASSES = "h-8 w-full bg-background shadow-none"
14
+
15
+ def css_classes(extra = nil)
16
+ super([ EXTRA_CLASSES, extra ].compact.join(" "))
17
+ end
18
+
19
+ def element_attributes(**defaults)
20
+ super(**{ "data-sidebar" => "input" }.merge(defaults))
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end