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,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ module Forms
5
+ module Dsl
6
+ # :nodoc:
7
+ class FormObject
8
+ include InputMethods
9
+
10
+ attr_reader :builder, :form, :id
11
+
12
+ def initialize(builder:, form:, id: Ariadne::Generate.id)
13
+ @builder = builder
14
+ @form = form
15
+ @id = id
16
+
17
+ yield(self) if block_given?
18
+ end
19
+
20
+ def group(**options, &block)
21
+ add_input(InputGroup.new(builder: @builder, form: @form, **options, &block))
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,322 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ module Forms
5
+ module Dsl
6
+ # :nodoc:
7
+ class Input
8
+ # Use this macro anywhere you want to include the various params all inputs can receive.
9
+ #
10
+ # @!macro [new] form_input_attributes
11
+ # @param name [String] Value for the HTML name attribute.
12
+ # @param id [String] Value for the HTML id attribute.
13
+ # @param class [String] CSS classes to include in the input's HTML `class` attribute. Exists for compatibility with Rails form builders.
14
+ # @param classes [Array] CSS classes to include in the input's HTML `class` attribute. Combined with the `:class` argument. The list may contain strings, hashes, or `nil` values, and is automatically cleaned up by Ariadne's [`class_name` helper](https://github.com/primer/view_components/blob/c9cb95c98fee3e2e27f4a10683f555e22285e7f1/app/lib/primer/class_name_helper.rb) (`nils`, falsy entries, and blank strings are ignored).
15
+ # @param caption [String] A string describing the field and what sorts of input it expects. Displayed below the input.
16
+ # @param label [String] Label text displayed above the input.
17
+ # @param visually_hide_label [Boolean] When set to `true`, hides the label. Although the label will be hidden visually, it will still be visible to screen readers.
18
+ # @param disabled [Boolean] When set to `true`, the input will not accept keyboard or mouse input.
19
+ # @param hidden [Boolean] When set to `true`, visually hides the field.
20
+ # @param invalid [Boolean] If set to `true`, the input will be rendered with a red border. Implied if `validation_message` is truthy. This option is set to `true` automatically if the model object associated with the form reports that the input is invalid via Rails validations. It is provided for cases where the form does not have an associated model. If the input is invalid as determined by Rails validations, setting `invalid` to `false` will have no effect.
21
+ # @param validation_message [String] A string displayed between the caption and the input indicating the input's contents are invalid. This option is, by default, set to the first Rails validation message for the input (assuming the form is associated with a model object). Use `validation_message` to override the default or to provide a validation message in case there is no associated model object.
22
+ # @param label_attributes [Hash] Attributes that will be passed to Rails' `builder.label` method. These can be HTML attributes or any of the other label options Rails supports. They will appear as HTML attributes on the `<label>` tag.
23
+ # @param scope_name_to_model [Boolean] Default `true`. When set to `false`, prevents the model name from prefixing the field name. For example, if the field name is `my_field`, Rails will normally emit an HTML name attribute of `model[my_field]`. Setting `scope_name_to_model` to `false` will cause Rails to emit `my_field` instead.
24
+ # @param scope_id_to_model [Boolean] Default `true`. When set to `false`, prevents the model name from prefixing the field ID. For example, if the field name is `my_field`, Rails will normally emit an HTML ID attribute of `model_my_field`. Setting `scope_id_to_model` to `false` will cause Rails to emit `my_field` instead.
25
+ # @param required [Boolean] Default `false`. When set to `true`, causes an asterisk (*) to appear next to the field's label indicating it is a required field. Note that this option explicitly does _not_ add a `required` HTML attribute. Doing so would enable native browser validations, which are inaccessible and inconsistent with the Ariadne design system.
26
+ # @param aria [Hash] Key/value pairs that represent Aria attributes and their values. Eg. `aria: { current: true }` becomes `aria-current="true"`.
27
+ # @param data [Hash] Key/value pairs that represent data attributes and their values. Eg. `data: { foo: "bar" }` becomes `data-foo="bar"`.
28
+ # @macro form_system_arguments
29
+
30
+ # @!macro [new] form_size_arguments
31
+ # @param size [Symbol] The size of the field. <%= one_of(Ariadne::Forms::Dsl::Input::SIZE_OPTIONS) %>
32
+
33
+ # @!macro [new] form_full_width_arguments
34
+ # @param full_width [Boolean] When set to `true`, the field will take up all the horizontal space allowed by its container. Defaults to `true`.
35
+
36
+ # @!macro [new] form_system_arguments
37
+ # @param system_arguments [Hash] A hash of attributes passed to the underlying Rails builder methods. These options may mean something special depending on the type of input, otherwise they are emitted as HTML attributes. See the [Rails documentation](https://guides.rubyonrails.org/form_helpers.html) for more information. In addition, the usual Ariadne utility arguments are accepted in system arguments. For example, passing `mt: 2` will add the `mt-2` class to the input. See the Ariadne system arguments docs for details.
38
+
39
+ SPACE_DELIMITED_ARIA_ATTRIBUTES = [:describedby].freeze
40
+ DEFAULT_SIZE = :medium
41
+ SIZE_MAPPINGS = {
42
+ :small => "FormControl-small",
43
+ DEFAULT_SIZE => "FormControl-medium",
44
+ :large => "FormControl-large",
45
+ }.freeze
46
+ SIZE_OPTIONS = SIZE_MAPPINGS.keys
47
+
48
+ include Ariadne::ClassNameHelper
49
+
50
+ attr_reader :builder, :form, :input_attributes, :label_attributes, :caption, :validation_message, :ids, :form_control, :base_id
51
+
52
+ alias_method :form_control?, :form_control
53
+
54
+ def initialize(builder:, form:, **options)
55
+ @builder = builder
56
+ @form = form
57
+
58
+ @options = options
59
+ @input_attributes = @options.delete(:html_attrs) || {}
60
+ @input_attributes.delete(:id) if @input_attributes[:id].blank?
61
+ @label_attributes = @options[:label_html_attributes] || {}
62
+ @label_attributes[:for] = id if id.present?
63
+
64
+ @label_attributes[:class] = merge_class_names(
65
+ @label_attributes[:class],
66
+ @options.delete(:visually_hide_label) ? "sr-only" : nil,
67
+ )
68
+
69
+ @input_attributes.delete(:class) if @input_attributes[:class].blank?
70
+ @label_attributes.delete(:class) if @label_attributes[:class].blank?
71
+
72
+ @caption = @options.delete(:caption)
73
+ @validation_message = @options.delete(:validation_message)
74
+ @invalid = @options.delete(:invalid)
75
+ @full_width = @options.delete(:full_width) { true }
76
+
77
+ # If scope_name_to_model is false, the name of the input for eg. `my_field`
78
+ # will be `my_field` instead of the Rails default of `model[my_field]`.
79
+ #
80
+ # We achieve this by passing the `name` option to Rails form builder
81
+ # methods. These methods will use the passed name if provided instead
82
+ # of generating a scoped one.
83
+ #
84
+ unless @options.delete(:scope_name_to_model) { true }
85
+ @input_attributes[:name] = name
86
+ end
87
+ # rubocop:enable Style/IfUnlessModifier
88
+
89
+ # If scope_id_to_model is false, the name of the input for eg. `my_field`
90
+ # will be `my_field` instead of the Rails default of `model_my_field`.
91
+ #
92
+ # We achieve this by passing the `id` option to Rails form builder
93
+ # methods. These methods will use the passed id if provided instead
94
+ # of generating a scoped one. The id is the name of the field unless
95
+ # explicitly provided in system_arguments.
96
+ #
97
+ # rubocop:disable Style/IfUnlessModifier
98
+ unless @options.delete(:scope_id_to_model) { true }
99
+ @input_attributes[:id] = @input_attributes.delete(:id) { name }
100
+ end
101
+ # rubocop:enable Style/IfUnlessModifier
102
+
103
+ # Whether or not to wrap the component in a FormControl, which renders a
104
+ # label above and validation message beneath the input.
105
+ @form_control = @options.delete(:form_control) { true }
106
+
107
+ @options[:invalid] = "true" if invalid?
108
+
109
+ @base_id = Ariadne::Generate.id
110
+
111
+ @ids = {}.tap do |id_map|
112
+ id_map[:validation] = "validation-#{@base_id}" if supports_validation?
113
+ id_map[:caption] = "caption-#{@base_id}" if caption? || caption_template?
114
+ end
115
+
116
+ if required?
117
+ input_attributes[:required] = true
118
+ add_input_aria(:required, true)
119
+ end
120
+
121
+ add_input_aria(:invalid, true) if invalid?
122
+ add_input_aria(:describedby, ids.values) if ids.any?
123
+ end
124
+
125
+ def add_input_classes(*class_names)
126
+ input_attributes[:class] = merge_class_names(
127
+ input_attributes[:class], *class_names
128
+ )
129
+ end
130
+
131
+ def add_input_aria(key, value)
132
+ @input_attributes[:aria] ||= {}
133
+
134
+ @input_attributes[:aria][key] = if space_delimited_aria_attribute?(key)
135
+ aria_join(@input_attributes[:aria][key], *Array(value))
136
+ else
137
+ value
138
+ end
139
+ end
140
+
141
+ def add_input_data(key, value)
142
+ input_data[key] = value
143
+ end
144
+
145
+ # :nocov:
146
+ def remove_input_data(key)
147
+ input_data.delete(key)
148
+ end
149
+ # :nocov:
150
+
151
+ def merge_input_attributes!(arguments)
152
+ arguments.each do |k, v|
153
+ case k
154
+ when :class, :classes, "class", "classes"
155
+ add_input_classes(v)
156
+ when :aria, "aria"
157
+ v.each do |aria_k, aria_v|
158
+ add_input_aria(aria_k, aria_v)
159
+ end
160
+ when :data, "data"
161
+ v.each do |data_k, data_v|
162
+ add_input_data(data_k, data_v)
163
+ end
164
+ else
165
+ @input_attributes[k] = v
166
+ end
167
+ end
168
+ end
169
+
170
+ def validation_id
171
+ ids[:validation]
172
+ end
173
+
174
+ def caption_id
175
+ ids[:caption]
176
+ end
177
+
178
+ def caption?
179
+ caption.present?
180
+ end
181
+
182
+ def caption_template?
183
+ return false unless form
184
+
185
+ form.caption_template?(caption_template_name)
186
+ end
187
+
188
+ def render_caption_template
189
+ form.render_caption_template(caption_template_name)
190
+ end
191
+
192
+ def valid?
193
+ supports_validation? && validation_messages.empty? && !@invalid
194
+ end
195
+
196
+ def invalid?
197
+ supports_validation? && !valid?
198
+ end
199
+
200
+ def hidden?
201
+ !!input_attributes[:hidden]
202
+ end
203
+
204
+ def required?
205
+ @options[:required] ||
206
+ input_attributes[:aria_required] ||
207
+ input_attributes[:"aria-required"] ||
208
+ input_attributes.dig(:aria, :required)
209
+ end
210
+
211
+ def disabled?
212
+ !!@input_attributes[:disabled]
213
+ end
214
+
215
+ def size
216
+ @size ||= SIZE_MAPPINGS.include?(@size) ? @size : DEFAULT_SIZE
217
+ end
218
+
219
+ def validation_messages
220
+ @validation_messages ||=
221
+ if validation_message
222
+ [validation_message]
223
+ elsif builder.object.respond_to?(:errors)
224
+ name ? builder.object.errors.full_messages_for(name) : []
225
+ else
226
+ []
227
+ end
228
+ end
229
+
230
+ def autofocus!
231
+ @input_attributes[:autofocus] = true
232
+ end
233
+
234
+ def id
235
+ @input_attributes[:id]
236
+ end
237
+
238
+ # :nocov:
239
+ def name
240
+ raise_for_abstract_method!(__method__)
241
+ end
242
+
243
+ def label
244
+ raise_for_abstract_method!(__method__)
245
+ end
246
+
247
+ def type
248
+ raise_for_abstract_method!(__method__)
249
+ end
250
+
251
+ def to_component
252
+ raise_for_abstract_method!(__method__)
253
+ end
254
+ # :nocov:
255
+
256
+ def focusable?
257
+ false
258
+ end
259
+
260
+ def input?
261
+ true
262
+ end
263
+
264
+ def supports_validation?
265
+ true
266
+ end
267
+
268
+ def validation_arguments
269
+ {
270
+ class: "FormControl-inlineValidation",
271
+ id: validation_id,
272
+ hidden: valid? || validation_messages.empty?,
273
+ }
274
+ end
275
+
276
+ def validation_message_arguments
277
+ {}
278
+ end
279
+
280
+ def validation_success_icon_target
281
+ ""
282
+ end
283
+
284
+ def validation_error_icon_target
285
+ ""
286
+ end
287
+
288
+ private
289
+
290
+ def input_data
291
+ @input_attributes[:data] ||= {}
292
+ end
293
+
294
+ def caption_template_name
295
+ return unless name
296
+
297
+ @caption_template_name ||= if respond_to?(:value)
298
+ :"#{name}_#{value}"
299
+ else
300
+ name.to_sym
301
+ end
302
+ end
303
+
304
+ def space_delimited_aria_attribute?(attrib)
305
+ SPACE_DELIMITED_ARIA_ATTRIBUTES.include?(attrib)
306
+ end
307
+
308
+ def aria_join(*values)
309
+ values = values.flat_map { |v| v.to_s.split }
310
+ values.reject!(&:empty?)
311
+ values.join(" ")
312
+ end
313
+
314
+ # :nocov:
315
+ def raise_for_abstract_method!(method_name)
316
+ raise NotImplementedError, "subclasses must implement ##{method_name}."
317
+ end
318
+ # :nocov:
319
+ end
320
+ end
321
+ end
322
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ module Forms
5
+ module Dsl
6
+ # :nodoc:
7
+ class InputGroup
8
+ include InputMethods
9
+
10
+ attr_reader :builder, :form, :system_arguments
11
+
12
+ def initialize(builder:, form:, **system_arguments)
13
+ @builder = builder
14
+ @form = form
15
+ @system_arguments = system_arguments
16
+
17
+ yield(self) if block_given?
18
+ end
19
+
20
+ def to_component
21
+ Ariadne::Form::Group::Component.new(inputs: inputs, builder: builder, form: form, **@system_arguments)
22
+ end
23
+
24
+ def type
25
+ :group
26
+ end
27
+
28
+ def input?
29
+ true
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ module Forms
5
+ module Dsl
6
+ # :nodoc:
7
+ module InputMethods
8
+ # Used to render another form object.
9
+ #
10
+ # @param args [Array] Positional arguments passed to Rails' [`fields_for` helper](https://api.rubyonrails.org/v7.0.4/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for).
11
+ # @param kwargs [Hash] The options accepted by the form reference input (see forms docs). Includes keyword arguments passed to Rails' [`fields_for` helper](https://api.rubyonrails.org/v7.0.4/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for).
12
+ def fields_for(*args, **kwargs, &block)
13
+ add_input(FormReferenceInput.new(*args, builder: builder, form: form, **kwargs, &block))
14
+ end
15
+
16
+ # Adds a multi input to this form.
17
+ #
18
+ # @param options [Hash] The options accepted by the multi input (see forms docs).
19
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
20
+ def multi(**options, &block)
21
+ add_input(MultiInput.new(builder: builder, form: form, **options, &block))
22
+ end
23
+
24
+ # Adds a hidden input to this form.
25
+ #
26
+ # @param options [Hash] The options accepted by the hidden input (see forms docs).
27
+ def hidden(**options)
28
+ add_input(HiddenInput.new(builder: builder, form: form, **options))
29
+ end
30
+
31
+ # Adds a check box to this form.
32
+ #
33
+ # @param options [Hash] The options accepted by the check box input (see forms docs).
34
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
35
+ def check_box(**options, &block)
36
+ add_input(CheckBoxInput.new(builder: builder, form: form, **options, &block))
37
+ end
38
+
39
+ # Adds a radio button group to this form.
40
+ #
41
+ # @param options [Hash] The options accepted by the radio button group input (see forms docs).
42
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
43
+ def radio_button_group(**options, &block)
44
+ add_input(RadioButtonGroupInput.new(builder: builder, form: form, **options, &block))
45
+ end
46
+
47
+ # Adds a check box group to this form.
48
+ #
49
+ # @param options [Hash] The options accepted by the check box group input (see forms docs).
50
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
51
+ def check_box_group(**options, &block)
52
+ add_input(CheckBoxGroupInput.new(builder: builder, form: form, **options, &block))
53
+ end
54
+
55
+ # Adds a horizontal separator to the form.
56
+ def separator
57
+ add_input(Separator.new)
58
+ end
59
+
60
+ # START text input methods
61
+
62
+ # Adds a text field to this form.
63
+ #
64
+ # @param options [Hash] The options accepted by the text field input (see forms docs).
65
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
66
+ def text_field(**options, &block)
67
+ options = decorate_options(**options)
68
+ add_input(TextFieldInput.new(builder: builder, form: form, **options, &block))
69
+ end
70
+
71
+ # Adds an autocomplete text field to this form.
72
+ #
73
+ # @param options [Hash] The options accepted by the autocomplete input (see forms docs).
74
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
75
+ def auto_complete(**options, &block)
76
+ options = decorate_options(**options)
77
+ add_input(AutoCompleteInput.new(builder: builder, form: form, **options, &block))
78
+ end
79
+
80
+ # Adds a text area to this form.
81
+ #
82
+ # @param options [Hash] The options accepted by the text area input (see forms docs).
83
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
84
+ def text_area(**options, &block)
85
+ options = decorate_options(**options)
86
+ add_input(TextAreaInput.new(builder: builder, form: form, **options, &block))
87
+ end
88
+
89
+ # END text input methods
90
+
91
+ # START select input methods
92
+
93
+ # Adds a select list to this form.
94
+ #
95
+ # @param options [Hash] The options accepted by the select list input (see forms docs).
96
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
97
+ def select_list(**options, &block)
98
+ options = decorate_options(**options)
99
+ add_input(SelectInput.new(builder: builder, form: form, **options, &block))
100
+ end
101
+
102
+ # Adds an <%= link_to_component(Ariadne::Alpha::ActionMenu) %> to this form.
103
+ #
104
+ # @param options [Hash] The options accepted by the <%= link_to_component(Ariadne::Alpha::ActionMenu) %> component.
105
+ # @param block [Proc] The block passed to `#render` when the <%= link_to_component(Ariadne::Alpha::ActionMenu) %> is rendered. This block is passed an instance of <%= link_to_component(Ariadne::Alpha::ActionMenu) %>, which can be used to add items, dividers, etc.
106
+ def action_menu(**options, &block)
107
+ options = decorate_options(**options)
108
+ add_input(ActionMenuInput.new(builder: builder, form: form, **options, &block))
109
+ end
110
+
111
+ # END select input methods
112
+
113
+ # START button input methods
114
+
115
+ # Adds a submit button to this form.
116
+ #
117
+ # @param options [Hash] The options accepted by the submit button input (see forms docs).
118
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
119
+ def submit(**options, &block)
120
+ options = decorate_options(**options)
121
+ add_input(SubmitButtonInput.new(builder: builder, form: form, **options, &block))
122
+ end
123
+
124
+ # Adds a (non-submit) button to this form.
125
+ #
126
+ # @param options [Hash] The options accepted by the button input (see forms docs).
127
+ # @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
128
+ def button(**options, &block)
129
+ options = decorate_options(**options)
130
+ add_input(ButtonInput.new(builder: builder, form: form, **options, &block))
131
+ end
132
+
133
+ # END button input methods
134
+
135
+ # @private
136
+ def inputs
137
+ @inputs ||= []
138
+ end
139
+
140
+ private
141
+
142
+ # @private
143
+ def add_input(input)
144
+ inputs << input
145
+ end
146
+
147
+ # @private
148
+ #
149
+ # Called before the corresponding Input class is instantiated. The return value of this method is passed
150
+ # to the Input class's constructor.
151
+ def decorate_options(**options)
152
+ options
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ module Forms
5
+ module Dsl
6
+ # :nodoc:
7
+ class SubmitButtonInput < Input
8
+ attr_reader :name, :label, :block
9
+
10
+ def initialize(**options, &block)
11
+ @block = block
12
+
13
+ super(**options)
14
+ end
15
+
16
+ def to_component
17
+ html_attrs = @input_attributes || {}
18
+ @block.call(Ariadne::UI::Button::Component.new(type: :submit, **@options, html_attrs: html_attrs))
19
+ end
20
+
21
+ def before_render
22
+ content # ensures that block is called
23
+ end
24
+
25
+ # :nocov:
26
+ def type
27
+ :submit_button
28
+ end
29
+
30
+ def supports_validation?
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ module Forms
5
+ module Dsl
6
+ # :nodoc:
7
+ class TextFieldInput < Input
8
+ attr_reader(
9
+ :name, :label, :show_clear_button, :leading_visual, :clear_button_id, :visually_hide_label, :inset, :monospace, :field_wrap_classes, :auto_check_src
10
+ )
11
+
12
+ def initialize(name:, label:, **options)
13
+ @name = name
14
+ @label = label
15
+
16
+ @show_clear_button = options.delete(:show_clear_button)
17
+ @inset = options.delete(:inset)
18
+ @monospace = options.delete(:monospace)
19
+ @auto_check_src = options.delete(:auto_check_src)
20
+
21
+ super(**options)
22
+
23
+ add_input_data(:target, "ariadne-text-field.inputElement #{@input_attributes.dig(:data, :target) || ""}".rstrip)
24
+ end
25
+
26
+ def to_component
27
+ html_attrs = @input_attributes || {}
28
+ Ariadne::Form::TextField::Component.new(name: @name, label: @label, **@options, html_attrs: html_attrs)
29
+ end
30
+
31
+ def type
32
+ :text_field
33
+ end
34
+
35
+ def focusable?
36
+ true
37
+ end
38
+
39
+ def validation_arguments
40
+ if auto_check_src.present?
41
+ super.merge(
42
+ data: {
43
+ target: "primer-text-field.validationElement",
44
+ },
45
+ )
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def validation_success_icon_target
52
+ "primer-text-field.validationSuccessIcon"
53
+ end
54
+
55
+ def validation_error_icon_target
56
+ "primer-text-field.validationErrorIcon"
57
+ end
58
+
59
+ def validation_message_arguments
60
+ if auto_check_src.present?
61
+ super.merge(
62
+ data: {
63
+ target: "primer-text-field.validationMessageElement",
64
+ },
65
+ )
66
+ else
67
+ super
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ # :nodoc:
5
+ module Forms
6
+ # :nodoc:
7
+ module Utils
8
+ # Unfortunately this bug (https://github.com/ruby/ruby/pull/5646) prevents us from using
9
+ # Ruby's native Module.const_source_location. Instead we have to fudge it by searching
10
+ # for the file in the configured autoload paths. Doing so relies on Rails' autoloading
11
+ # conventions, so it should work ok. Zeitwerk also has this information but lacks a
12
+ # public API to map constants to source files.
13
+ def const_source_location(class_name)
14
+ return unless class_name
15
+
16
+ # NOTE: underscore respects namespacing, i.e. will convert Foo::Bar to foo/bar.
17
+ class_path = "#{class_name.underscore}.rb"
18
+
19
+ ActiveSupport::Dependencies.autoload_paths.each do |autoload_path|
20
+ absolute_path = File.join(autoload_path, class_path)
21
+ return absolute_path if File.exist?(absolute_path)
22
+ end
23
+
24
+ nil
25
+ end
26
+
27
+ def classify(options)
28
+ options
29
+ end
30
+ end
31
+
32
+ Utils.extend(Utils)
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ariadne
4
+ module Generate
5
+ class << self
6
+ def id
7
+ SecureRandom.hex(6)
8
+ end
9
+ end
10
+ end
11
+ end