shadcn-rails 0.1.0

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 (315) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +40 -0
  3. data/CHANGELOG.md +54 -0
  4. data/CLAUDE.md +463 -0
  5. data/PROGRESS.md +485 -0
  6. data/README.md +1483 -0
  7. data/Rakefile +29 -0
  8. data/__tests__/controllers/__snapshots__/calendar_controller.test.js.snap +13 -0
  9. data/__tests__/controllers/__snapshots__/popover_controller.test.js.snap +46 -0
  10. data/__tests__/controllers/__snapshots__/sheet_controller.test.js.snap +111 -0
  11. data/__tests__/controllers/__snapshots__/tabs_controller.test.js.snap +27 -0
  12. data/__tests__/controllers/accordion_controller.test.js +904 -0
  13. data/__tests__/controllers/calendar_controller.test.js +1370 -0
  14. data/__tests__/controllers/carousel_controller.test.js +912 -0
  15. data/__tests__/controllers/checkbox_controller.test.js +454 -0
  16. data/__tests__/controllers/collapsible_controller.test.js +407 -0
  17. data/__tests__/controllers/combobox_controller.test.js +966 -0
  18. data/__tests__/controllers/context_menu_controller.test.js +627 -0
  19. data/__tests__/controllers/date_picker_controller.test.js +636 -0
  20. data/__tests__/controllers/dialog_controller.test.js +878 -0
  21. data/__tests__/controllers/drawer_controller.test.js +995 -0
  22. data/__tests__/controllers/menubar_controller.test.js +736 -0
  23. data/__tests__/controllers/navigation_menu_controller.test.js +598 -0
  24. data/__tests__/controllers/popover_controller.test.js +1007 -0
  25. data/__tests__/controllers/radio_group_controller.test.js +640 -0
  26. data/__tests__/controllers/resizable_controller.test.js +680 -0
  27. data/__tests__/controllers/select_controller.test.js +674 -0
  28. data/__tests__/controllers/sheet_controller.test.js +986 -0
  29. data/__tests__/controllers/slider_controller.test.js +1036 -0
  30. data/__tests__/controllers/switch_controller.test.js +424 -0
  31. data/__tests__/controllers/tabs_controller.test.js +907 -0
  32. data/__tests__/controllers/toggle_group_controller.test.js +839 -0
  33. data/__tests__/controllers/tooltip_controller.test.js +808 -0
  34. data/__tests__/helpers/stimulus-test-helper.js +203 -0
  35. data/app/assets/config/manifest.js +1 -0
  36. data/app/assets/javascripts/shadcn/controllers/accordion_controller.d.ts +53 -0
  37. data/app/assets/javascripts/shadcn/controllers/accordion_controller.js +140 -0
  38. data/app/assets/javascripts/shadcn/controllers/avatar_controller.d.ts +22 -0
  39. data/app/assets/javascripts/shadcn/controllers/avatar_controller.js +26 -0
  40. data/app/assets/javascripts/shadcn/controllers/calendar_controller.js +592 -0
  41. data/app/assets/javascripts/shadcn/controllers/carousel_controller.js +263 -0
  42. data/app/assets/javascripts/shadcn/controllers/checkbox_controller.d.ts +31 -0
  43. data/app/assets/javascripts/shadcn/controllers/checkbox_controller.js +48 -0
  44. data/app/assets/javascripts/shadcn/controllers/collapsible_controller.d.ts +43 -0
  45. data/app/assets/javascripts/shadcn/controllers/collapsible_controller.js +73 -0
  46. data/app/assets/javascripts/shadcn/controllers/combobox_controller.js +234 -0
  47. data/app/assets/javascripts/shadcn/controllers/command_controller.js +141 -0
  48. data/app/assets/javascripts/shadcn/controllers/command_dialog_controller.js +162 -0
  49. data/app/assets/javascripts/shadcn/controllers/context_menu_controller.js +202 -0
  50. data/app/assets/javascripts/shadcn/controllers/date_picker_controller.js +282 -0
  51. data/app/assets/javascripts/shadcn/controllers/dialog_controller.d.ts +67 -0
  52. data/app/assets/javascripts/shadcn/controllers/dialog_controller.js +187 -0
  53. data/app/assets/javascripts/shadcn/controllers/drawer_controller.d.ts +58 -0
  54. data/app/assets/javascripts/shadcn/controllers/drawer_controller.js +112 -0
  55. data/app/assets/javascripts/shadcn/controllers/dropdown_controller.d.ts +83 -0
  56. data/app/assets/javascripts/shadcn/controllers/dropdown_controller.js +225 -0
  57. data/app/assets/javascripts/shadcn/controllers/hover_card_controller.d.ts +59 -0
  58. data/app/assets/javascripts/shadcn/controllers/hover_card_controller.js +143 -0
  59. data/app/assets/javascripts/shadcn/controllers/input_otp_controller.d.ts +44 -0
  60. data/app/assets/javascripts/shadcn/controllers/input_otp_controller.js +206 -0
  61. data/app/assets/javascripts/shadcn/controllers/menubar_controller.js +323 -0
  62. data/app/assets/javascripts/shadcn/controllers/navigation_menu_controller.js +251 -0
  63. data/app/assets/javascripts/shadcn/controllers/popover_controller.d.ts +56 -0
  64. data/app/assets/javascripts/shadcn/controllers/popover_controller.js +141 -0
  65. data/app/assets/javascripts/shadcn/controllers/radio_group_controller.d.ts +47 -0
  66. data/app/assets/javascripts/shadcn/controllers/radio_group_controller.js +108 -0
  67. data/app/assets/javascripts/shadcn/controllers/resizable_controller.js +272 -0
  68. data/app/assets/javascripts/shadcn/controllers/scroll_area_controller.d.ts +44 -0
  69. data/app/assets/javascripts/shadcn/controllers/scroll_area_controller.js +74 -0
  70. data/app/assets/javascripts/shadcn/controllers/select_controller.d.ts +84 -0
  71. data/app/assets/javascripts/shadcn/controllers/select_controller.js +222 -0
  72. data/app/assets/javascripts/shadcn/controllers/sheet_controller.d.ts +60 -0
  73. data/app/assets/javascripts/shadcn/controllers/sheet_controller.js +151 -0
  74. data/app/assets/javascripts/shadcn/controllers/sidebar_controller.js +148 -0
  75. data/app/assets/javascripts/shadcn/controllers/slider_controller.d.ts +102 -0
  76. data/app/assets/javascripts/shadcn/controllers/slider_controller.js +364 -0
  77. data/app/assets/javascripts/shadcn/controllers/switch_controller.d.ts +46 -0
  78. data/app/assets/javascripts/shadcn/controllers/switch_controller.js +78 -0
  79. data/app/assets/javascripts/shadcn/controllers/tabs_controller.d.ts +51 -0
  80. data/app/assets/javascripts/shadcn/controllers/tabs_controller.js +126 -0
  81. data/app/assets/javascripts/shadcn/controllers/toast_controller.d.ts +37 -0
  82. data/app/assets/javascripts/shadcn/controllers/toast_controller.js +58 -0
  83. data/app/assets/javascripts/shadcn/controllers/toggle_controller.d.ts +27 -0
  84. data/app/assets/javascripts/shadcn/controllers/toggle_controller.js +42 -0
  85. data/app/assets/javascripts/shadcn/controllers/toggle_group_controller.d.ts +44 -0
  86. data/app/assets/javascripts/shadcn/controllers/toggle_group_controller.js +68 -0
  87. data/app/assets/javascripts/shadcn/controllers/tooltip_controller.d.ts +56 -0
  88. data/app/assets/javascripts/shadcn/controllers/tooltip_controller.js +117 -0
  89. data/app/assets/javascripts/shadcn/index.d.ts +74 -0
  90. data/app/assets/javascripts/shadcn/index.js +133 -0
  91. data/app/assets/stylesheets/.keep +0 -0
  92. data/app/assets/stylesheets/shadcn/base.css +445 -0
  93. data/app/assets/stylesheets/shadcn/components.css +513 -0
  94. data/app/assets/stylesheets/shadcn/index.css +18 -0
  95. data/app/assets/stylesheets/shadcn/themes/gray.css +68 -0
  96. data/app/assets/stylesheets/shadcn/themes/slate.css +68 -0
  97. data/app/assets/stylesheets/shadcn/themes/stone.css +68 -0
  98. data/app/assets/stylesheets/shadcn/themes/zinc.css +68 -0
  99. data/app/components/shadcn/accordion_component.rb +63 -0
  100. data/app/components/shadcn/accordion_content_component.rb +29 -0
  101. data/app/components/shadcn/accordion_item_component.rb +40 -0
  102. data/app/components/shadcn/accordion_trigger_component.rb +49 -0
  103. data/app/components/shadcn/alert_component.rb +75 -0
  104. data/app/components/shadcn/alert_description_component.rb +12 -0
  105. data/app/components/shadcn/alert_dialog_action_component.rb +24 -0
  106. data/app/components/shadcn/alert_dialog_cancel_component.rb +24 -0
  107. data/app/components/shadcn/alert_dialog_component.rb +71 -0
  108. data/app/components/shadcn/alert_dialog_content_component.rb +57 -0
  109. data/app/components/shadcn/alert_dialog_description_component.rb +12 -0
  110. data/app/components/shadcn/alert_dialog_footer_component.rb +19 -0
  111. data/app/components/shadcn/alert_dialog_header_component.rb +19 -0
  112. data/app/components/shadcn/alert_dialog_title_component.rb +12 -0
  113. data/app/components/shadcn/alert_title_component.rb +12 -0
  114. data/app/components/shadcn/aspect_ratio_component.rb +49 -0
  115. data/app/components/shadcn/avatar_component.rb +107 -0
  116. data/app/components/shadcn/avatar_fallback_component.rb +17 -0
  117. data/app/components/shadcn/badge_component.rb +49 -0
  118. data/app/components/shadcn/base_component.rb +100 -0
  119. data/app/components/shadcn/breadcrumb_component.rb +70 -0
  120. data/app/components/shadcn/breadcrumb_item_component.rb +50 -0
  121. data/app/components/shadcn/button_component.rb +141 -0
  122. data/app/components/shadcn/button_group_component.rb +69 -0
  123. data/app/components/shadcn/calendar_component.rb +337 -0
  124. data/app/components/shadcn/card_action_component.rb +10 -0
  125. data/app/components/shadcn/card_component.rb +63 -0
  126. data/app/components/shadcn/card_content_component.rb +19 -0
  127. data/app/components/shadcn/card_description_component.rb +12 -0
  128. data/app/components/shadcn/card_footer_component.rb +12 -0
  129. data/app/components/shadcn/card_header_component.rb +24 -0
  130. data/app/components/shadcn/card_title_component.rb +18 -0
  131. data/app/components/shadcn/carousel_component.rb +275 -0
  132. data/app/components/shadcn/checkbox_component.rb +103 -0
  133. data/app/components/shadcn/collapsible_component.rb +66 -0
  134. data/app/components/shadcn/collapsible_content_component.rb +28 -0
  135. data/app/components/shadcn/combobox_component.rb +322 -0
  136. data/app/components/shadcn/command_component.rb +52 -0
  137. data/app/components/shadcn/command_dialog_component.rb +76 -0
  138. data/app/components/shadcn/command_empty_component.rb +12 -0
  139. data/app/components/shadcn/command_group_component.rb +34 -0
  140. data/app/components/shadcn/command_input_component.rb +59 -0
  141. data/app/components/shadcn/command_item_component.rb +48 -0
  142. data/app/components/shadcn/command_list_component.rb +38 -0
  143. data/app/components/shadcn/command_separator_component.rb +12 -0
  144. data/app/components/shadcn/command_shortcut_component.rb +12 -0
  145. data/app/components/shadcn/context_menu_component.rb +64 -0
  146. data/app/components/shadcn/context_menu_content_component.rb +44 -0
  147. data/app/components/shadcn/context_menu_item_component.rb +63 -0
  148. data/app/components/shadcn/context_menu_label_component.rb +18 -0
  149. data/app/components/shadcn/context_menu_separator_component.rb +12 -0
  150. data/app/components/shadcn/context_menu_shortcut_component.rb +12 -0
  151. data/app/components/shadcn/date_picker_component.rb +368 -0
  152. data/app/components/shadcn/dialog_component.rb +77 -0
  153. data/app/components/shadcn/dialog_content_component.rb +91 -0
  154. data/app/components/shadcn/dialog_description_component.rb +12 -0
  155. data/app/components/shadcn/dialog_footer_component.rb +12 -0
  156. data/app/components/shadcn/dialog_header_component.rb +19 -0
  157. data/app/components/shadcn/dialog_title_component.rb +12 -0
  158. data/app/components/shadcn/drawer_component.rb +72 -0
  159. data/app/components/shadcn/drawer_content_component.rb +76 -0
  160. data/app/components/shadcn/drawer_description_component.rb +12 -0
  161. data/app/components/shadcn/drawer_footer_component.rb +12 -0
  162. data/app/components/shadcn/drawer_header_component.rb +19 -0
  163. data/app/components/shadcn/drawer_title_component.rb +12 -0
  164. data/app/components/shadcn/dropdown_menu_component.rb +75 -0
  165. data/app/components/shadcn/dropdown_menu_content_component.rb +49 -0
  166. data/app/components/shadcn/dropdown_menu_group_component.rb +10 -0
  167. data/app/components/shadcn/dropdown_menu_item_component.rb +63 -0
  168. data/app/components/shadcn/dropdown_menu_label_component.rb +18 -0
  169. data/app/components/shadcn/dropdown_menu_separator_component.rb +12 -0
  170. data/app/components/shadcn/dropdown_menu_shortcut_component.rb +12 -0
  171. data/app/components/shadcn/empty_component.rb +48 -0
  172. data/app/components/shadcn/empty_content_component.rb +12 -0
  173. data/app/components/shadcn/empty_description_component.rb +12 -0
  174. data/app/components/shadcn/empty_header_component.rb +29 -0
  175. data/app/components/shadcn/empty_media_component.rb +21 -0
  176. data/app/components/shadcn/empty_title_component.rb +12 -0
  177. data/app/components/shadcn/field_component.rb +113 -0
  178. data/app/components/shadcn/hover_card_component.rb +64 -0
  179. data/app/components/shadcn/hover_card_content_component.rb +36 -0
  180. data/app/components/shadcn/input_component.rb +108 -0
  181. data/app/components/shadcn/input_group_component.rb +70 -0
  182. data/app/components/shadcn/input_otp_component.rb +183 -0
  183. data/app/components/shadcn/item_actions_component.rb +12 -0
  184. data/app/components/shadcn/item_component.rb +98 -0
  185. data/app/components/shadcn/item_content_component.rb +24 -0
  186. data/app/components/shadcn/item_description_component.rb +12 -0
  187. data/app/components/shadcn/item_footer_component.rb +12 -0
  188. data/app/components/shadcn/item_group_component.rb +24 -0
  189. data/app/components/shadcn/item_header_component.rb +12 -0
  190. data/app/components/shadcn/item_media_component.rb +22 -0
  191. data/app/components/shadcn/item_separator_component.rb +12 -0
  192. data/app/components/shadcn/item_title_component.rb +12 -0
  193. data/app/components/shadcn/kbd_component.rb +36 -0
  194. data/app/components/shadcn/label_component.rb +49 -0
  195. data/app/components/shadcn/menubar_checkbox_item_component.rb +76 -0
  196. data/app/components/shadcn/menubar_component.rb +56 -0
  197. data/app/components/shadcn/menubar_content_component.rb +64 -0
  198. data/app/components/shadcn/menubar_item_component.rb +65 -0
  199. data/app/components/shadcn/menubar_label_component.rb +27 -0
  200. data/app/components/shadcn/menubar_menu_component.rb +34 -0
  201. data/app/components/shadcn/menubar_radio_group_component.rb +42 -0
  202. data/app/components/shadcn/menubar_radio_item_component.rb +76 -0
  203. data/app/components/shadcn/menubar_separator_component.rb +22 -0
  204. data/app/components/shadcn/menubar_shortcut_component.rb +21 -0
  205. data/app/components/shadcn/menubar_sub_component.rb +38 -0
  206. data/app/components/shadcn/menubar_sub_content_component.rb +45 -0
  207. data/app/components/shadcn/menubar_sub_trigger_component.rb +59 -0
  208. data/app/components/shadcn/menubar_trigger_component.rb +31 -0
  209. data/app/components/shadcn/native_select_component.rb +150 -0
  210. data/app/components/shadcn/navigation_menu_component.rb +76 -0
  211. data/app/components/shadcn/navigation_menu_content_component.rb +30 -0
  212. data/app/components/shadcn/navigation_menu_item_component.rb +39 -0
  213. data/app/components/shadcn/navigation_menu_link_component.rb +38 -0
  214. data/app/components/shadcn/navigation_menu_list_component.rb +29 -0
  215. data/app/components/shadcn/navigation_menu_trigger_component.rb +59 -0
  216. data/app/components/shadcn/pagination_component.rb +195 -0
  217. data/app/components/shadcn/pagination_content_component.rb +47 -0
  218. data/app/components/shadcn/pagination_ellipsis_component.rb +30 -0
  219. data/app/components/shadcn/pagination_item_component.rb +53 -0
  220. data/app/components/shadcn/pagination_next_component.rb +48 -0
  221. data/app/components/shadcn/pagination_previous_component.rb +48 -0
  222. data/app/components/shadcn/popover_component.rb +76 -0
  223. data/app/components/shadcn/popover_content_component.rb +25 -0
  224. data/app/components/shadcn/progress_component.rb +77 -0
  225. data/app/components/shadcn/radio_group_component.rb +129 -0
  226. data/app/components/shadcn/radio_group_item_component.rb +109 -0
  227. data/app/components/shadcn/resizable_handle_component.rb +98 -0
  228. data/app/components/shadcn/resizable_panel_component.rb +56 -0
  229. data/app/components/shadcn/resizable_panel_group_component.rb +94 -0
  230. data/app/components/shadcn/scroll_area_component.rb +110 -0
  231. data/app/components/shadcn/select_component.rb +151 -0
  232. data/app/components/shadcn/select_group_component.rb +32 -0
  233. data/app/components/shadcn/select_item_component.rb +59 -0
  234. data/app/components/shadcn/select_separator_component.rb +12 -0
  235. data/app/components/shadcn/separator_component.rb +54 -0
  236. data/app/components/shadcn/sheet_component.rb +82 -0
  237. data/app/components/shadcn/sheet_content_component.rb +95 -0
  238. data/app/components/shadcn/sheet_description_component.rb +12 -0
  239. data/app/components/shadcn/sheet_footer_component.rb +12 -0
  240. data/app/components/shadcn/sheet_header_component.rb +19 -0
  241. data/app/components/shadcn/sheet_title_component.rb +12 -0
  242. data/app/components/shadcn/sidebar_component.rb +180 -0
  243. data/app/components/shadcn/sidebar_content_component.rb +32 -0
  244. data/app/components/shadcn/sidebar_footer_component.rb +24 -0
  245. data/app/components/shadcn/sidebar_group_action_component.rb +26 -0
  246. data/app/components/shadcn/sidebar_group_component.rb +38 -0
  247. data/app/components/shadcn/sidebar_group_content_component.rb +32 -0
  248. data/app/components/shadcn/sidebar_group_label_component.rb +25 -0
  249. data/app/components/shadcn/sidebar_header_component.rb +24 -0
  250. data/app/components/shadcn/sidebar_inset_component.rb +25 -0
  251. data/app/components/shadcn/sidebar_menu_action_component.rb +37 -0
  252. data/app/components/shadcn/sidebar_menu_badge_component.rb +25 -0
  253. data/app/components/shadcn/sidebar_menu_button_component.rb +52 -0
  254. data/app/components/shadcn/sidebar_menu_component.rb +32 -0
  255. data/app/components/shadcn/sidebar_menu_item_component.rb +41 -0
  256. data/app/components/shadcn/sidebar_menu_skeleton_component.rb +46 -0
  257. data/app/components/shadcn/sidebar_menu_sub_button_component.rb +43 -0
  258. data/app/components/shadcn/sidebar_menu_sub_component.rb +33 -0
  259. data/app/components/shadcn/sidebar_menu_sub_item_component.rb +30 -0
  260. data/app/components/shadcn/sidebar_provider_component.rb +57 -0
  261. data/app/components/shadcn/sidebar_rail_component.rb +30 -0
  262. data/app/components/shadcn/sidebar_separator_component.rb +24 -0
  263. data/app/components/shadcn/sidebar_trigger_component.rb +51 -0
  264. data/app/components/shadcn/skeleton_component.rb +29 -0
  265. data/app/components/shadcn/slider_component.rb +76 -0
  266. data/app/components/shadcn/spinner_component.rb +67 -0
  267. data/app/components/shadcn/switch_component.rb +147 -0
  268. data/app/components/shadcn/table_body_component.rb +16 -0
  269. data/app/components/shadcn/table_caption_component.rb +12 -0
  270. data/app/components/shadcn/table_cell_component.rb +12 -0
  271. data/app/components/shadcn/table_component.rb +57 -0
  272. data/app/components/shadcn/table_footer_component.rb +16 -0
  273. data/app/components/shadcn/table_head_component.rb +12 -0
  274. data/app/components/shadcn/table_header_component.rb +16 -0
  275. data/app/components/shadcn/table_row_component.rb +40 -0
  276. data/app/components/shadcn/tabs_component.rb +78 -0
  277. data/app/components/shadcn/tabs_content_component.rb +32 -0
  278. data/app/components/shadcn/tabs_list_component.rb +30 -0
  279. data/app/components/shadcn/tabs_trigger_component.rb +37 -0
  280. data/app/components/shadcn/textarea_component.rb +84 -0
  281. data/app/components/shadcn/toast_action_component.rb +18 -0
  282. data/app/components/shadcn/toast_component.rb +114 -0
  283. data/app/components/shadcn/toast_description_component.rb +12 -0
  284. data/app/components/shadcn/toast_title_component.rb +12 -0
  285. data/app/components/shadcn/toast_viewport_component.rb +12 -0
  286. data/app/components/shadcn/toggle_component.rb +77 -0
  287. data/app/components/shadcn/toggle_group_component.rb +96 -0
  288. data/app/components/shadcn/toggle_group_item_component.rb +62 -0
  289. data/app/components/shadcn/tooltip_component.rb +89 -0
  290. data/app/components/shadcn/typography_component.rb +112 -0
  291. data/babel.config.cjs +5 -0
  292. data/bin/console +11 -0
  293. data/bin/setup +8 -0
  294. data/config/importmap.rb +5 -0
  295. data/fly.toml +26 -0
  296. data/jest.config.js +19 -0
  297. data/jest.setup.js +8 -0
  298. data/lib/generators/shadcn/component/component_generator.rb +188 -0
  299. data/lib/generators/shadcn/install/install_generator.rb +140 -0
  300. data/lib/generators/shadcn/install/templates/initializer.rb.tt +35 -0
  301. data/lib/generators/shadcn/install/templates/shadcn.yml.tt +35 -0
  302. data/lib/generators/shadcn/theme/theme_generator.rb +128 -0
  303. data/lib/shadcn/rails/class_merger.rb +228 -0
  304. data/lib/shadcn/rails/configuration.rb +341 -0
  305. data/lib/shadcn/rails/engine.rb +59 -0
  306. data/lib/shadcn/rails/helpers/class_name_helper.rb +35 -0
  307. data/lib/shadcn/rails/helpers/component_helper.rb +60 -0
  308. data/lib/shadcn/rails/helpers/pagination_helper.rb +187 -0
  309. data/lib/shadcn/rails/version.rb +7 -0
  310. data/lib/shadcn/rails.rb +179 -0
  311. data/package-lock.json +7415 -0
  312. data/package.json +68 -0
  313. data/rollup.config.js +29 -0
  314. data/sig/shadcn/rails.rbs +6 -0
  315. metadata +526 -0
@@ -0,0 +1,60 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ /**
4
+ * Sheet controller for slide-out panels
5
+ */
6
+ export default class SheetController extends Controller {
7
+ static targets: ["trigger", "template", "overlay", "content"];
8
+ static values: {
9
+ open: { type: "Boolean"; default: false };
10
+ side: { type: "String"; default: "right" };
11
+ };
12
+
13
+ /** Sheet trigger target */
14
+ readonly triggerTarget: HTMLElement;
15
+ readonly hasTriggerTarget: boolean;
16
+
17
+ /** Template containing sheet content */
18
+ readonly templateTarget: HTMLTemplateElement;
19
+ readonly hasTemplateTarget: boolean;
20
+
21
+ /** Sheet overlay target */
22
+ readonly overlayTarget: HTMLElement;
23
+ readonly hasOverlayTarget: boolean;
24
+
25
+ /** Sheet content target */
26
+ readonly contentTarget: HTMLElement;
27
+ readonly hasContentTarget: boolean;
28
+
29
+ /** Whether the sheet is open */
30
+ openValue: boolean;
31
+ readonly hasOpenValue: boolean;
32
+
33
+ /** Side to display: "top", "right", "bottom", "left" */
34
+ sideValue: "top" | "right" | "bottom" | "left";
35
+ readonly hasSideValue: boolean;
36
+
37
+ /** Portal element (created dynamically) */
38
+ portal: HTMLDivElement | null;
39
+
40
+ /** Previously focused element */
41
+ previousActiveElement: Element | null;
42
+
43
+ /** Open the sheet */
44
+ open(): void;
45
+
46
+ /** Close the sheet */
47
+ close(): void;
48
+
49
+ /** Toggle sheet open/closed */
50
+ toggle(): void;
51
+
52
+ /** Handle keydown events (Escape to close, Tab for focus trapping) */
53
+ handleKeydown(event: KeyboardEvent): void;
54
+
55
+ /** Focus the first focusable element in the sheet */
56
+ focusFirstElement(): void;
57
+
58
+ /** Trap focus within the sheet */
59
+ trapFocus(event: KeyboardEvent): void;
60
+ }
@@ -0,0 +1,151 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ /**
4
+ * Sheet controller for slide-out panels
5
+ */
6
+ export default class extends Controller {
7
+ static targets = ["trigger", "template", "overlay", "content"]
8
+ static values = {
9
+ open: { type: Boolean, default: false },
10
+ side: { type: String, default: "right" }
11
+ }
12
+
13
+ connect() {
14
+ this.portal = null
15
+ this.previousActiveElement = null
16
+ this.boundHandleKeydown = this.handleKeydown.bind(this)
17
+
18
+ if (this.openValue) {
19
+ this.open()
20
+ }
21
+ }
22
+
23
+ disconnect() {
24
+ this.close()
25
+ if (this.portal) {
26
+ this.portal.remove()
27
+ }
28
+ }
29
+
30
+ open() {
31
+ if (this.openValue) return
32
+
33
+ this.previousActiveElement = document.activeElement
34
+ this.openValue = true
35
+
36
+ // Move template content to body
37
+ if (this.hasTemplateTarget && !this.portal) {
38
+ this.portal = document.createElement("div")
39
+ this.portal.className = "shadcn-sheet-portal"
40
+ this.portal.innerHTML = this.templateTarget.innerHTML
41
+ document.body.appendChild(this.portal)
42
+
43
+ this.portalOverlay = this.portal.querySelector('[data-shadcn--sheet-target="overlay"]')
44
+ this.portalContent = this.portal.querySelector('[data-shadcn--sheet-target="content"]')
45
+
46
+ // Re-attach event listeners for close buttons
47
+ const closeButtons = this.portal.querySelectorAll('[data-action*="shadcn--sheet#close"]')
48
+ closeButtons.forEach(btn => {
49
+ btn.addEventListener("click", () => this.close())
50
+ })
51
+ }
52
+
53
+ requestAnimationFrame(() => {
54
+ if (this.portalOverlay) {
55
+ this.portalOverlay.dataset.state = "open"
56
+ this.portalOverlay.removeAttribute("hidden")
57
+ }
58
+ if (this.portalContent) {
59
+ this.portalContent.dataset.state = "open"
60
+ this.portalContent.removeAttribute("hidden")
61
+ }
62
+
63
+ document.addEventListener("keydown", this.boundHandleKeydown)
64
+ document.body.style.overflow = "hidden"
65
+
66
+ this.focusFirstElement()
67
+ })
68
+
69
+ this.dispatch("opened")
70
+ }
71
+
72
+ close() {
73
+ if (!this.openValue) return
74
+
75
+ this.openValue = false
76
+
77
+ if (this.portalOverlay) {
78
+ this.portalOverlay.dataset.state = "closed"
79
+ }
80
+ if (this.portalContent) {
81
+ this.portalContent.dataset.state = "closed"
82
+ }
83
+
84
+ document.removeEventListener("keydown", this.boundHandleKeydown)
85
+ document.body.style.overflow = ""
86
+
87
+ if (this.previousActiveElement) {
88
+ this.previousActiveElement.focus()
89
+ }
90
+
91
+ setTimeout(() => {
92
+ if (this.portal) {
93
+ this.portal.remove()
94
+ this.portal = null
95
+ }
96
+ }, 300)
97
+
98
+ this.dispatch("closed")
99
+ }
100
+
101
+ toggle() {
102
+ if (this.openValue) {
103
+ this.close()
104
+ } else {
105
+ this.open()
106
+ }
107
+ }
108
+
109
+ handleKeydown(event) {
110
+ if (event.key === "Escape") {
111
+ this.close()
112
+ } else if (event.key === "Tab") {
113
+ this.trapFocus(event)
114
+ }
115
+ }
116
+
117
+ focusFirstElement() {
118
+ if (!this.portalContent) return
119
+
120
+ const focusable = this.portalContent.querySelectorAll(
121
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
122
+ )
123
+ if (focusable.length > 0) {
124
+ focusable[0].focus()
125
+ } else {
126
+ this.portalContent.focus()
127
+ }
128
+ }
129
+
130
+ trapFocus(event) {
131
+ if (!this.portalContent) return
132
+
133
+ const focusable = this.portalContent.querySelectorAll(
134
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
135
+ )
136
+ const firstFocusable = focusable[0]
137
+ const lastFocusable = focusable[focusable.length - 1]
138
+
139
+ if (event.shiftKey) {
140
+ if (document.activeElement === firstFocusable) {
141
+ lastFocusable.focus()
142
+ event.preventDefault()
143
+ }
144
+ } else {
145
+ if (document.activeElement === lastFocusable) {
146
+ firstFocusable.focus()
147
+ event.preventDefault()
148
+ }
149
+ }
150
+ }
151
+ }
@@ -0,0 +1,148 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ // Constants for sidebar dimensions
4
+ const SIDEBAR_COOKIE_NAME = "sidebar:state"
5
+ const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 // 7 days
6
+ const SIDEBAR_WIDTH = "16rem"
7
+ const SIDEBAR_WIDTH_MOBILE = "18rem"
8
+ const SIDEBAR_WIDTH_ICON = "3rem"
9
+
10
+ export default class extends Controller {
11
+ static targets = ["sidebar"]
12
+ static values = {
13
+ open: { type: Boolean, default: true },
14
+ openMobile: { type: Boolean, default: false },
15
+ keyboardShortcut: { type: String, default: "b" }
16
+ }
17
+
18
+ connect() {
19
+ // Set initial state from cookie if available
20
+ const savedState = this.getCookie(SIDEBAR_COOKIE_NAME)
21
+ if (savedState !== null) {
22
+ this.openValue = savedState === "true"
23
+ }
24
+
25
+ // Set up keyboard shortcut
26
+ this.handleKeyDown = this.handleKeyDown.bind(this)
27
+ document.addEventListener("keydown", this.handleKeyDown)
28
+
29
+ // Set up mobile detection
30
+ this.isMobile = window.innerWidth < 768
31
+ this.handleResize = this.handleResize.bind(this)
32
+ window.addEventListener("resize", this.handleResize)
33
+
34
+ // Initial state sync
35
+ this.syncState()
36
+ }
37
+
38
+ disconnect() {
39
+ document.removeEventListener("keydown", this.handleKeyDown)
40
+ window.removeEventListener("resize", this.handleResize)
41
+ }
42
+
43
+ handleKeyDown(event) {
44
+ // Check for Cmd/Ctrl + keyboard shortcut
45
+ if (
46
+ (event.metaKey || event.ctrlKey) &&
47
+ event.key.toLowerCase() === this.keyboardShortcutValue.toLowerCase()
48
+ ) {
49
+ event.preventDefault()
50
+ this.toggle()
51
+ }
52
+ }
53
+
54
+ handleResize() {
55
+ const wasMobile = this.isMobile
56
+ this.isMobile = window.innerWidth < 768
57
+
58
+ // Close mobile sidebar when switching to desktop
59
+ if (wasMobile && !this.isMobile) {
60
+ this.openMobileValue = false
61
+ }
62
+ }
63
+
64
+ toggle() {
65
+ if (this.isMobile) {
66
+ this.openMobileValue = !this.openMobileValue
67
+ } else {
68
+ this.openValue = !this.openValue
69
+ }
70
+ }
71
+
72
+ setOpen(open) {
73
+ if (this.isMobile) {
74
+ this.openMobileValue = open
75
+ } else {
76
+ this.openValue = open
77
+ }
78
+ }
79
+
80
+ openValueChanged() {
81
+ this.syncState()
82
+ // Save to cookie
83
+ this.setCookie(SIDEBAR_COOKIE_NAME, String(this.openValue), SIDEBAR_COOKIE_MAX_AGE)
84
+ }
85
+
86
+ openMobileValueChanged() {
87
+ this.syncState()
88
+ }
89
+
90
+ syncState() {
91
+ const state = this.isMobile ? (this.openMobileValue ? "expanded" : "collapsed") : (this.openValue ? "expanded" : "collapsed")
92
+
93
+ // Update data attributes on the provider element
94
+ this.element.dataset.state = state
95
+ this.element.dataset.mobile = this.isMobile
96
+
97
+ // Update sidebar targets if they exist
98
+ if (this.hasSidebarTarget) {
99
+ this.sidebarTargets.forEach(sidebar => {
100
+ sidebar.dataset.state = state
101
+ sidebar.dataset.mobile = this.isMobile
102
+ })
103
+ }
104
+
105
+ // Dispatch custom event for other components to listen to
106
+ this.element.dispatchEvent(new CustomEvent("sidebar:state-change", {
107
+ bubbles: true,
108
+ detail: {
109
+ open: this.isMobile ? this.openMobileValue : this.openValue,
110
+ isMobile: this.isMobile,
111
+ state: state
112
+ }
113
+ }))
114
+ }
115
+
116
+ // Cookie helpers
117
+ getCookie(name) {
118
+ const value = `; ${document.cookie}`
119
+ const parts = value.split(`; ${name}=`)
120
+ if (parts.length === 2) {
121
+ return parts.pop().split(";").shift()
122
+ }
123
+ return null
124
+ }
125
+
126
+ setCookie(name, value, maxAge) {
127
+ document.cookie = `${name}=${value}; path=/; max-age=${maxAge}; SameSite=Lax`
128
+ }
129
+
130
+ // Actions for external triggers
131
+ open() {
132
+ this.setOpen(true)
133
+ }
134
+
135
+ close() {
136
+ this.setOpen(false)
137
+ }
138
+
139
+ // Handle clicking outside on mobile to close
140
+ clickOutside(event) {
141
+ if (this.isMobile && this.openMobileValue) {
142
+ const sidebar = this.sidebarTargets[0]
143
+ if (sidebar && !sidebar.contains(event.target)) {
144
+ this.openMobileValue = false
145
+ }
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,102 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ /**
4
+ * Slider Controller
5
+ * Handles slider value selection with drag and keyboard support
6
+ */
7
+ export default class SliderController extends Controller {
8
+ static targets: ["track", "range", "thumb", "input", "output"];
9
+ static values: {
10
+ min: { type: "Number"; default: 0 };
11
+ max: { type: "Number"; default: 100 };
12
+ step: { type: "Number"; default: 1 };
13
+ value: { type: "Number"; default: 0 };
14
+ name: "String";
15
+ disabled: { type: "Boolean"; default: false };
16
+ outputFormat: { type: "String"; default: "{value}" };
17
+ };
18
+
19
+ /** Slider track target */
20
+ readonly trackTarget: HTMLElement;
21
+ readonly hasTrackTarget: boolean;
22
+
23
+ /** Filled range target */
24
+ readonly rangeTarget: HTMLElement;
25
+ readonly hasRangeTarget: boolean;
26
+
27
+ /** Draggable thumb target */
28
+ readonly thumbTarget: HTMLElement;
29
+ readonly hasThumbTarget: boolean;
30
+
31
+ /** Hidden input target */
32
+ readonly inputTarget: HTMLInputElement;
33
+ readonly hasInputTarget: boolean;
34
+
35
+ /** Output display target */
36
+ readonly outputTarget: HTMLElement;
37
+ readonly hasOutputTarget: boolean;
38
+
39
+ /** Minimum value */
40
+ minValue: number;
41
+ readonly hasMinValue: boolean;
42
+
43
+ /** Maximum value */
44
+ maxValue: number;
45
+ readonly hasMaxValue: boolean;
46
+
47
+ /** Step increment */
48
+ stepValue: number;
49
+ readonly hasStepValue: boolean;
50
+
51
+ /** Current value */
52
+ valueValue: number;
53
+ readonly hasValueValue: boolean;
54
+
55
+ /** Input name for form submission */
56
+ nameValue: string;
57
+ readonly hasNameValue: boolean;
58
+
59
+ /** Whether slider is disabled */
60
+ disabledValue: boolean;
61
+ readonly hasDisabledValue: boolean;
62
+
63
+ /** Format string for output (use {value} for value, {percent} for percentage) */
64
+ outputFormatValue: string;
65
+ readonly hasOutputFormatValue: boolean;
66
+
67
+ /** Whether currently dragging */
68
+ isDragging: boolean;
69
+
70
+ /** Start drag operation */
71
+ startDrag(event: MouseEvent | TouchEvent): void;
72
+
73
+ /** Handle drag movement */
74
+ handleDrag(event: MouseEvent | TouchEvent): void;
75
+
76
+ /** Stop drag operation */
77
+ stopDrag(): void;
78
+
79
+ /** Handle keyboard navigation */
80
+ handleKeydown(event: KeyboardEvent): void;
81
+
82
+ /** Snap value to step */
83
+ snapToStep(value: number): number;
84
+
85
+ /** Update visual elements */
86
+ updateVisuals(): void;
87
+
88
+ /** Update output element */
89
+ updateOutput(): void;
90
+
91
+ /** Dispatch change event */
92
+ dispatchChange(): void;
93
+
94
+ /** Get current percentage */
95
+ readonly percentage: number;
96
+
97
+ /** Called when valueValue changes */
98
+ valueValueChanged(): void;
99
+
100
+ /** Update style for native input range element */
101
+ updateStyle(event: Event): void;
102
+ }