ariadne_view_components 0.0.59 → 0.0.65

Sign up to get free protection for your applications and to get access to all the features.
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