ariadne_view_components 0.0.4 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/assets/javascripts/ariadne-form-with.d.ts +20 -0
  4. data/app/assets/javascripts/ariadne-form.d.ts +22 -0
  5. data/app/assets/javascripts/ariadne.d.ts +1 -0
  6. data/app/assets/javascripts/ariadne_view_components.js +7 -1
  7. data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
  8. data/app/assets/javascripts/clipboard-copy-component.d.ts +4 -0
  9. data/app/assets/javascripts/comment-component.d.ts +13 -0
  10. data/app/assets/javascripts/rich-text-area-component.d.ts +4 -0
  11. data/app/assets/javascripts/slideover-component.d.ts +9 -0
  12. data/app/assets/javascripts/time-ago-component.d.ts +1 -0
  13. data/app/assets/javascripts/time_ago_component.d.ts +1 -0
  14. data/app/assets/javascripts/tooltip-component.d.ts +24 -0
  15. data/app/assets/stylesheets/application.ariadne_view_components.css +6 -3
  16. data/app/assets/stylesheets/prosemirror.css +323 -0
  17. data/app/assets/stylesheets/tooltip-component.css +37 -0
  18. data/app/components/ariadne/ariadne-form.ts +96 -0
  19. data/app/components/ariadne/ariadne.ts +11 -1
  20. data/app/components/ariadne/base_button.rb +12 -12
  21. data/app/components/ariadne/base_component.rb +13 -131
  22. data/app/components/ariadne/blankslate_component.html.erb +5 -5
  23. data/app/components/ariadne/blankslate_component.rb +5 -10
  24. data/app/components/ariadne/body_component.rb +30 -0
  25. data/app/components/ariadne/button_component.rb +12 -16
  26. data/app/components/ariadne/clipboard_copy_component.html.erb +5 -4
  27. data/app/components/ariadne/clipboard_copy_component.rb +41 -3
  28. data/app/components/ariadne/comment-component.ts +55 -0
  29. data/app/components/ariadne/comment_component.html.erb +22 -0
  30. data/app/components/ariadne/comment_component.rb +57 -0
  31. data/app/components/ariadne/component.rb +4 -3
  32. data/app/components/ariadne/container_component.rb +1 -1
  33. data/app/components/ariadne/counter_component.rb +4 -4
  34. data/app/components/ariadne/flash_component.html.erb +12 -12
  35. data/app/components/ariadne/flash_component.rb +17 -17
  36. data/app/components/ariadne/flex_component.rb +49 -0
  37. data/app/components/ariadne/footer_component.html.erb +1 -1
  38. data/app/components/ariadne/footer_component.rb +1 -1
  39. data/app/components/ariadne/grid_component.html.erb +13 -4
  40. data/app/components/ariadne/grid_component.rb +22 -11
  41. data/app/components/ariadne/header_component.html.erb +7 -7
  42. data/app/components/ariadne/header_component.rb +8 -8
  43. data/app/components/ariadne/heading_component.rb +3 -3
  44. data/app/components/ariadne/heroicon_component.html.erb +4 -6
  45. data/app/components/ariadne/heroicon_component.rb +21 -10
  46. data/app/components/ariadne/inline_flex_component.rb +14 -12
  47. data/app/components/ariadne/link_component.rb +13 -8
  48. data/app/components/ariadne/list_component.html.erb +5 -7
  49. data/app/components/ariadne/list_component.rb +9 -38
  50. data/app/components/ariadne/main_component.rb +32 -0
  51. data/app/components/ariadne/narrow_container_component.html.erb +3 -0
  52. data/app/components/ariadne/narrow_container_component.rb +30 -0
  53. data/app/components/ariadne/panel_bar_component.html.erb +20 -0
  54. data/app/components/ariadne/panel_bar_component.rb +79 -0
  55. data/app/components/ariadne/pill_component.rb +2 -2
  56. data/app/components/ariadne/rich-text-area-component.ts +32 -0
  57. data/app/components/ariadne/rich_text_area_component.html.erb +6 -0
  58. data/app/components/ariadne/rich_text_area_component.rb +35 -0
  59. data/app/components/ariadne/slideover-component.ts +3 -3
  60. data/app/components/ariadne/slideover_component.html.erb +3 -6
  61. data/app/components/ariadne/slideover_component.rb +19 -15
  62. data/app/components/ariadne/tab_bar_component.html.erb +3 -0
  63. data/app/components/ariadne/tab_bar_component.rb +45 -0
  64. data/app/components/ariadne/tab_component.html.erb +7 -0
  65. data/app/components/ariadne/tab_component.rb +43 -0
  66. data/app/components/ariadne/{time_ago_component.ts → time-ago-component.ts} +0 -0
  67. data/app/components/ariadne/time_ago_component.rb +2 -2
  68. data/app/components/ariadne/timeline_component.html.erb +6 -6
  69. data/app/components/ariadne/tooltip-component.ts +57 -0
  70. data/app/components/ariadne/tooltip_component.html.erb +4 -0
  71. data/app/components/ariadne/tooltip_component.rb +34 -31
  72. data/app/lib/ariadne/action_view_extensions/form_helper.rb +4 -1
  73. data/app/lib/ariadne/fetch_or_fallback_helper.rb +3 -1
  74. data/app/lib/ariadne/form_builder.rb +22 -22
  75. data/app/lib/ariadne/icon_helper.rb +1 -1
  76. data/lib/ariadne/view_components/engine.rb +171 -3
  77. data/lib/ariadne/view_components/version.rb +1 -1
  78. data/lib/ariadne/view_components.rb +31 -30
  79. data/lib/rubocop/config/default.yml +0 -6
  80. data/lib/rubocop/cop/ariadne/no_tag_memoize.rb +1 -0
  81. data/lib/tasks/docs.rake +9 -0
  82. data/static/arguments.yml +186 -15
  83. data/static/audited_at.json +12 -2
  84. data/static/classes.yml +200 -154
  85. data/static/constants.json +168 -146
  86. data/static/statuses.json +12 -2
  87. metadata +65 -13
  88. data/lib/ariadne/classify/utilities.rb +0 -199
  89. data/lib/ariadne/classify/utilities.yml +0 -1817
  90. data/lib/ariadne/classify/validation.rb +0 -18
  91. data/lib/ariadne/classify.rb +0 -218
  92. data/lib/rubocop/cop/ariadne/ariadne_heroicon.rb +0 -252
  93. data/lib/rubocop/cop/ariadne/component_name_migration.rb +0 -35
  94. data/lib/rubocop/cop/ariadne/system_argument_instead_of_class.rb +0 -57
  95. data/lib/tasks/utilities.rake +0 -121
@@ -0,0 +1,4 @@
1
+ <%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do %>
2
+ <span><%= @text %></span>
3
+ <span class="tooltip-arrow ariadne-absolute ariadne-block" data-popper-arrow></span>
4
+ <% end %>
@@ -24,82 +24,85 @@ module Ariadne
24
24
  # - When there is no visible text on the trigger element and the tooltip content is appropriate as a label for the element, set `type: :label`.
25
25
  # This type is usually only appropriate for an icon-only control.
26
26
  class TooltipComponent < Ariadne::Component
27
- DIRECTION_DEFAULT = :s
28
- DIRECTION_OPTIONS = [DIRECTION_DEFAULT, :n, :e, :w, :ne, :nw, :se, :sw].freeze
27
+ DEFAULT_TAG = :tooltip
28
+ DEFAULT_PLACEMENT = :top
29
+ VALID_PLACEMENTS = [DEFAULT_PLACEMENT, :right, :bottom, :left].freeze
30
+
31
+ DEFAULT_CLASSES = "ariadne-invisible ariadne-absolute ariadne-bg-slate-900 ariadne-text-white ariadne-font-semibold ariadne-max-w-xs ariadne-py-1 ariadne-px-2 ariadne-rounded z-max"
32
+
33
+ DATA_CONTROLLER = "tooltip-component"
34
+ DATA_ACTION = "mouseover->tooltip-component#show mouseout->tooltip-component#hide"
29
35
 
30
36
  TYPE_DEFAULT = :description
31
37
  TYPE_OPTIONS = [:label, TYPE_DEFAULT].freeze
38
+
39
+ # DEFAULT_DATA_ATTRIBUTES = {
40
+ # "data-controller": DATA_CONTROLLER,
41
+ # "data-action": "mouseover->tooltip-component#show mouseout->tooltip-component#hide",
42
+ # "data-tooltip-component-placement": DEFAULT_PLACEMENT,
43
+ # }
44
+
32
45
  # @example As a description for an icon-only button
33
46
  # @description
34
47
  # If the tooltip content provides supplementary description, set `type: :description` to establish an `aria-describedby` relationship.
35
48
  # The trigger element should also have a _concise_ accessible label via `aria-label`.
36
49
  # @code
37
50
  # <%= render(Ariadne::HeroiconComponent.new(icon: :moon, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE, attributes: { id: "bold-button-0" })) %>
38
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "bold-button-0" }, type: :description, text: "Add bold text", direction: :ne)) %>
51
+ # <%= render(Ariadne::TooltipComponent.new(for_id: "bold-button-0", type: :description, text: "Add bold text", direction: :top)) %>
39
52
  # @example As a label for an icon-only button
40
53
  # @description
41
54
  # If the tooltip labels the icon-only button, set `type: :label`. This tooltip content becomes the accessible name for the button.
42
55
  # @code
43
- # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "like-button"})) { "👍" } %>
44
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "like-button" }, type: :label, text: "Like", direction: :n)) %>
56
+ # <%= render(Ariadne::ButtonComponent.new(attributes: { id: "like-button" })) { "👍" } %>
57
+ # <%= render(Ariadne::TooltipComponent.new(for_id: "like-button", type: :label, text: "Like", direction: :top)) %>
45
58
  #
46
59
  # @example As a description for a button with visible label
47
60
  # @description
48
61
  # If the button already has visible label text, the tooltip content is likely supplementary so set `type: :description`.
49
62
  # @code
50
63
  # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "save-button"}, scheme: :success)) { "Save" } %>
51
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "save-button"}, type: :description, text: "This will immediately impact all organization members", direction: :ne)) %>
64
+ # <%= render(Ariadne::TooltipComponent.new(for_id: "save-button", type: :description, text: "This will immediately impact all organization members", direction: :right)) %>
52
65
  # @example With direction
53
66
  # @description
54
67
  # Set direction of tooltip with `direction`. The tooltip is responsive and will automatically adjust direction to avoid cutting off.
55
68
  # @code
56
69
  # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "North", m: 2})) { "North" } %>
57
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "North"}, type: :description, text: "This is a North-facing tooltip, and is responsive.", direction: :n)) %>
70
+ # <%= render(Ariadne::TooltipComponent.new(for_id: "North", type: :description, text: "This is a North-facing tooltip, and is responsive.", direction: :top)) %>
58
71
  # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "South", m: 2})) { "South" } %>
59
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "South"}, type: :description, text: "This is a South-facing tooltip and is responsive.", direction: :s)) %>
72
+ # <%= render(Ariadne::TooltipComponent.new(for_id: "South", type: :description, text: "This is a South-facing tooltip and is responsive.", direction: :bottom)) %>
60
73
  # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "East", m: 2})) { "East" } %>
61
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "East"}, type: :description, text: "This is a East-facing tooltip and is responsive.", direction: :e)) %>
74
+ # <%= render(Ariadne::TooltipComponent.new(for_id: "East", type: :description, text: "This is a East-facing tooltip and is responsive.", direction: :right)) %>
62
75
  # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "West", m: 2})) { "West" } %>
63
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "West"}, type: :description, text: "This is a West-facing tooltip and is responsive.", direction: :w)) %>
64
- # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "Northeast", m: 2})) { "Northeast" } %>
65
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "Northeast"}, type: :description, text: "This is a Northeast-facing tooltip and is responsive.", direction: :ne)) %>
66
- # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "Southeast", m: 2})) { "Southeast" } %>
67
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "Southeast"}, type: :description, text: "This is a Southeast-facing tooltip and is responsive.", direction: :se)) %>
68
- # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "Northwest", m: 2})) { "Northwest" } %>
69
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "Northwest"}, type: :description, text: "This is a Northwest-facing tooltip and is responsive.", direction: :nw)) %>
70
- # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "Southwest", m: 2})) { "Southwest" } %>
71
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "Southwest"}, type: :description, text: "This is a Southwest-facing tooltip and is responsive.", direction: :sw)) %>
76
+ # <%= render(Ariadne::TooltipComponent.new(for_id: "West""", type: :description, text: "This is a West-facing tooltip and is responsive.", direction: :left)) %>
72
77
  # @example With relative parent
73
78
  # @description
74
79
  # When the tooltip and trigger element have a parent container with `relative: position`, it should not affect width of the tooltip.
75
80
  # @code
76
81
  # <span style="position: relative;">
77
82
  # <%= render(Ariadne::ButtonComponent.new(attributes: {id: "test-button"}, scheme: :info)) { "Test" } %>
78
- # <%= render(Ariadne::TooltipComponent.new(attributes: { for: "test-button" }, type: :description, text: "This tooltip should take up the full width", direction: :ne)) %>
83
+ # <%= render(Ariadne::TooltipComponent.new(for_id: "test-button", type: :description, text: "This tooltip should take up the full width", direction: :bottom)) %>
79
84
  # </span>
80
85
  # @param tag [Symbol, String] The rendered tag name
81
- # @param type [Symbol] <%= one_of(Ariadne::TooltipComponent::TYPE_OPTIONS) %>
86
+ # @param for_id [String] The ID of the element that the tooltip should be attached to.
82
87
  # @param text [String] The text content of the tooltip. This should be brief and no longer than a sentence.
83
- # @param direction [Symbol] <%= one_of(Ariadne::TooltipComponent::DIRECTION_OPTIONS) %>
88
+ # @param type [Symbol] <%= one_of(Ariadne::TooltipComponent::TYPE_OPTIONS) %>
89
+ # @param direction [Symbol] <%= one_of(Ariadne::TooltipComponent::VALID_PLACEMENTS) %>
84
90
  # @param classes [String] <%= link_to_classes_docs %>
85
91
  # @param attributes [Hash] <%= link_to_attributes_docs %>
86
- def initialize(tag: :"tool-tip", type: TYPE_DEFAULT, text:, direction: DIRECTION_DEFAULT, classes: "", attributes: {})
92
+ def initialize(tag: DEFAULT_TAG, for_id:, text:, type: TYPE_DEFAULT, direction: DEFAULT_PLACEMENT, classes: "", attributes: {})
87
93
  raise TypeError, "tooltip text must be a string" unless text.is_a?(String)
88
94
 
89
- @tag = check_incoming_tag(:"tool-tip", tag)
95
+ @tag = check_incoming_tag(DEFAULT_TAG, tag)
90
96
 
91
97
  @text = text
92
- @classes = classes
98
+ @classes = class_names(DEFAULT_CLASSES, classes)
93
99
 
94
100
  @attributes = attributes
95
- @attributes[:hidden] = true
96
- @attributes[:visible] ||= false
97
- @attributes[:"data-direction"] = fetch_or_raise(DIRECTION_OPTIONS, direction)
98
- @attributes[:"data-type"] = fetch_or_raise(TYPE_OPTIONS, type)
99
- end
101
+ @attributes[:for] = for_id
100
102
 
101
- def call
102
- render(Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes)) { @text }
103
+ @attributes[:"data-tooltip-component-placement"] = fetch_or_raise(VALID_PLACEMENTS, direction)
104
+ @attributes[:"data-type"] = fetch_or_raise(TYPE_OPTIONS, type)
105
+ @attributes[:"data-tooltip-component-target"] = "tooltip"
103
106
  end
104
107
  end
105
108
  end
@@ -12,7 +12,10 @@ module Ariadne
12
12
  options[:builder] ||= Ariadne::FormBuilder
13
13
  options[:html] ||= {}
14
14
  options = options.merge(attributes)
15
- form_with(model: model, scope: scope, url: url, format: format, **options, &block)
15
+ data = {
16
+ controller: "ariadne-form",
17
+ }
18
+ form_with(model: model, scope: scope, url: url, format: format, data: data, **options, &block)
16
19
  end
17
20
  end
18
21
  end
@@ -25,7 +25,9 @@ module Ariadne
25
25
  TRUE_OR_FALSE = [true, false].freeze
26
26
 
27
27
  def fetch_or_raise(allowed_values, given_value)
28
- raise ArgumentError, "allowed_values must be an array; it was #{allowed_values.class}" unless allowed_values.is_a?(Array)
28
+ if !allowed_values.is_a?(Array) && !allowed_values.is_a?(Set)
29
+ raise ArgumentError, "allowed_values must be an array or a set; it was #{allowed_values.class}"
30
+ end
29
31
 
30
32
  if allowed_values.include?(given_value)
31
33
  given_value
@@ -5,66 +5,66 @@ module Ariadne
5
5
  class FormBuilder < ActionView::Helpers::FormBuilder
6
6
  include ClassNameHelper
7
7
 
8
- DEFAULT_SECTION_CLASSES = "divide-y divide-gray-200 pt-8 space-y-6 sm:pt-10 sm:space-y-5"
8
+ DEFAULT_SECTION_CLASSES = "ariadne-pt-8 ariadne-space-y-6 sm:ariadne-pt-10 sm:ariadne-space-y-5"
9
9
  def section(classes: "", attributes: {}, &block)
10
10
  actual_classes = class_names(DEFAULT_SECTION_CLASSES, classes)
11
11
  options = { class: actual_classes, **attributes }
12
12
  @template.content_tag(:div, **options, &block)
13
13
  end
14
14
 
15
- DEFAULT_SECTION_HEADING_CLASSES = "text-lg leading-6 font-medium text-gray-900"
16
- def heading(classes: "", attributes: {}, &block)
15
+ DEFAULT_SECTION_HEADING_CLASSES = "ariadne-text-lg ariadne-leading-6 ariadne-font-medium ariadne-text-gray-900"
16
+ def heading(tag: :h3, classes: "", attributes: {}, &block)
17
17
  actual_classes = class_names(DEFAULT_SECTION_HEADING_CLASSES, classes)
18
18
  options = { class: actual_classes, **attributes }
19
- @template.content_tag(:h3, **options, &block)
19
+ @template.content_tag(tag, **options, &block)
20
20
  end
21
21
 
22
- DEFAULT_SECTION_SUBHEADING_CLASSES = "mt-1 max-w-2xl text-sm text-gray-500"
22
+ DEFAULT_SECTION_SUBHEADING_CLASSES = "ariadne-mt-1 ariadne-max-w-2xl ariadne-text-sm ariadne-text-gray-500"
23
23
  def subheading(classes: "", attributes: {}, &block)
24
24
  actual_classes = class_names(DEFAULT_SECTION_SUBHEADING_CLASSES, classes)
25
25
  options = { class: actual_classes, **attributes }
26
26
  @template.content_tag(:p, **options, &block)
27
27
  end
28
28
 
29
- DEFAULT_LABEL_CLASSES = "block text-sm font-medium text-gray-700"
30
- def label(method, text = nil, options = {}, &block)
31
- options[:class] = class_names(DEFAULT_LABEL_CLASSES, options[:class])
32
- super(method, **options)
29
+ DEFAULT_LABEL_CLASSES = "ariadne-block ariadne-text-sm ariadne-font-medium ariadne-text-gray-700 ariadne-pl-2"
30
+ def label(object_name, content, ptions = {}, &block)
31
+ options[:class] = class_names(DEFAULT_LABEL_CLASSES, options.delete(:classes))
32
+ super(object_name, content, options, &block)
33
33
  end
34
34
 
35
- DEFAULT_TEXT_CLASSES = "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md"
35
+ DEFAULT_TEXT_CLASSES = "ariadne-shadow-sm focus:ariadne-ring-indigo-500 focus:ariadne-border-indigo-500 ariadne-block ariadne-w-full sm:ariadne-text-sm ariadne-border-gray-300 ariadne-rounded-md"
36
36
  def text_field(method, options = {})
37
- options[:class] = class_names(DEFAULT_TEXT_CLASSES, options[:class])
37
+ options[:class] = class_names(DEFAULT_TEXT_CLASSES, options.delete(:classes))
38
38
  super(method, **options)
39
39
  end
40
40
 
41
- DEFAULT_CHECKBOX_CLASSES = "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
41
+ DEFAULT_CHECKBOX_CLASSES = "focus:ariadne-ring-indigo-500 ariadne-h-4 ariadne-w-4 ariadne-text-indigo-600 ariadne-border-gray-300 ariadne-rounded"
42
42
  def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
43
- options[:class] = class_names(DEFAULT_TEXT_CLASSES, options[:class])
44
- super(method, **options)
43
+ options[:class] = class_names(DEFAULT_CHECKBOX_CLASSES, options.delete(:classes))
44
+ super(method, options, checked_value, unchecked_value)
45
45
  end
46
46
 
47
- DEFAULT_RADIO_CLASSES = "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
47
+ DEFAULT_RADIO_CLASSES = "focus:ariadne-ring-indigo-500 ariadne-h-4 ariadne-w-4 ariadne-text-indigo-600 ariadne-border-gray-300 ariadne-rounded"
48
48
  def radio_button(method, tag_value, options = {})
49
- options[:class] = class_names(DEFAULT_RADIO_CLASSES, options[:class])
49
+ options[:class] = class_names(DEFAULT_RADIO_CLASSES, options.delete(:classes))
50
50
  super(method, tag_value, **options)
51
51
  end
52
52
 
53
- DEFAULT_TEXTAREA_CLASSES = "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border border-gray-300 rounded-md"
53
+ DEFAULT_TEXTAREA_CLASSES = "ariadne-shadow-sm focus:ariadne-ring-indigo-500 focus:ariadne-border-indigo-500 ariadne-block ariadne-w-full sm:ariadne-text-sm ariadne-border ariadne-border-gray-300 ariadne-rounded-md"
54
54
  def text_area(method, options = {})
55
- options[:class] = class_names(DEFAULT_TEXTAREA_CLASSES, options[:class])
55
+ options[:class] = class_names(DEFAULT_TEXTAREA_CLASSES, options.delete(:classes))
56
56
  super(method, **options)
57
57
  end
58
58
 
59
- DEFAULT_EMAIL_CLASSES = "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md"
59
+ DEFAULT_EMAIL_CLASSES = "ariadne-shadow-sm focus:ariadne-ring-indigo-500 focus:ariadne-border-indigo-500 ariadne-block ariadne-w-full sm:ariadne-text-sm ariadne-border-gray-300 ariadne-rounded-md"
60
60
  def email_field(method, options = {})
61
- options[:class] = class_names(DEFAULT_EMAIL_CLASSES, options[:class])
61
+ options[:class] = class_names(DEFAULT_EMAIL_CLASSES, options.delete(:classes))
62
62
  super(method, **options)
63
63
  end
64
64
 
65
- DEFAULT_PASSWORD_CLASSES = "appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
65
+ DEFAULT_PASSWORD_CLASSES = "ariadne-appearance-none ariadne-block ariadne-w-full ariadne-px-3 ariadne-py-2 ariadne-border ariadne-border-gray-300 ariadne-rounded-md ariadne-shadow-sm ariadne-placeholder-gray-400 focus:ariadne-outline-none focus:ariadne-ring-indigo-500 focus:ariadne-border-indigo-500 sm:ariadne-text-sm"
66
66
  def password_field(method, options = {})
67
- options[:class] = class_names(DEFAULT_PASSWORD_CLASSES, options[:class])
67
+ options[:class] = class_names(DEFAULT_PASSWORD_CLASSES, options.delete(:classes))
68
68
  super(method, **options)
69
69
  end
70
70
  end
@@ -23,7 +23,7 @@ module Ariadne
23
23
 
24
24
  icon_presence!(icon, variant)
25
25
  variant_presence!(icon, variant)
26
- fetch_or_raise(HeroiconsHelper::Icon::VARIANTS, variant)
26
+ fetch_or_raise(HeroiconsHelper::Icon::VALID_VARIANTS, variant)
27
27
 
28
28
  true
29
29
  end
@@ -1,10 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/engine"
4
- require "ariadne/classify/utilities"
4
+
5
+ require "view_component"
6
+
7
+ require "tailwind_merge"
8
+ require "simple_form"
5
9
 
6
10
  module Ariadne
11
+ # The Ariadne gem is a collection of ViewComponents that can be used to build
12
+ # accessible, responsive, and consistent UIs for Yetto.
7
13
  module ViewComponents
14
+ mattr_accessor :tailwind_merger
15
+
8
16
  # :nodoc:
9
17
  class Engine < ::Rails::Engine
10
18
  isolate_namespace Ariadne::ViewComponents
@@ -28,8 +36,168 @@ module Ariadne
28
36
  end
29
37
  end
30
38
 
31
- config.after_initialize do |app|
32
- ::Ariadne::Classify::Utilities.validate_class_names = app.config.ariadne_view_components.delete(:validate_class_names)
39
+ initializer "ariadne_view_components.simple_form" do |_app|
40
+ # Use this setup block to configure all options available in SimpleForm.
41
+ SimpleForm.setup do |config|
42
+ # Default class for buttons
43
+ config.button_class = "my-2 bg-blue-500 hover:bg-blue-700 text-white font-bold text-sm py-2 px-4 rounded"
44
+
45
+ # Define the default class of the input wrapper of the boolean input.
46
+ config.boolean_label_class = ""
47
+
48
+ # How the label text should be generated altogether with the required text.
49
+ config.label_text = lambda { |label, required, _explicit_label| "#{label} #{required}" }
50
+
51
+ # Define the way to render check boxes / radio buttons with labels.
52
+ config.boolean_style = :inline
53
+
54
+ # You can wrap each item in a collection of radio/check boxes with a tag
55
+ config.item_wrapper_tag = :div
56
+
57
+ # Defines if the default input wrapper class should be included in radio
58
+ # collection wrappers.
59
+ config.include_default_input_wrapper_class = false
60
+
61
+ # CSS class to add for error notification helper.
62
+ config.error_notification_class = "text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-400"
63
+
64
+ # Method used to tidy up errors. Specify any Rails Array method.
65
+ # :first lists the first message for each field.
66
+ # :to_sentence to list all errors for each field.
67
+ config.error_method = :to_sentence
68
+
69
+ # add validation classes to `input_field`
70
+ config.input_field_error_class = "border-red-500"
71
+ config.input_field_valid_class = "border-green-400"
72
+ config.label_class = "text-sm font-medium text-gray-600"
73
+
74
+ # vertical forms
75
+ #
76
+ # vertical default_wrapper
77
+ config.wrappers(:vertical_form, tag: "div", class: "mb-4") do |b|
78
+ b.use(:html5)
79
+ b.use(:placeholder)
80
+ b.optional(:maxlength)
81
+ b.optional(:minlength)
82
+ b.optional(:pattern)
83
+ b.optional(:min_max)
84
+ b.optional(:readonly)
85
+ b.use(:label, class: "block", error_class: "text-red-500")
86
+ b.use(:input, class: "shadow appearance-none border border-gray-300 rounded w-full py-2 px-3 bg-white focus:outline-none focus:ring-0 focus:border-blue-500 text-gray-400 leading-6 transition-colors duration-200 ease-in-out", error_class: "border-red-500", valid_class: "border-green-400")
87
+ b.use(:full_error, wrap_with: { tag: "p", class: "mt-2 text-red-500 text-xs italic" })
88
+ b.use(:hint, wrap_with: { tag: "p", class: "mt-2 text-grey-700 text-xs italic" })
89
+ end
90
+
91
+ # vertical input for boolean (aka checkboxes)
92
+ config.wrappers(:vertical_boolean, tag: "div", class: "mb-4 flex items-start", error_class: "") do |b|
93
+ b.use(:html5)
94
+ b.optional(:readonly)
95
+ b.wrapper(tag: "div", class: "flex items-center h-5") do |ba|
96
+ ba.use(:input, class: "focus:ring-2 focus:ring-indigo-500:focus ring-offset-2 h-4 w-4 text-indigo-600 border-gray-300 rounded")
97
+ end
98
+ b.wrapper(tag: "div", class: "ml-3 text-sm") do |bb|
99
+ bb.use(:label, class: "block", error_class: "text-red-500")
100
+ bb.use(:hint, wrap_with: { tag: "p", class: "block text-grey-700 text-xs italic" })
101
+ bb.use(:full_error, wrap_with: { tag: "p", class: "block text-red-500 text-xs italic" })
102
+ end
103
+ end
104
+
105
+ # vertical input for radio buttons and check boxes
106
+ config.wrappers(:vertical_collection, item_wrapper_class: "flex items-center", item_label_class: "my-1 ml-3 block text-sm font-medium text-gray-400", tag: "div", class: "my-4") do |b|
107
+ b.use(:html5)
108
+ b.optional(:readonly)
109
+ b.wrapper(:legend_tag, tag: "legend", class: "text-sm font-medium text-gray-600", error_class: "text-red-500") do |ba|
110
+ ba.use(:label_text)
111
+ end
112
+ b.use(:input, class: "focus:ring-2 focus:ring-indigo-500 ring-offset-2 h-4 w-4 text-indigo-600 border-gray-300 rounded", error_class: "text-red-500", valid_class: "text-green-400")
113
+ b.use(:full_error, wrap_with: { tag: "p", class: "block mt-2 text-red-500 text-xs italic" })
114
+ b.use(:hint, wrap_with: { tag: "p", class: "mt-2 text-grey-700 text-xs italic" })
115
+ end
116
+
117
+ # vertical file input
118
+ config.wrappers(:vertical_file, tag: "div", class: "") do |b|
119
+ b.use(:html5)
120
+ b.use(:placeholder)
121
+ b.optional(:maxlength)
122
+ b.optional(:minlength)
123
+ b.optional(:readonly)
124
+ b.use(:label, class: "text-sm font-medium text-gray-600 block", error_class: "text-red-500")
125
+ b.use(:input, class: "w-full text-gray-500 px-3 py-2 border rounded", error_class: "text-red-500 border-red-500", valid_class: "text-green-400")
126
+ b.use(:full_error, wrap_with: { tag: "p", class: "mt-2 text-red-500 text-xs italic" })
127
+ b.use(:hint, wrap_with: { tag: "p", class: "mt-2 text-grey-700 text-xs italic" })
128
+ end
129
+
130
+ # vertical multi select
131
+ config.wrappers(:vertical_multi_select, tag: "div", class: "my-4", error_class: "f", valid_class: "") do |b|
132
+ b.use(:html5)
133
+ b.optional(:readonly)
134
+ b.wrapper(:legend_tag, tag: "legend", class: "text-sm font-medium text-gray-600", error_class: "text-red-500") do |ba|
135
+ ba.use(:label_text)
136
+ end
137
+ b.wrapper(tag: "div", class: "inline-flex space-x-1") do |ba|
138
+ # ba.use :input, class: 'flex w-auto w-auto text-gray-500 text-sm border-gray-300 rounded p-2', error_class: 'text-red-500', valid_class: 'text-green-400'
139
+ ba.use(:input, class: "flex w-auto w-auto shadow appearance-none border border-gray-300 rounded w-full p-2 bg-white focus:outline-none focus:border-blue-500 text-gray-400 leading-4 transition-colors duration-200 ease-in-out")
140
+ end
141
+ b.use(:full_error, wrap_with: { tag: "p", class: "mt-2 text-red-500 text-xs italic" })
142
+ b.use(:hint, wrap_with: { tag: "p", class: "mt-2 text-grey-700 text-xs italic" })
143
+ end
144
+
145
+ # vertical range input
146
+ config.wrappers(:vertical_range, tag: "div", class: "my-4", error_class: "text-red-500", valid_class: "text-green-400") do |b|
147
+ b.use(:html5)
148
+ b.use(:placeholder)
149
+ b.optional(:readonly)
150
+ b.optional(:step)
151
+ b.use(:label, class: "text-sm font-medium text-gray-600 block", error_class: "text-red-500")
152
+ b.wrapper(tag: "div", class: "flex items-center h-5") do |ba|
153
+ ba.use(:input, class: "rounded-lg overflow-hidden appearance-none bg-gray-400 h-3 w-full text-gray-300", error_class: "text-red-500", valid_class: "text-green-400")
154
+ end
155
+ b.use(:full_error, wrap_with: { tag: "p", class: "mt-2 text-red-500 text-xs italic" })
156
+ b.use(:hint, wrap_with: { tag: "p", class: "mt-2 text-grey-700 text-xs italic" })
157
+ end
158
+
159
+ # The default wrapper to be used by the FormBuilder.
160
+ config.default_wrapper = :vertical_form
161
+
162
+ # Custom wrappers for input types. This should be a hash containing an input
163
+ # type as key and the wrapper that will be used for all inputs with specified type.
164
+ config.wrapper_mappings = {
165
+ boolean: :vertical_boolean,
166
+ check_boxes: :vertical_collection,
167
+ date: :vertical_multi_select,
168
+ datetime: :vertical_multi_select,
169
+ file: :vertical_file,
170
+ radio_buttons: :vertical_collection,
171
+ range: :vertical_range,
172
+ time: :vertical_multi_select,
173
+ }
174
+ end
175
+ end
176
+
177
+ config.after_initialize do |_app|
178
+ # TODO: is any of this necessary???
179
+ ActionView::Base.field_error_proc = proc do |html_tag, instance_tag|
180
+ fragment = Nokogiri::HTML.fragment(html_tag)
181
+ field = fragment.at("input,select,textarea")
182
+
183
+ model = instance_tag.object
184
+ error_message = model.errors.full_messages.join(", ")
185
+
186
+ html = if field
187
+ field["class"] = "#{field["class"]} invalid"
188
+ html = <<-HTML
189
+ #{fragment}
190
+ <p class="error">#{error_message}</p>
191
+ HTML
192
+ html
193
+ else
194
+ html_tag
195
+ end
196
+
197
+ html.html_safe # rubocop:disable Rails/OutputSafety
198
+ end
199
+
200
+ Ariadne::ViewComponents.tailwind_merger = TailwindMerge::Merger.new
33
201
  end
34
202
  end
35
203
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ariadne
4
4
  module ViewComponents
5
- VERSION = "0.0.4"
5
+ VERSION = "0.0.7"
6
6
  end
7
7
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "ariadne/classify"
4
3
  require "ariadne/view_components/version"
5
4
  require "ariadne/view_components/engine"
6
5
  require "ariadne/view_components/constants"
@@ -15,45 +14,47 @@ module Ariadne
15
14
  audited_at: "audited_at.json",
16
15
  }.freeze
17
16
 
18
- # generate_statuses returns a hash mapping component name to
19
- # the component's status sorted alphabetically by the component name.
20
- def self.generate_statuses
21
- Ariadne::Component.descendants.sort_by(&:name).each_with_object({}) do |component, mem|
22
- mem[component.to_s] = component.status.to_s
17
+ class << self
18
+ # generate_statuses returns a hash mapping component name to
19
+ # the component's status sorted alphabetically by the component name.
20
+ def generate_statuses
21
+ Ariadne::Component.descendants.sort_by(&:name).each_with_object({}) do |component, mem|
22
+ mem[component.to_s] = component.status.to_s
23
+ end
23
24
  end
24
- end
25
25
 
26
- # generate_audited_at returns a hash mapping component name to
27
- # the day the component has passed an accessibility audit.
28
- def self.generate_audited_at
29
- Ariadne::Component.descendants.sort_by(&:name).each_with_object({}) do |component, mem|
30
- mem[component.to_s] = component.audited_at.to_s
26
+ # generate_audited_at returns a hash mapping component name to
27
+ # the day the component has passed an accessibility audit.
28
+ def generate_audited_at
29
+ Ariadne::Component.descendants.sort_by(&:name).each_with_object({}) do |component, mem|
30
+ mem[component.to_s] = component.audited_at.to_s
31
+ end
31
32
  end
32
- end
33
33
 
34
- # generate_constants returns a hash mapping component name to
35
- # all of its constants.
36
- def self.generate_constants
37
- Ariadne::Component.descendants.sort_by(&:name).each_with_object({}) do |component, mem|
38
- mem[component.to_s] = component.constants(false).sort.each_with_object({}) do |constant, h|
39
- h[constant] = component.const_get(constant)
34
+ # generate_constants returns a hash mapping component name to
35
+ # all of its constants.
36
+ def generate_constants
37
+ Ariadne::Component.descendants.sort_by(&:name).each_with_object({}) do |component, mem|
38
+ mem[component.to_s] = component.constants(false).sort.each_with_object({}) do |constant, h|
39
+ h[constant] = component.const_get(constant)
40
+ end
40
41
  end
41
42
  end
42
- end
43
43
 
44
- # dump generates the requested stat hash and outputs it to a file.
45
- def self.dump(stats)
46
- require "json"
44
+ # dump generates the requested stat hash and outputs it to a file.
45
+ def dump(stats)
46
+ require "json"
47
47
 
48
- File.open(File.join(DEFAULT_STATIC_PATH, FILE_NAMES[stats]), "w") do |f|
49
- f.write(JSON.pretty_generate(send("generate_#{stats}")))
50
- f.write($INPUT_RECORD_SEPARATOR)
48
+ File.open(File.join(DEFAULT_STATIC_PATH, FILE_NAMES[stats]), "w") do |f|
49
+ f.write(JSON.pretty_generate(send("generate_#{stats}")))
50
+ f.write($INPUT_RECORD_SEPARATOR)
51
+ end
51
52
  end
52
- end
53
53
 
54
- # read returns a JSON string matching the output of the corresponding stat.
55
- def self.read(stats)
56
- File.read(File.join(DEFAULT_STATIC_PATH, FILE_NAMES[stats]))
54
+ # read returns a JSON string matching the output of the corresponding stat.
55
+ def read(stats)
56
+ File.read(File.join(DEFAULT_STATIC_PATH, FILE_NAMES[stats]))
57
+ end
57
58
  end
58
59
  end
59
60
  end
@@ -4,11 +4,5 @@ require:
4
4
  AllCops:
5
5
  DisabledByDefault: true
6
6
 
7
- Ariadne/SystemArgumentInsteadOfClass:
8
- Enabled: true
9
-
10
7
  Ariadne/NoTagMemoize:
11
- Enabled: false
12
-
13
- Ariadne/AriadneHeroicon:
14
8
  Enabled: true
@@ -16,6 +16,7 @@ module RuboCop
16
16
  #
17
17
  # good
18
18
  # @attributes[:tag] = :h2
19
+ # TODO: TEST THIS
19
20
  class NoTagMemoize < RuboCop::Cop::Cop
20
21
  INVALID_MESSAGE = <<~STR
21
22
  Avoid `[:tag] ||=`. Instead, try one of the following:
data/lib/tasks/docs.rake CHANGED
@@ -47,6 +47,9 @@ namespace :docs do
47
47
  # Rails controller for rendering arbitrary ERB
48
48
  view_context = ApplicationController.new.tap { |c| c.request = ActionDispatch::TestRequest.create }.view_context
49
49
  components = [
50
+ Ariadne::RichTextAreaComponent,
51
+ Ariadne::CommentComponent,
52
+ Ariadne::BodyComponent,
50
53
  Ariadne::BlankslateComponent,
51
54
  Ariadne::BaseButton,
52
55
  Ariadne::ButtonComponent,
@@ -55,6 +58,7 @@ namespace :docs do
55
58
  Ariadne::CounterComponent,
56
59
  Ariadne::GridComponent,
57
60
  Ariadne::FlashComponent,
61
+ Ariadne::FlexComponent,
58
62
  Ariadne::FooterComponent,
59
63
  Ariadne::HeaderComponent,
60
64
  Ariadne::HeadingComponent,
@@ -63,8 +67,13 @@ namespace :docs do
63
67
  Ariadne::InlineFlexComponent,
64
68
  Ariadne::LinkComponent,
65
69
  Ariadne::ListComponent,
70
+ Ariadne::MainComponent,
71
+ Ariadne::NarrowContainerComponent,
72
+ Ariadne::PanelBarComponent,
66
73
  Ariadne::PillComponent,
67
74
  Ariadne::SlideoverComponent,
75
+ Ariadne::TabComponent,
76
+ Ariadne::TabBarComponent,
68
77
  Ariadne::Text,
69
78
  Ariadne::TimeAgoComponent,
70
79
  Ariadne::TimelineComponent,