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,102 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Ariadne
5
+ module UI
6
+ module ClipboardCopy
7
+ # Use `ClipboardCopy` to copy element text content or input values to the clipboard.
8
+ #
9
+ # @example Default
10
+ # <%= render(Ariadne::UI::ClipboardCopy::Component.new(value: "Text to copy", aria_label: "Copy text to the system clipboard" )) %>
11
+ #
12
+ # @example With text instead of icons
13
+ # <%= render(Ariadne::UI::ClipboardCopy::Component.new(value: "Text to copy", aria_label: "Copy text to the system clipboard" )) do %>
14
+ # Click to copy!
15
+ # <% end %>
16
+ #
17
+ # @example Copying from an element
18
+ # <%= render(Ariadne::UI::ClipboardCopy::Component.new(for_id: "blob-path", aria_label: "Copy text to the system clipboard" )) %>
19
+ # <div id="blob-path">src/index.js</div>
20
+ #
21
+ # @accessibility
22
+ # Always set an accessible label to help the user interact with the component.
23
+ class Component < Ariadne::UI::Button::Component
24
+ # @param value [Strinulus_g] Text to copy when the component is clicked.
25
+ option :value, optional: true
26
+ # @param for [String] If `value` is not provided, the element with this id will be copied.
27
+ option :for, optional: true
28
+
29
+ def initialize(**options)
30
+ super
31
+ validate!
32
+ end
33
+
34
+ accepts_html_attributes do |html_attrs|
35
+ prepend_controller(html_attrs)
36
+ prepend_action(html_attrs, "clipboard-copy->ariadne-ui-clipboard-copy#copy")
37
+ html_attrs[:value] = @value if @value.present?
38
+ html_attrs[:for] = @for if @for.present?
39
+ end
40
+
41
+ def before_render
42
+ validate_aria_label!(html_attrs) if content.blank?
43
+ end
44
+
45
+ private def validate!
46
+ raise ArgumentError, "Must provide either `value` or `for`" if @value.blank? && @for.blank?
47
+ raise ArgumentError, "Must provide only `value` or `for`, not both" if @value.present? && @for.present?
48
+ end
49
+
50
+ def classes
51
+ style(:button, size:) + style(:size)
52
+ end
53
+
54
+ style do
55
+ base do
56
+ [
57
+ "ariadne-inline-flex",
58
+ "ariadne-items-center",
59
+ "ariadne-justify-center",
60
+ ]
61
+ end
62
+
63
+ variants do
64
+ size do
65
+ xs do
66
+ [
67
+ "ariadne-gap-0.5",
68
+ "ariadne-text-xs-md",
69
+ "ariadne-rounded",
70
+ "[&>svg]:ariadne-size-3",
71
+ ]
72
+ end
73
+ sm do
74
+ [
75
+ "ariadne-gap-0.5",
76
+ "ariadne-px-1",
77
+ "ariadne-text-sm-md",
78
+ "ariadne-rounded",
79
+ "[&>svg]:ariadne-size-4",
80
+ ]
81
+ end
82
+ base do
83
+ ["ariadne-gap-1", "ariadne-text-base", "ariadne-rounded-md"]
84
+ end
85
+ md do
86
+ [
87
+ "ariadne-gap-1",
88
+ "ariadne-text-base",
89
+ "ariadne-rounded-lg",
90
+ "[&>svg]:ariadne-size-5",
91
+ ]
92
+ end
93
+ lg do
94
+ ["ariadne-gap-1", "ariadne-text-lg", "ariadne-rounded-lg"]
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,54 @@
1
+ import {controllerFactory} from '@utils/createController'
2
+
3
+ import '@github/clipboard-copy-element'
4
+
5
+ const CLIPBOARD_COPY_TIMER_DURATION = 2000
6
+
7
+ export default class ClipboardCopyController extends controllerFactory<HTMLDetailsElement>()({
8
+ targets: {
9
+ initial: HTMLElement,
10
+ confirmed: null,
11
+ },
12
+ }) {
13
+ declare clipboardCopyElementTimers: WeakMap<HTMLElement, number>
14
+
15
+ connect() {
16
+ this.clipboardCopyElementTimers = new WeakMap()
17
+ }
18
+
19
+ copy(event: Event) {
20
+ const target = event.target as HTMLElement
21
+ const currentTimeout = this.clipboardCopyElementTimers.get(target)
22
+
23
+ if (currentTimeout) {
24
+ clearTimeout(currentTimeout)
25
+ this.clipboardCopyElementTimers.delete(target)
26
+ } else {
27
+ this.showConfirm()
28
+ }
29
+
30
+ this.clipboardCopyElementTimers.set(
31
+ target,
32
+ window.setTimeout(() => {
33
+ this.showInitial()
34
+ this.clipboardCopyElementTimers.delete(target)
35
+ }, CLIPBOARD_COPY_TIMER_DURATION),
36
+ )
37
+ }
38
+
39
+ showConfirm() {
40
+ if (this.hasConfirmedTarget) {
41
+ this.confirmedTarget.classList.remove('hidden')
42
+ this.confirmedTarget.classList.add('inline-block')
43
+ this.initialTarget.classList.add('hidden')
44
+ }
45
+ }
46
+
47
+ showInitial() {
48
+ this.initialTarget.classList.remove('hidden')
49
+ this.initialTarget.classList.add('inline-block')
50
+ if (this.hasConfirmedTarget) {
51
+ this.confirmedTarget.classList.add('hidden')
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,32 @@
1
+ <details class="inline" <%= html_attrs.to_html %>>
2
+ <summary class="list-none rounded" data-combobox-target="anchor">
3
+ <%= content %>
4
+ </summary>
5
+ <details-menu
6
+ class='<%= style(:popover, size:) %>'
7
+ role="menu"
8
+ data-combobox-target="popover"
9
+ data-action="details-menu-selected->combobox#close">
10
+ <% if show_search == :yes %>
11
+ <div class="p-1 pb-0">
12
+ <%= render UI::Input::Component.new(
13
+ theme: :soft,
14
+ width: :full,
15
+ html_attrs: input_attrs
16
+ ) %>
17
+ </div>
18
+ <% end %>
19
+ <div class="<%= style(:options) %>"
20
+ data-combobox-target="options">
21
+ <%= options %>
22
+ <div class="text-center" data-input-filter-target="emptyRoot">
23
+ <% if any_options? %>
24
+ <%= "no_options" %>
25
+ <% else %>
26
+ <span class="py-3 text-sm text-zinc-600 dark:text-zinc-400"><%= translate(:no_options) %></span>
27
+ <% end %>
28
+ </div>
29
+ </div>
30
+ </details-menu>
31
+ </details>
32
+ <%= selector %>
@@ -0,0 +1,83 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Ariadne
5
+ module UI
6
+ module Combobox
7
+ class Component < BaseComponent
8
+ option :placement, default: proc { "bottom" }
9
+ option :show_search, default: proc { :yes }
10
+ option :size, default: proc { :md }
11
+
12
+ option :clamped, default: proc { false }
13
+
14
+ option :changed_action, optional: true
15
+
16
+ option :filter_placeholder, default: proc { "Filter…" }
17
+
18
+ renders_many :options, Ariadne::UI::Combobox::MenuItem::Component
19
+ renders_one :selector, ->(size:, icon:) do
20
+ render(Ariadne::UI::Button::Component.new(theme: :ghost, size: size, icon_only: :yes)) do
21
+ render(Ariadne::UI::Heroicon::Component.new(icon: icon, variant: :solid, size: size))
22
+ end
23
+ end
24
+
25
+ def any_options?
26
+ options.any?
27
+ end
28
+
29
+ accepts_html_attributes do |html_attrs|
30
+ html_attrs[:data] = {
31
+ controller: "input-filter #{stimulus_name}",
32
+ "#{stimulus_name}-placement-value": placement,
33
+ "#{stimulus_name}-clamped-value": clamped,
34
+ }
35
+ if changed_action
36
+ html_attrs[:data][:action] = "#{stimulus_name}:changed->#{changed_action}"
37
+ end
38
+ end
39
+
40
+ accepts_html_attributes_for :input,
41
+ autocomplete: "off",
42
+ autofocus: true,
43
+ type: "search",
44
+ placeholder: proc { @filter_placeholder },
45
+ data: {
46
+ "input-filter-target": "input",
47
+ "#{stimulus_name}-target": "searchInput",
48
+ "action": "input-filter#handleInput",
49
+ }
50
+
51
+ style :popover do
52
+ base do
53
+ [
54
+ "fixed",
55
+ "max-w-[90vw]",
56
+ "z-20",
57
+ "scroll",
58
+ "scrollbar-trigger",
59
+ "rounded-lg",
60
+ "shadow-lg",
61
+ "bg-white",
62
+ "dark:bg-zinc-900",
63
+ ]
64
+ end
65
+
66
+ variants do
67
+ size do
68
+ sm { "w-36" }
69
+ md { "w-52" }
70
+ lg { "w-96" }
71
+ end
72
+ end
73
+ end
74
+
75
+ style :options do
76
+ base do
77
+ ["overflow-auto", "p-2"]
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,119 @@
1
+ import {type Placement, autoUpdate, computePosition, flip, offset, shift, size} from '@floating-ui/dom'
2
+ import {controllerFactory} from '@utils/createController'
3
+ import {useClickOutside, useMutation} from 'stimulus-use'
4
+
5
+ export default class ComboboxController extends controllerFactory<HTMLDetailsElement>()({
6
+ targets: {
7
+ anchor: null,
8
+ options: null,
9
+ popover: null,
10
+ searchInput: HTMLInputElement,
11
+ },
12
+ values: {
13
+ clamped: Boolean,
14
+ placement: String,
15
+ },
16
+ }) {
17
+ private changedIds = new Set<string>()
18
+ private clickHandlers: Array<() => void> = []
19
+ labels: Array<{el: HTMLLabelElement; searchString: string}>
20
+ unsubAutoUpdate: (() => void) | undefined
21
+
22
+ private setupClickHandlers() {
23
+ const cb = () => this.toggle()
24
+
25
+ for (const fn of this.clickHandlers) {
26
+ fn()
27
+ }
28
+ this.clickHandlers = []
29
+
30
+ for (const el of this.anchorTarget.querySelectorAll('button, [tabindex]:not([tabindex="-1"])')) {
31
+ el.addEventListener('click', cb)
32
+ this.clickHandlers.push(() => el.removeEventListener('click', cb))
33
+ }
34
+ }
35
+
36
+ checkboxClicked(e: Event) {
37
+ const target = e.target as HTMLInputElement
38
+ const value = target.value
39
+ if (this.changedIds.has(value)) {
40
+ this.changedIds.delete(value)
41
+ } else {
42
+ this.changedIds.add(value)
43
+ }
44
+ this.dispatch('clicked', {detail: value})
45
+ }
46
+
47
+ clickOutside() {
48
+ this.element.open = false
49
+ this.setupAutoUpdate()
50
+ this.close()
51
+ }
52
+
53
+ close() {
54
+ if (this.hasSearchInputTarget) this.searchInputTarget.value = ''
55
+ if (this.changedIds.size > 0) {
56
+ this.dispatch('changed')
57
+ this.changedIds.clear()
58
+ }
59
+ }
60
+
61
+ connect() {
62
+ useClickOutside(this)
63
+ useMutation(this, {childList: true, subtree: true})
64
+ this.setupAutoUpdate()
65
+ this.setupClickHandlers()
66
+ }
67
+
68
+ disconnect() {
69
+ this.unsubAutoUpdate?.()
70
+ }
71
+
72
+ setupAutoUpdate(): void {
73
+ if (!this.element.open) {
74
+ this.unsubAutoUpdate?.()
75
+ return
76
+ }
77
+
78
+ const updatePopoverPosition = (): void => {
79
+ const options = this.optionsTarget
80
+ const searchInput = this.hasSearchInputTarget ? this.searchInputTarget : null
81
+ const shouldClamp = this.clampedValue
82
+
83
+ void computePosition(this.anchorTarget, this.popoverTarget, {
84
+ middleware: [
85
+ offset(6),
86
+ flip(),
87
+ shift({padding: 6}),
88
+ size({
89
+ apply({availableHeight}) {
90
+ const inputHeight = searchInput ? searchInput.getBoundingClientRect().height : 0
91
+
92
+ let maxHeight = availableHeight - inputHeight - 6
93
+ if (shouldClamp && maxHeight > 400) maxHeight = 400
94
+
95
+ Object.assign(options.style, {
96
+ maxHeight: `${maxHeight}px`,
97
+ })
98
+ },
99
+ }),
100
+ ],
101
+ placement: this.placementValue as Placement,
102
+ strategy: 'fixed',
103
+ }).then(({x, y}) => {
104
+ Object.assign(this.popoverTarget.style, {
105
+ left: `${x}px`,
106
+ top: `${y}px`,
107
+ })
108
+ })
109
+ }
110
+
111
+ updatePopoverPosition()
112
+ this.unsubAutoUpdate = autoUpdate(this.anchorTarget, this.popoverTarget, updatePopoverPosition)
113
+ }
114
+
115
+ toggle(): void {
116
+ this.element.open = !this.element.open
117
+ this.setupAutoUpdate()
118
+ }
119
+ }
@@ -0,0 +1,9 @@
1
+ <% slot = capture do %>
2
+ <span class="truncate"><%= label %></span>
3
+ <% end %>
4
+
5
+ <%= content_tag(
6
+ link? ? :a : :button,
7
+ slot,
8
+ html_attrs,
9
+ ) %>
@@ -0,0 +1,53 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Ariadne
5
+ module UI
6
+ module Combobox
7
+ module MenuItem
8
+ class Component < BaseComponent
9
+ option :label
10
+ option :as, default: proc { :link } # :button
11
+
12
+ accepts_html_attributes do |html_attrs|
13
+ html_attrs[:class] = style(as:)
14
+ html_attrs["data-input-filter-target"] = "searchString"
15
+ if as == :link && !html_attrs[:target]
16
+ html_attrs[:target] = "_top"
17
+ end
18
+ end
19
+
20
+ style do
21
+ base do
22
+ [
23
+ "flex",
24
+ "gap-0.5",
25
+ "items-center",
26
+ "ps-2",
27
+ "pe-1",
28
+ "text-ui-base",
29
+ "rounded",
30
+ "!ring-0",
31
+ "hover:bg-zinc-100",
32
+ "hover:dark:bg-zinc-800",
33
+ "focus-within:bg-zinc-100",
34
+ "focus-within:dark:bg-zinc-800",
35
+ ]
36
+ end
37
+ variants do
38
+ as do
39
+ button do
40
+ "appearance-none inline-flex w-full"
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def link?
47
+ as == :link
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,11 @@
1
+ <label
2
+ tabindex="0"
3
+ role="menuitemcheckbox"
4
+ class="<%= style %>">
5
+ <span class="px-1"><%= option_component %></span>
6
+ <span
7
+ class="truncate <%= text_content ? 'px-1' : 'inline-flex items-center' %>"
8
+ data-input-filter-target="searchString">
9
+ <%= content %>
10
+ </span>
11
+ </label>
@@ -0,0 +1,45 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Ariadne
5
+ module UI
6
+ module Combobox
7
+ module Option
8
+ class Component < BaseComponent
9
+ option :as, default: proc { :link } # :button
10
+
11
+ option :type, default: proc { :multiple }
12
+ option :text_content, default: proc { true }
13
+
14
+ accepts_html_attributes disabled: false,
15
+ tabindex: -1,
16
+ data: proc {
17
+ { action: "#{UI::Combobox::Component.stimulus_name}#checkboxClicked" }
18
+ }
19
+
20
+ def option_component
21
+ cmp = type == :multiple ? Checkbox : Radio
22
+ render(cmp::Component.new(html_attrs:))
23
+ end
24
+
25
+ style do
26
+ base do
27
+ [
28
+ "flex",
29
+ "gap-0.5",
30
+ "items-center",
31
+ "text-ui-base",
32
+ "rounded",
33
+ "!ring-0",
34
+ "hover:bg-zinc-100",
35
+ "hover:dark:bg-zinc-800",
36
+ "focus-within:bg-zinc-100",
37
+ "focus-within:dark:bg-zinc-800",
38
+ ]
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ <svg class="<%= html_attrs[:class] %>" <%= svg_attributes %>>
2
+ <%= inner %>
3
+ </svg>
@@ -0,0 +1,141 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "heroicons_helper"
5
+
6
+ module Ariadne
7
+ module UI
8
+ module Heroicon
9
+ # @example Default
10
+ # <%= render(Ariadne::UI::Heroicon::Component.new(icon: :check, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE)) %>
11
+ # <%= render(Ariadne::UI::Heroicon::Component.new(icon: :check, variant: HeroiconsHelper::Icon::VARIANT_SOLID)) %>
12
+ #
13
+ # @example Medium
14
+ # <%= render(Ariadne::UI::Heroicon::Component.new(icon: :"user-group", variant: HeroiconsHelper::Icon::VARIANT_OUTLINE, size: :md)) %>
15
+ #
16
+ # @example Helper
17
+ # <%= ariadne_heroicon(icon: :check, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE) %>
18
+ #
19
+ # @param tag [Symbol, String] The rendered tag name
20
+ # @param classes [String] <%= link_to_classes_docs %>
21
+ # @param icon [Symbol, String] Name of <%= link_to_heroicons %> to use.
22
+ # @param variant [String] <%= one_of(HeroiconsHelper::Icon::VALID_VARIANTS, sort: false) %>
23
+ # @param size [Symbol] <%= one_of(Ariadne::UI::Heroicon::Component::SIZE_MAPPINGS, sort: false) %>
24
+ class Component < Ariadne::BaseComponent
25
+ option :icon
26
+ option :variant
27
+ option :size, default: -> { :md }
28
+
29
+ include IconHelper
30
+ include HeroiconsHelper
31
+
32
+ PRELOADED_ICONS = [
33
+ {
34
+ name: "bell",
35
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
36
+ },
37
+ {
38
+ name: "check",
39
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
40
+ },
41
+ {
42
+ name: "chevron-down",
43
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
44
+ },
45
+ {
46
+ name: "clipboard",
47
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
48
+ },
49
+ {
50
+ name: "clock",
51
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
52
+ },
53
+ {
54
+ name: "information-circle",
55
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
56
+ },
57
+ {
58
+ name: "dots-horizontal",
59
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
60
+ },
61
+ {
62
+ name: "link",
63
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
64
+ },
65
+ {
66
+ name: "lock-closed",
67
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
68
+ },
69
+ {
70
+ name: "mail",
71
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
72
+ },
73
+ {
74
+ name: "menu",
75
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
76
+ },
77
+ {
78
+ name: "pencil",
79
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
80
+ },
81
+ {
82
+ name: "plus-sm",
83
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
84
+ },
85
+ {
86
+ name: "question-mark-circle",
87
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
88
+ },
89
+ {
90
+ name: "search",
91
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
92
+ },
93
+ {
94
+ name: "search",
95
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
96
+ },
97
+ {
98
+ name: "trash",
99
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
100
+ },
101
+ {
102
+ name: "x-mark",
103
+ variant: HeroiconsHelper::Icon::VARIANT_OUTLINE,
104
+ },
105
+ ].freeze
106
+
107
+ accepts_html_attributes do |html_attrs|
108
+ html_attrs[:class] = Ariadne::ViewComponents.tailwind_merger.merge([@heroicon.attributes["class"], style(size: @size), html_attrs[:class]].join(" "))
109
+ end
110
+
111
+ def initialize(**options)
112
+ super
113
+
114
+ check_icon_presence!(icon, variant)
115
+
116
+ @heroicon = heroicon(@icon, variant: @variant)
117
+ end
118
+
119
+ def svg_attributes
120
+ tag.attributes(html_attrs.except(:class).merge(@heroicon.attributes.except("class", "aria-hidden")))
121
+ end
122
+
123
+ def inner
124
+ ActiveSupport::SafeBuffer.new(@heroicon.path)
125
+ end
126
+
127
+ style do
128
+ variants do
129
+ size do
130
+ xs { ["ariadne-w-4", "ariadne-h-4"] }
131
+ sm { ["ariadne-w-5", "ariadne-h-5"] }
132
+ md { ["ariadne-w-6", "ariadne-h-6"] }
133
+ lg { ["ariadne-w-10", "ariadne-h-10"] }
134
+ xl { ["ariadne-w-12", "ariadne-h-12"] }
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end