ariadne_view_components 0.0.59 → 0.0.65

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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -0
  3. data/LICENSE.txt +661 -49
  4. data/README.md +52 -4
  5. data/app/assets/javascripts/ariadne_view_components.js +98 -7
  6. data/app/assets/javascripts/ariadne_view_components.js.br +0 -0
  7. data/app/assets/javascripts/ariadne_view_components.js.gz +0 -0
  8. data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
  9. data/app/assets/stylesheets/ariadne_view_components.css +1 -7
  10. data/app/assets/stylesheets/ariadne_view_components.css.br +0 -0
  11. data/app/assets/stylesheets/ariadne_view_components.css.gz +0 -0
  12. data/app/components/ariadne/base_component.rb +79 -27
  13. data/app/components/ariadne/behaviors/tooltipable.rb +120 -0
  14. data/app/components/ariadne/conditional_wrapper.rb +21 -0
  15. data/app/components/ariadne/form/base_component.rb +74 -0
  16. data/app/components/ariadne/form/base_input_component.rb +60 -0
  17. data/app/components/ariadne/form/caption/component.html.erb +10 -0
  18. data/app/components/ariadne/form/caption/component.rb +29 -0
  19. data/app/components/ariadne/form/form_control/component.html.erb +19 -0
  20. data/app/components/ariadne/form/form_control/component.rb +27 -0
  21. data/app/components/ariadne/form/form_reference/component.html.erb +1 -0
  22. data/app/components/ariadne/form/form_reference/component.rb +18 -0
  23. data/app/components/ariadne/form/group/component.html.erb +5 -0
  24. data/app/components/ariadne/form/group/component.rb +27 -0
  25. data/app/components/ariadne/form/hidden_field/component.html.erb +1 -0
  26. data/app/components/ariadne/form/hidden_field/component.rb +15 -0
  27. data/app/components/ariadne/form/separator/component.html.erb +1 -0
  28. data/app/components/ariadne/form/separator/component.rb +8 -0
  29. data/app/components/ariadne/form/spacing_wrapper/component.html.erb +3 -0
  30. data/app/components/ariadne/form/spacing_wrapper/component.rb +8 -0
  31. data/app/components/ariadne/form/text_field/component.html.erb +25 -0
  32. data/app/components/ariadne/form/text_field/component.rb +132 -0
  33. data/app/components/ariadne/form/validation_message/component.html.erb +5 -0
  34. data/app/components/ariadne/form/validation_message/component.rb +14 -0
  35. data/app/components/ariadne/layout/narrow/component.html.erb +10 -0
  36. data/app/components/ariadne/layout/narrow/component.rb +24 -0
  37. data/app/components/ariadne/layout/nav_bar/component.css +0 -0
  38. data/app/components/ariadne/layout/nav_bar/component.html.erb +123 -0
  39. data/app/components/ariadne/layout/nav_bar/component.rb +77 -0
  40. data/app/components/ariadne/ui/button/component.html.erb +5 -0
  41. data/app/components/ariadne/ui/button/component.rb +184 -0
  42. data/app/components/ariadne/ui/clipboard_copy/component.html.erb +8 -0
  43. data/app/components/ariadne/ui/clipboard_copy/component.rb +102 -0
  44. data/app/components/ariadne/ui/clipboard_copy/component.ts +54 -0
  45. data/app/components/ariadne/ui/combobox/component.html.erb +32 -0
  46. data/app/components/ariadne/ui/combobox/component.rb +83 -0
  47. data/app/components/ariadne/ui/combobox/component.ts +119 -0
  48. data/app/components/ariadne/ui/combobox/menu_item/component.html.erb +9 -0
  49. data/app/components/ariadne/ui/combobox/menu_item/component.rb +53 -0
  50. data/app/components/ariadne/ui/combobox/option/component.html.erb +11 -0
  51. data/app/components/ariadne/ui/combobox/option/component.rb +45 -0
  52. data/app/components/ariadne/ui/heroicon/component.html.erb +3 -0
  53. data/app/components/ariadne/ui/heroicon/component.rb +141 -0
  54. data/app/components/ariadne/ui/image/component.rb +69 -0
  55. data/app/components/ariadne/ui/link/component.html.erb +3 -0
  56. data/app/components/ariadne/ui/link/component.rb +56 -0
  57. data/app/components/ariadne/ui/typography/component.html.erb +3 -0
  58. data/app/components/ariadne/ui/typography/component.rb +41 -0
  59. data/app/frontend/ariadne/index.ts +14 -0
  60. data/app/frontend/ariadne/stimulus_app.ts +53 -0
  61. data/app/frontend/ariadne/theme.ts +8 -0
  62. data/app/frontend/controllers/tooltip.ts +75 -0
  63. data/app/frontend/entrypoints/application.ts +1 -0
  64. data/app/frontend/stylesheets/ariadne_view_components.css +18 -0
  65. data/app/frontend/stylesheets/scrollbar.css +28 -0
  66. data/app/frontend/stylesheets/tippy.js/themes/tomato.css +4 -0
  67. data/app/frontend/stylesheets/typography.css +117 -0
  68. data/app/frontend/utils/createController.ts +95 -0
  69. data/app/helpers/ariadne/form_helper.rb +31 -0
  70. data/app/lib/ariadne/attributes_helper.rb +119 -0
  71. data/app/lib/ariadne/fetch_or_fallback_helper.rb +1 -1
  72. data/app/lib/ariadne/form.rb +16 -0
  73. data/app/lib/ariadne/view_helper.rb +2 -5
  74. data/app/lib/view_components_contrib/html_attrs.rb +64 -0
  75. data/app/lib/view_components_contrib/style_variants.rb +14 -0
  76. data/lib/ariadne/forms/acts_as_component.rb +125 -0
  77. data/lib/ariadne/forms/base.html.erb +8 -0
  78. data/lib/ariadne/forms/base.rb +132 -0
  79. data/lib/ariadne/forms/buffer_rewriter.rb +51 -0
  80. data/lib/ariadne/forms/builder.rb +88 -0
  81. data/lib/ariadne/forms/dsl/button_input.rb +33 -0
  82. data/lib/ariadne/forms/dsl/form_object.rb +26 -0
  83. data/lib/ariadne/forms/dsl/input.rb +322 -0
  84. data/lib/ariadne/forms/dsl/input_group.rb +34 -0
  85. data/lib/ariadne/forms/dsl/input_methods.rb +157 -0
  86. data/lib/ariadne/forms/dsl/submit_button_input.rb +36 -0
  87. data/lib/ariadne/forms/dsl/text_field_input.rb +73 -0
  88. data/lib/ariadne/forms/utils.rb +34 -0
  89. data/lib/ariadne/generate.rb +11 -0
  90. data/lib/ariadne/view_components/engine.rb +24 -7
  91. data/lib/ariadne/view_components/version.rb +1 -1
  92. data/lib/ariadne/view_components.rb +1 -1
  93. data/lib/ariadne/yard/backend.rb +24 -0
  94. data/lib/ariadne/yard/component_manifest.rb +148 -0
  95. data/lib/ariadne/yard/component_ref.rb +49 -0
  96. data/lib/ariadne/yard/docs_helper.rb +98 -0
  97. data/lib/ariadne/yard/info_arch_docs_helper.rb +31 -0
  98. data/lib/ariadne/yard/lookbook_docs_helper.rb +32 -0
  99. data/lib/ariadne/yard/lookbook_pages_backend.rb +235 -0
  100. data/lib/ariadne/yard/registry.rb +136 -0
  101. data/lib/ariadne/yard/renders_many_handler.rb +23 -0
  102. data/lib/ariadne/yard/renders_one_handler.rb +23 -0
  103. data/lib/ariadne/yard.rb +19 -0
  104. data/static/arguments.yml +141 -48
  105. data/static/audited_at.json +0 -9
  106. data/static/classes.yml +210 -209
  107. data/static/constants.json +2 -209
  108. data/static/statuses.json +0 -9
  109. metadata +140 -210
  110. data/app/assets/builds/ariadne_view_components.css +0 -2202
  111. data/app/assets/javascripts/components/ariadne/accumulator_controller/accumulator_controller.d.ts +0 -22
  112. data/app/assets/javascripts/components/ariadne/ariadne-form.d.ts +0 -22
  113. data/app/assets/javascripts/components/ariadne/ariadne.d.ts +0 -2
  114. data/app/assets/javascripts/components/ariadne/clipboard_copy_component/clipboard-copy-component.d.ts +0 -4
  115. data/app/assets/javascripts/components/ariadne/dropdown/menu_component.d.ts +0 -1
  116. data/app/assets/javascripts/components/ariadne/events_controller/events_controller.d.ts +0 -4
  117. data/app/assets/javascripts/components/ariadne/options_controller/options_controller.d.ts +0 -39
  118. data/app/assets/javascripts/components/ariadne/outlet_manager_controller/outlet_manager_controller.d.ts +0 -42
  119. data/app/assets/javascripts/components/ariadne/slideover_component/slideover-component.d.ts +0 -9
  120. data/app/assets/javascripts/components/ariadne/string_match_controller/string_match_controller.d.ts +0 -27
  121. data/app/assets/javascripts/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.d.ts +0 -48
  122. data/app/assets/javascripts/components/ariadne/tab_container_component/tab-container-component.d.ts +0 -1
  123. data/app/assets/javascripts/components/ariadne/tab_nav_component/tab-nav-component.d.ts +0 -9
  124. data/app/assets/javascripts/components/ariadne/time_ago_component/time-ago-component.d.ts +0 -1
  125. data/app/assets/javascripts/components/ariadne/toggleable_controller/toggleable_controller.d.ts +0 -34
  126. data/app/assets/javascripts/components/ariadne/tooltip_component/tooltip-component.d.ts +0 -24
  127. data/app/assets/stylesheets/dropdown.css +0 -46
  128. data/app/assets/stylesheets/prosemirror.css +0 -323
  129. data/app/assets/stylesheets/tooltip-component.css +0 -37
  130. data/app/components/ariadne/accumulator_controller/accumulator_controller.d.ts +0 -22
  131. data/app/components/ariadne/accumulator_controller/accumulator_controller.js +0 -39
  132. data/app/components/ariadne/accumulator_controller/accumulator_controller.ts +0 -48
  133. data/app/components/ariadne/action_card_component.html.erb +0 -13
  134. data/app/components/ariadne/action_card_component.rb +0 -88
  135. data/app/components/ariadne/ariadne-form.d.ts +0 -22
  136. data/app/components/ariadne/ariadne-form.js +0 -85
  137. data/app/components/ariadne/ariadne.d.ts +0 -2
  138. data/app/components/ariadne/ariadne.js +0 -24
  139. data/app/components/ariadne/ariadne.ts +0 -29
  140. data/app/components/ariadne/avatar_component.rb +0 -81
  141. data/app/components/ariadne/avatar_stack_component/avatar_stack_component.html.erb +0 -12
  142. data/app/components/ariadne/avatar_stack_component.rb +0 -75
  143. data/app/components/ariadne/base_button.rb +0 -70
  144. data/app/components/ariadne/blankslate_component/blankslate_component.html.erb +0 -26
  145. data/app/components/ariadne/blankslate_component.rb +0 -148
  146. data/app/components/ariadne/body_component.rb +0 -30
  147. data/app/components/ariadne/bottom_tab_component.html.erb +0 -4
  148. data/app/components/ariadne/bottom_tab_component.rb +0 -44
  149. data/app/components/ariadne/bottom_tab_nav_component.html.erb +0 -5
  150. data/app/components/ariadne/bottom_tab_nav_component.rb +0 -33
  151. data/app/components/ariadne/breadcrumbs_component.html.erb +0 -13
  152. data/app/components/ariadne/breadcrumbs_component.rb +0 -31
  153. data/app/components/ariadne/button_component/button_component.html.erb +0 -4
  154. data/app/components/ariadne/button_component.rb +0 -165
  155. data/app/components/ariadne/checkbox_component.html.erb +0 -5
  156. data/app/components/ariadne/checkbox_component.rb +0 -43
  157. data/app/components/ariadne/clipboard_copy_component/clipboard-copy-component.d.ts +0 -4
  158. data/app/components/ariadne/clipboard_copy_component/clipboard-copy-component.js +0 -18
  159. data/app/components/ariadne/clipboard_copy_component/clipboard-copy-component.ts +0 -19
  160. data/app/components/ariadne/clipboard_copy_component/clipboard_copy_component.html.erb +0 -9
  161. data/app/components/ariadne/clipboard_copy_component.rb +0 -90
  162. data/app/components/ariadne/close_button_component.html.erb +0 -4
  163. data/app/components/ariadne/close_button_component.rb +0 -33
  164. data/app/components/ariadne/combobox_component.html.erb +0 -14
  165. data/app/components/ariadne/combobox_component.rb +0 -76
  166. data/app/components/ariadne/component.rb +0 -127
  167. data/app/components/ariadne/container_component/container_component.html.erb +0 -3
  168. data/app/components/ariadne/container_component.rb +0 -25
  169. data/app/components/ariadne/content.rb +0 -12
  170. data/app/components/ariadne/counter_component.rb +0 -100
  171. data/app/components/ariadne/details_component/details_component.html.erb +0 -4
  172. data/app/components/ariadne/details_component.rb +0 -81
  173. data/app/components/ariadne/dropdown/menu_component.d.ts +0 -1
  174. data/app/components/ariadne/dropdown/menu_component.html.erb +0 -20
  175. data/app/components/ariadne/dropdown/menu_component.js +0 -1
  176. data/app/components/ariadne/dropdown/menu_component.rb +0 -101
  177. data/app/components/ariadne/dropdown/menu_component.ts +0 -1
  178. data/app/components/ariadne/dropdown_component/dropdown_component.html.erb +0 -8
  179. data/app/components/ariadne/dropdown_component.rb +0 -172
  180. data/app/components/ariadne/events_controller/events_controller.d.ts +0 -4
  181. data/app/components/ariadne/events_controller/events_controller.js +0 -6
  182. data/app/components/ariadne/events_controller/events_controller.ts +0 -7
  183. data/app/components/ariadne/flash_component/flash_component.html.erb +0 -31
  184. data/app/components/ariadne/flash_component.rb +0 -128
  185. data/app/components/ariadne/flex_component/flex_component.html.erb +0 -5
  186. data/app/components/ariadne/flex_component.rb +0 -56
  187. data/app/components/ariadne/footer_component/footer_component.html.erb +0 -7
  188. data/app/components/ariadne/footer_component.rb +0 -23
  189. data/app/components/ariadne/grid_component/grid_component.html.erb +0 -26
  190. data/app/components/ariadne/grid_component.rb +0 -67
  191. data/app/components/ariadne/header_component/header_component.html.erb +0 -29
  192. data/app/components/ariadne/header_component.rb +0 -111
  193. data/app/components/ariadne/heading_component.rb +0 -49
  194. data/app/components/ariadne/heroicon_component/heroicon_component.html.erb +0 -4
  195. data/app/components/ariadne/heroicon_component.rb +0 -166
  196. data/app/components/ariadne/image_component.rb +0 -53
  197. data/app/components/ariadne/inline_flex_component/inline_flex_component.html.erb +0 -6
  198. data/app/components/ariadne/inline_flex_component.rb +0 -72
  199. data/app/components/ariadne/layout_component.html.erb +0 -21
  200. data/app/components/ariadne/layout_component.rb +0 -69
  201. data/app/components/ariadne/link_component.rb +0 -65
  202. data/app/components/ariadne/list_component/list_component.html.erb +0 -3
  203. data/app/components/ariadne/list_component.rb +0 -70
  204. data/app/components/ariadne/modal_component.html.erb +0 -11
  205. data/app/components/ariadne/modal_component.rb +0 -88
  206. data/app/components/ariadne/narrow_container_component/narrow_container_component.html.erb +0 -3
  207. data/app/components/ariadne/narrow_container_component.rb +0 -30
  208. data/app/components/ariadne/options_controller/options_controller.d.ts +0 -39
  209. data/app/components/ariadne/options_controller/options_controller.js +0 -89
  210. data/app/components/ariadne/options_controller/options_controller.ts +0 -122
  211. data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.d.ts +0 -42
  212. data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.js +0 -237
  213. data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.ts +0 -278
  214. data/app/components/ariadne/panel_bar_component/panel_bar_component.html.erb +0 -20
  215. data/app/components/ariadne/panel_bar_component.rb +0 -80
  216. data/app/components/ariadne/pill_component/pill_component.html.erb +0 -3
  217. data/app/components/ariadne/pill_component.rb +0 -44
  218. data/app/components/ariadne/popover_component.html.erb +0 -10
  219. data/app/components/ariadne/popover_component.rb +0 -81
  220. data/app/components/ariadne/progress_bar_component.html.erb +0 -5
  221. data/app/components/ariadne/progress_bar_component.rb +0 -63
  222. data/app/components/ariadne/relative_time_component.html.erb +0 -3
  223. data/app/components/ariadne/relative_time_component.rb +0 -61
  224. data/app/components/ariadne/show_more_button_component.html.erb +0 -11
  225. data/app/components/ariadne/show_more_button_component.rb +0 -47
  226. data/app/components/ariadne/slideover_component/slideover-component.d.ts +0 -9
  227. data/app/components/ariadne/slideover_component/slideover-component.js +0 -11
  228. data/app/components/ariadne/slideover_component/slideover-component.ts +0 -17
  229. data/app/components/ariadne/slideover_component/slideover_component.html.erb +0 -9
  230. data/app/components/ariadne/slideover_component.rb +0 -66
  231. data/app/components/ariadne/spinner_component.html.erb +0 -16
  232. data/app/components/ariadne/spinner_component.rb +0 -45
  233. data/app/components/ariadne/string_match_controller/string_match_controller.d.ts +0 -27
  234. data/app/components/ariadne/string_match_controller/string_match_controller.js +0 -51
  235. data/app/components/ariadne/string_match_controller/string_match_controller.ts +0 -65
  236. data/app/components/ariadne/subheader_component.html.erb +0 -11
  237. data/app/components/ariadne/subheader_component.rb +0 -65
  238. data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.d.ts +0 -48
  239. data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.js +0 -207
  240. data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.ts +0 -256
  241. data/app/components/ariadne/tab_component/tab_component.html.erb +0 -3
  242. data/app/components/ariadne/tab_component.rb +0 -98
  243. data/app/components/ariadne/tab_container_component/tab-container-component.d.ts +0 -1
  244. data/app/components/ariadne/tab_container_component/tab-container-component.js +0 -23
  245. data/app/components/ariadne/tab_container_component/tab-container-component.ts +0 -24
  246. data/app/components/ariadne/tab_container_component.erb +0 -10
  247. data/app/components/ariadne/tab_container_component.rb +0 -68
  248. data/app/components/ariadne/tab_nav_component/tab-nav-component.d.ts +0 -9
  249. data/app/components/ariadne/tab_nav_component/tab-nav-component.js +0 -33
  250. data/app/components/ariadne/tab_nav_component/tab-nav-component.ts +0 -34
  251. data/app/components/ariadne/tab_nav_component/tab_nav_component.html.erb +0 -7
  252. data/app/components/ariadne/tab_nav_component.rb +0 -72
  253. data/app/components/ariadne/table_nav_component/table_nav_component.html.erb +0 -52
  254. data/app/components/ariadne/table_nav_component.rb +0 -338
  255. data/app/components/ariadne/text.rb +0 -25
  256. data/app/components/ariadne/time_ago_component/time-ago-component.d.ts +0 -1
  257. data/app/components/ariadne/time_ago_component/time-ago-component.js +0 -1
  258. data/app/components/ariadne/time_ago_component/time-ago-component.ts +0 -1
  259. data/app/components/ariadne/time_ago_component.rb +0 -56
  260. data/app/components/ariadne/timeline_component/timeline_component.html.erb +0 -19
  261. data/app/components/ariadne/timeline_component.rb +0 -34
  262. data/app/components/ariadne/toggle_component/toggle_component.html.erb +0 -15
  263. data/app/components/ariadne/toggle_component.rb +0 -95
  264. data/app/components/ariadne/toggleable_controller/toggleable_controller.d.ts +0 -34
  265. data/app/components/ariadne/toggleable_controller/toggleable_controller.js +0 -54
  266. data/app/components/ariadne/toggleable_controller/toggleable_controller.ts +0 -77
  267. data/app/components/ariadne/tooltip_component/tooltip-component.d.ts +0 -24
  268. data/app/components/ariadne/tooltip_component/tooltip-component.js +0 -43
  269. data/app/components/ariadne/tooltip_component/tooltip-component.ts +0 -57
  270. data/app/components/ariadne/tooltip_component/tooltip_component.html.erb +0 -4
  271. data/app/components/ariadne/tooltip_component.rb +0 -108
  272. data/app/lib/ariadne/action_view_extensions/form_helper.rb +0 -30
  273. data/app/lib/ariadne/audited/dsl.rb +0 -32
  274. data/app/lib/ariadne/form_builder.rb +0 -80
  275. data/app/lib/ariadne/status/dsl.rb +0 -41
  276. data/config/importmap.rb +0 -3
  277. data/exe/tailwindcss +0 -21
  278. data/lib/rubocop/cop/ariadne/base_cop.rb +0 -26
  279. data/tailwind.config.js +0 -70
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # :nodoc:
5
+ module AttributesHelper
6
+ PLURAL_ARIA_ATTRIBUTES = [:describedby, :labelledby].freeze
7
+ PLURAL_DATA_ATTRIBUTES = [:target, :targets].freeze
8
+
9
+ def prepend_controller(html_attrs, name = stimulus_name)
10
+ prepend_data_attribute(html_attrs, :controller, name)
11
+ end
12
+
13
+ def prepend_action(html_attrs, action)
14
+ prepend_data_attribute(html_attrs, :action, action)
15
+ end
16
+
17
+ def prepend_data_attribute(html_attrs, attr_name, attr_value)
18
+ html_attrs[:data] ||= {}
19
+ html_attrs[:data][attr_name] = "#{attr_value} #{html_attrs[:data][attr_name]}".strip
20
+ html_attrs
21
+ end
22
+
23
+ def aria(html_attrs, val)
24
+ html_attrs[:"aria-#{val}"] || html_attrs.dig(:aria, val.to_sym)
25
+ end
26
+
27
+ def data(html_attrs, val)
28
+ html_attrs[:"data-#{val}"] || html_attrs.dig(:data, val.to_sym)
29
+ end
30
+
31
+ # Merges hashes that contain "aria-*" keys and nested aria: hashes. Removes keys from
32
+ # each hash and returns them in the new hash.
33
+ #
34
+ # Eg. merge_aria({ "aria-disabled": "true" }, { aria: { invalid: "true" } })
35
+ # => { disabled: "true", invalid: "true" }
36
+ #
37
+ # Certain aria attributes can contain multiple values separated by spaces. merge_aria
38
+ # will combine these plural attributes into a composite string.
39
+ #
40
+ # Eg. merge_aria({ "aria-labelledby": "foo" }, { aria: { labelledby: "bar" } })
41
+ # => { labelledby: "foo bar" }
42
+ #
43
+ # It's designed to be used to normalize and merge aria information from system_arguments
44
+ # hashes. Consider using this pattern in component initializers:
45
+ #
46
+ # @system_arguments[:aria] = merge_aria(
47
+ # @system_arguments,
48
+ # { aria: { labelled_by: id } }
49
+ # )
50
+ def merge_aria(*hashes)
51
+ merge_prefixed_attribute_hashes(
52
+ *hashes, prefix: :aria, plural_keys: PLURAL_ARIA_ATTRIBUTES
53
+ )
54
+ end
55
+
56
+ # Merges hashes that contain "data-*" keys and nested data: hashes. Removes keys from
57
+ # each hash and returns them in the new hash.
58
+ #
59
+ # Eg. merge_data({ "data-foo": "true" }, { data: { bar: "true" } })
60
+ # => { foo: "true", bar: "true" }
61
+ #
62
+ # Certain data attributes can contain multiple values separated by spaces. merge_data
63
+ # will combine these plural attributes into a composite string.
64
+ #
65
+ # Eg. merge_data({ "data-target": "foo" }, { data: { target: "bar" } })
66
+ # => { target: "foo bar" }
67
+ #
68
+ # It's designed to be used to normalize and merge data information from system_arguments
69
+ # hashes. Consider using this pattern in component initializers:
70
+ #
71
+ # @system_arguments[:data] = merge_aria(
72
+ # @system_arguments,
73
+ # { data: { foo: "bar" } }
74
+ # )
75
+ def merge_data(*hashes)
76
+ merge_prefixed_attribute_hashes(
77
+ *hashes, prefix: :data, plural_keys: PLURAL_DATA_ATTRIBUTES
78
+ )
79
+ end
80
+
81
+ def merge_prefixed_attribute_hashes(*hashes, prefix:, plural_keys:)
82
+ {}.tap do |result|
83
+ hashes.each do |hash|
84
+ next unless hash
85
+
86
+ prefix_hash = hash.delete(prefix) || {}
87
+
88
+ prefix_hash.each_pair do |key, val|
89
+ result[key] =
90
+ if plural_keys.include?(key)
91
+ [*(result[key] || "").split, val].join(" ").strip
92
+ else
93
+ val
94
+ end
95
+ end
96
+
97
+ hash.delete_if do |key, val|
98
+ key_s = key.to_s
99
+
100
+ if key.start_with?("#{prefix}-")
101
+ bare_key = key_s.sub("#{prefix}-", "").to_sym
102
+
103
+ result[bare_key] =
104
+ if plural_keys.include?(bare_key)
105
+ [*(result[bare_key] || "").split, val].join(" ").strip
106
+ else
107
+ val
108
+ end
109
+
110
+ true
111
+ else
112
+ false
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -34,7 +34,7 @@ module Ariadne
34
34
  check_against_given_value = against || given_value
35
35
  if allowed_values.include?(check_against_given_value)
36
36
  given_value
37
- else
37
+ elsif fallback_raises
38
38
  raise InvalidValueError, <<~MSG
39
39
  fetch_or_raise was called with an invalid value.
40
40
 
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # :nodoc:
5
+ module Form
6
+ def self.inline_form(builder, base = nil, &block)
7
+ base ||= defined?(ApplicationForm) ? ApplicationForm : Ariadne::Forms::Base
8
+
9
+ klass = Class.new(base) do
10
+ form(&block)
11
+ end
12
+
13
+ klass.new(builder)
14
+ end
15
+ end
16
+ end
@@ -7,14 +7,11 @@ module Ariadne
7
7
  class ViewHelperNotFound < StandardError; end
8
8
 
9
9
  HELPERS = {
10
- heroicon: "Ariadne::HeroiconComponent",
11
- heading: "Ariadne::HeadingComponent",
12
- time_ago: "Ariadne::TimeAgoComponent",
13
- image: "Ariadne::ImageComponent",
10
+ heroicon: "Ariadne::UI::Heroicon::Component",
14
11
  }.freeze
15
12
 
16
13
  HELPERS.each do |name, component|
17
- define_method "ariadne_#{name}" do |*args, **kwargs, &block|
14
+ define_method :"ariadne_#{name}" do |*args, **kwargs, &block|
18
15
  render component.constantize.new(*args, **kwargs), &block
19
16
  end
20
17
  end
@@ -0,0 +1,64 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "action_view/helpers/tag_helper"
5
+
6
+ module ViewComponentContrib
7
+ module HTMLAttrs
8
+ class AttributesHash < ::Hash
9
+ TAG_BUILDER = ActionView::Helpers::TagHelper::TagBuilder.new(nil)
10
+ TAG_OPTIONS = ActionView::Helpers::TagHelper::TagBuilder.instance_method(:tag_options)
11
+
12
+ def to_html(nested: false)
13
+ # TODO: implement + change usage by @tag nested-attributes
14
+ tag_options(self)&.html_safe # rubocop:disable Rails/OutputSafety
15
+ end
16
+ private def tag_options(...) = TAG_OPTIONS.bind_call(TAG_BUILDER, ...)
17
+ end
18
+
19
+ extend ActiveSupport::Concern
20
+
21
+ class_methods do
22
+ def accepts_html_attributes_for(name, **defaults, &block)
23
+ html_attribute_accessors[name] = defaults
24
+
25
+ name = name.to_sym
26
+ method_name = :"#{name}_attrs"
27
+ ivar_name = :"@#{method_name}"
28
+
29
+ attr_reader(method_name)
30
+
31
+ mod = Module.new do
32
+ define_method(:initialize) do |**options|
33
+ value = self.class.default_html_attributes_for(name).deep_merge!(options.delete(method_name).to_h)
34
+ super(**options)
35
+ instance_exec(value, &block) if block
36
+ instance_variable_set(ivar_name, value)
37
+ end
38
+ end
39
+
40
+ prepend(mod)
41
+ end
42
+
43
+ def accepts_html_attributes(**defaults, &block)
44
+ accepts_html_attributes_for(:html, **defaults, &block)
45
+ end
46
+
47
+ def html_attribute_accessors
48
+ @html_attribute_accessors ||=
49
+ if superclass.respond_to?(:html_attribute_accessors)
50
+ superclass.html_attribute_accessors.deep_dup
51
+ else
52
+ {}
53
+ end
54
+ end
55
+
56
+ def default_html_attributes_for(name)
57
+ html_attribute_accessors.fetch(name).each_with_object(AttributesHash.new) do |(k, v), acc|
58
+ acc[k] = v.is_a?(Proc) ? v.call : v
59
+ acc
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponentContrib
4
+ module StyleVariants
5
+ module ClassMethods
6
+ # Returns the name of the default style set based on the class name:
7
+ # MyComponent::Component => my_component
8
+ # Namespaced::MyComponent => my_component
9
+ def default_style_name
10
+ @default_style_name ||= name.split("::")[-2].underscore.presence || "component"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module Ariadne
6
+ module Forms
7
+ # :nodoc:
8
+ module ActsAsComponent
9
+ # :nodoc:
10
+ module InstanceMethods
11
+ delegate :render, :content_tag, :output_buffer, to: :@view_context
12
+
13
+ def render_in(view_context, &block)
14
+ @view_context = view_context
15
+ before_render
16
+ perform_render(&block)
17
+ end
18
+
19
+ # This is necessary to restore the functionality changed by https://github.com/rails/rails/pull/47194.
20
+ # I would love to remove this at some point, perhaps if we ever decide to replace
21
+ # ActsAsComponent with view component.
22
+ def capture(*args, &block)
23
+ old_buffer = @view_context.output_buffer
24
+ @view_context.output_buffer = ActionView::OutputBuffer.new
25
+ @view_context.capture(*args, &block)
26
+ ensure
27
+ @view_context.output_buffer = old_buffer
28
+ end
29
+
30
+ # :nocov:
31
+ def perform_render(&_block)
32
+ raise NotImplementedError, "subclasses must implement ##{__method__}."
33
+ end
34
+ # :nocov:
35
+
36
+ def before_render; end
37
+
38
+ # :nocov:
39
+ def set_original_view_context(view_context)
40
+ @view_context = view_context
41
+ end
42
+ # :nocov:
43
+ end
44
+
45
+ def self.extended(base)
46
+ base.include(InstanceMethods)
47
+ end
48
+
49
+ TemplateGlob = Struct.new(:glob_pattern, :method_name, :on_compile_callback)
50
+ TemplateParams = Struct.new(:source, :identifier, :type, :format, keyword_init: true)
51
+
52
+ attr_accessor :template_root_path
53
+
54
+ def renders_templates(glob_pattern, method_name = nil, &block)
55
+ template_globs << TemplateGlob.new(glob_pattern, method_name, block)
56
+ end
57
+ alias_method :renders_template, :renders_templates
58
+
59
+ def compile!
60
+ # always recompile in dev
61
+ return if defined?(@compiled) && @compiled && !Rails.env.development?
62
+
63
+ template_globs.each do |template_glob|
64
+ compile_templates_in(template_glob)
65
+ end
66
+
67
+ @compiled = true
68
+ end
69
+
70
+ private
71
+
72
+ def template_globs
73
+ @template_globs ||= []
74
+ end
75
+
76
+ def compile_templates_in(template_glob)
77
+ pattern = if Pathname(template_glob.glob_pattern).absolute?
78
+ template_glob.glob_pattern
79
+ else
80
+ # skip compilation for anonymous form classes, as in tests
81
+ return unless template_root_path
82
+
83
+ File.join(template_root_path, template_glob.glob_pattern)
84
+ end
85
+
86
+ template_paths = Dir.glob(pattern)
87
+
88
+ raise "Cannot compile multiple templates with the same method name." if template_paths.size > 1 && template_glob.method_name
89
+
90
+ template_paths.each do |template_path|
91
+ method_name = template_glob.method_name
92
+ method_name ||= "render_#{File.basename(template_path).chomp(".html.erb")}"
93
+ define_template_method(template_path, method_name)
94
+ template_glob&.on_compile_callback&.call(template_path)
95
+ end
96
+ end
97
+
98
+ def define_template_method(template_path, method_name)
99
+ class_eval(<<-RUBY, template_path, 0)
100
+ private def #{method_name}
101
+ capture { #{compile_template(template_path)} }
102
+ end
103
+ RUBY
104
+ # rubocop:enable Style/EvalWithLocation
105
+ # rubocop:enable Style/DocumentDynamicEvalDefinition
106
+ end
107
+
108
+ def compile_template(path)
109
+ handler = ActionView::Template.handler_for_extension("erb")
110
+ template = File.read(path)
111
+ template_params = TemplateParams.new({
112
+ source: template,
113
+ identifier: __FILE__,
114
+ type: "text/html",
115
+ format: "text/html",
116
+ })
117
+
118
+ # change @output_buffer ivar to output_buffer method call
119
+ BufferRewriter.rewrite(
120
+ handler.call(template_params, template),
121
+ )
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,8 @@
1
+ <div class="ariadne-flex ariadne-flex-col ariadne-gap-8">
2
+ <% inputs.each do |input| %>
3
+ <%= render(input.to_component) %>
4
+ <% end %>
5
+ </div>
6
+ <% if after_content? %>
7
+ <%= render_after_content %>
8
+ <% end %>
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ module Forms
5
+ # :nodoc:
6
+ class Base
7
+ extend ActsAsComponent
8
+
9
+ renders_template File.join(__dir__, "base.html.erb"), :render_base_form
10
+
11
+ class << self
12
+ attr_reader :has_after_content, :__vcf_form_block, :__vcf_builder
13
+ alias_method :after_content?, :has_after_content
14
+
15
+ def form(&block)
16
+ @__vcf_form_block = block
17
+ end
18
+
19
+ def new(builder, **options)
20
+ if builder && !builder.is_a?(Ariadne::Forms::Builder)
21
+ raise ArgumentError, "please pass an instance of Ariadne::Forms::Builder when " \
22
+ "constructing a form object (consider using the `ariadne_form_with` helper)"
23
+ end
24
+
25
+ allocate.tap do |form|
26
+ form.instance_variable_set(:@builder, builder)
27
+ form.send(:initialize, **options)
28
+ end
29
+ end
30
+
31
+ def inherited(base)
32
+ form_path = Ariadne::Forms::Utils.const_source_location(base.name)
33
+ return unless form_path
34
+
35
+ base.template_root_path = File.join(File.dirname(form_path), base.name.demodulize.underscore)
36
+
37
+ base.renders_template("after_content.html.erb") do
38
+ base.instance_variable_set(:@has_after_content, true)
39
+ end
40
+
41
+ base.renders_templates("*_caption.html.erb") do |path|
42
+ base.fields_with_caption_templates << File.basename(path).chomp("_caption.html.erb").to_sym
43
+ end
44
+ end
45
+
46
+ def caption_template?(field_name)
47
+ fields_with_caption_templates.include?(sanitize_field_name_for_template_path(field_name))
48
+ end
49
+
50
+ def fields_with_caption_templates
51
+ @fields_with_caption_templates ||= []
52
+ end
53
+
54
+ def sanitize_field_name_for_template_path(field_name)
55
+ field_name.to_s.delete_suffix("?").to_sym
56
+ end
57
+ end
58
+
59
+ def inputs
60
+ @inputs ||= form_object.inputs.map do |input|
61
+ next input unless input.input?
62
+
63
+ # wrap inputs in a group (unless they are already groups)
64
+ if input.type == :group
65
+ input
66
+ else
67
+ Ariadne::Forms::Dsl::InputGroup.new(builder: @builder, form: self) do |group|
68
+ group.send(:add_input, input)
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def each_input_in(root_input, &block)
75
+ return enum_for(__method__, root_input) unless block
76
+
77
+ root_input.inputs.each do |input|
78
+ if input.respond_to?(:inputs)
79
+ each_input_in(input, &block)
80
+ else
81
+ yield input
82
+ end
83
+ end
84
+ end
85
+
86
+ def before_render
87
+ each_input_in(self) do |input|
88
+ if input.input? && input.invalid? && input.focusable?
89
+ input.autofocus!
90
+ break
91
+ end
92
+ end
93
+ end
94
+
95
+ def caption_template?(*args)
96
+ self.class.caption_template?(*args)
97
+ end
98
+
99
+ def after_content?(*args)
100
+ self.class.after_content?(*args)
101
+ end
102
+
103
+ def render_caption_template(field_name)
104
+ send(:"render_#{self.class.sanitize_field_name_for_template_path(field_name)}_caption")
105
+ end
106
+
107
+ def perform_render(&_block)
108
+ return "" unless render?
109
+
110
+ Base.compile!
111
+ self.class.compile!
112
+
113
+ render_base_form
114
+ end
115
+
116
+ def render?
117
+ true
118
+ end
119
+
120
+ private
121
+
122
+ def form_object
123
+ @__pf_form_object ||= Ariadne::Forms::Dsl::FormObject.new(builder: @builder, form: self).tap do |obj|
124
+ # compile before adding inputs so caption templates are identified
125
+ self.class.compile!
126
+ instance_exec(obj, &self.class.__vcf_form_block)
127
+ end
128
+ # rubocop:enable Naming/MemoizedInstanceVariableName
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ripper"
4
+
5
+ module Ariadne
6
+ module Forms
7
+ # :nodoc:
8
+ class BufferRewriter < Ripper
9
+ class << self
10
+ def rewrite(code)
11
+ parser = new(code, "(code)", 0)
12
+ parser.parse
13
+
14
+ line_offsets = calc_line_offsets(code)
15
+
16
+ code.dup.tap do |result|
17
+ parser.var_refs.reverse_each do |lineno, stop|
18
+ line_offset = line_offsets[lineno]
19
+ stop += line_offset
20
+ stop -= 1 if stop < code.length
21
+ start = stop - "@output_buffer".length
22
+ result[start...stop] = "output_buffer"
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def calc_line_offsets(code)
30
+ idx = -1
31
+
32
+ [0].tap do |offsets|
33
+ while (idx = code.index(/\r?\n/, idx + 1))
34
+ offsets << Regexp.last_match.end(0)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def on_var_ref(var)
41
+ return unless var == "@output_buffer"
42
+
43
+ var_refs << [lineno, column]
44
+ end
45
+
46
+ def var_refs
47
+ @var_refs ||= []
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ # See: https://github.com/rails/rails/pull/46666
4
+ ActionView::Helpers::Tags::Base.prepend(
5
+ Module.new do
6
+ def initialize(*args, **kwargs, &block)
7
+ super
8
+
9
+ return if defined?(@generate_error_markup)
10
+
11
+ @generate_error_markup = @options.delete(:generate_error_markup) { true }
12
+ end
13
+
14
+ private
15
+
16
+ def error_wrapping(html_tag)
17
+ return html_tag unless @generate_error_markup
18
+
19
+ # :nocov:
20
+ super
21
+ # :nocov:
22
+ end
23
+ end,
24
+ )
25
+
26
+ module Ariadne
27
+ module Forms
28
+ module Tags
29
+ # :nodoc:
30
+ class TextField < ::ActionView::Helpers::Tags::TextField
31
+ def attributes
32
+ render
33
+ end
34
+
35
+ private
36
+
37
+ def tag(_name, options)
38
+ options
39
+ end
40
+ end
41
+ end
42
+
43
+ # :nodoc:
44
+ class Builder < ActionView::Helpers::FormBuilder
45
+ alias_method :ariadne_fields_for, :fields_for
46
+
47
+ def label(method, text = nil, **options, &block)
48
+ super(method, text, classify(options).merge(generate_error_markup: false), &block)
49
+ end
50
+
51
+ def check_box(method, options = {}, checked_value = 1, unchecked_value = 0, &block)
52
+ super(
53
+ method,
54
+ classify(options).merge(generate_error_markup: false),
55
+ checked_value,
56
+ unchecked_value,
57
+ &block
58
+ )
59
+ end
60
+
61
+ def radio_button(*args, **options, &block)
62
+ super(*args, classify(options).merge(generate_error_markup: false), &block)
63
+ end
64
+
65
+ def select(method, choices = nil, options = {}, html_options = {}, &block)
66
+ super(method, choices, options.merge(generate_error_markup: false), classify(html_options), &block)
67
+ end
68
+
69
+ def text_field(*args, **options, &block)
70
+ super(*args, classify(options).merge(generate_error_markup: false), &block)
71
+ end
72
+
73
+ def text_field_attributes(method, options = {})
74
+ Tags::TextField.new(@object_name, method, @template, options).attributes
75
+ end
76
+
77
+ def text_area(*args, **options, &block)
78
+ super(*args, classify(options).merge(generate_error_markup: false), &block)
79
+ end
80
+
81
+ private
82
+
83
+ def classify(options)
84
+ Ariadne::Forms::Utils.classify(options)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ module Forms
5
+ module Dsl
6
+ # :nodoc:
7
+ class ButtonInput < Input
8
+ attr_reader :name, :label, :block
9
+
10
+ def initialize(name:, label:, **system_arguments, &block)
11
+ @name = name
12
+ @label = label
13
+ @block = block
14
+
15
+ super(**system_arguments)
16
+ end
17
+
18
+ def to_component
19
+ Ariadne::UI::Button::Component.new(label: @label).with_content(@label)
20
+ end
21
+
22
+ # :nocov:
23
+ def type
24
+ :button
25
+ end
26
+
27
+ def supports_validation?
28
+ false
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end