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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/assets/javascripts/ariadne-form-with.d.ts +20 -0
- data/app/assets/javascripts/ariadne-form.d.ts +22 -0
- data/app/assets/javascripts/ariadne.d.ts +1 -0
- data/app/assets/javascripts/ariadne_view_components.js +7 -1
- data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
- data/app/assets/javascripts/clipboard-copy-component.d.ts +4 -0
- data/app/assets/javascripts/comment-component.d.ts +13 -0
- data/app/assets/javascripts/rich-text-area-component.d.ts +4 -0
- data/app/assets/javascripts/slideover-component.d.ts +9 -0
- data/app/assets/javascripts/time-ago-component.d.ts +1 -0
- data/app/assets/javascripts/time_ago_component.d.ts +1 -0
- data/app/assets/javascripts/tooltip-component.d.ts +24 -0
- data/app/assets/stylesheets/application.ariadne_view_components.css +6 -3
- data/app/assets/stylesheets/prosemirror.css +323 -0
- data/app/assets/stylesheets/tooltip-component.css +37 -0
- data/app/components/ariadne/ariadne-form.ts +96 -0
- data/app/components/ariadne/ariadne.ts +11 -1
- data/app/components/ariadne/base_button.rb +12 -12
- data/app/components/ariadne/base_component.rb +13 -131
- data/app/components/ariadne/blankslate_component.html.erb +5 -5
- data/app/components/ariadne/blankslate_component.rb +5 -10
- data/app/components/ariadne/body_component.rb +30 -0
- data/app/components/ariadne/button_component.rb +12 -16
- data/app/components/ariadne/clipboard_copy_component.html.erb +5 -4
- data/app/components/ariadne/clipboard_copy_component.rb +41 -3
- data/app/components/ariadne/comment-component.ts +55 -0
- data/app/components/ariadne/comment_component.html.erb +22 -0
- data/app/components/ariadne/comment_component.rb +57 -0
- data/app/components/ariadne/component.rb +4 -3
- data/app/components/ariadne/container_component.rb +1 -1
- data/app/components/ariadne/counter_component.rb +4 -4
- data/app/components/ariadne/flash_component.html.erb +12 -12
- data/app/components/ariadne/flash_component.rb +17 -17
- data/app/components/ariadne/flex_component.rb +49 -0
- data/app/components/ariadne/footer_component.html.erb +1 -1
- data/app/components/ariadne/footer_component.rb +1 -1
- data/app/components/ariadne/grid_component.html.erb +13 -4
- data/app/components/ariadne/grid_component.rb +22 -11
- data/app/components/ariadne/header_component.html.erb +7 -7
- data/app/components/ariadne/header_component.rb +8 -8
- data/app/components/ariadne/heading_component.rb +3 -3
- data/app/components/ariadne/heroicon_component.html.erb +4 -6
- data/app/components/ariadne/heroicon_component.rb +21 -10
- data/app/components/ariadne/inline_flex_component.rb +14 -12
- data/app/components/ariadne/link_component.rb +13 -8
- data/app/components/ariadne/list_component.html.erb +5 -7
- data/app/components/ariadne/list_component.rb +9 -38
- data/app/components/ariadne/main_component.rb +32 -0
- data/app/components/ariadne/narrow_container_component.html.erb +3 -0
- data/app/components/ariadne/narrow_container_component.rb +30 -0
- data/app/components/ariadne/panel_bar_component.html.erb +20 -0
- data/app/components/ariadne/panel_bar_component.rb +79 -0
- data/app/components/ariadne/pill_component.rb +2 -2
- data/app/components/ariadne/rich-text-area-component.ts +32 -0
- data/app/components/ariadne/rich_text_area_component.html.erb +6 -0
- data/app/components/ariadne/rich_text_area_component.rb +35 -0
- data/app/components/ariadne/slideover-component.ts +3 -3
- data/app/components/ariadne/slideover_component.html.erb +3 -6
- data/app/components/ariadne/slideover_component.rb +19 -15
- data/app/components/ariadne/tab_bar_component.html.erb +3 -0
- data/app/components/ariadne/tab_bar_component.rb +45 -0
- data/app/components/ariadne/tab_component.html.erb +7 -0
- data/app/components/ariadne/tab_component.rb +43 -0
- data/app/components/ariadne/{time_ago_component.ts → time-ago-component.ts} +0 -0
- data/app/components/ariadne/time_ago_component.rb +2 -2
- data/app/components/ariadne/timeline_component.html.erb +6 -6
- data/app/components/ariadne/tooltip-component.ts +57 -0
- data/app/components/ariadne/tooltip_component.html.erb +4 -0
- data/app/components/ariadne/tooltip_component.rb +34 -31
- data/app/lib/ariadne/action_view_extensions/form_helper.rb +4 -1
- data/app/lib/ariadne/fetch_or_fallback_helper.rb +3 -1
- data/app/lib/ariadne/form_builder.rb +22 -22
- data/app/lib/ariadne/icon_helper.rb +1 -1
- data/lib/ariadne/view_components/engine.rb +171 -3
- data/lib/ariadne/view_components/version.rb +1 -1
- data/lib/ariadne/view_components.rb +31 -30
- data/lib/rubocop/config/default.yml +0 -6
- data/lib/rubocop/cop/ariadne/no_tag_memoize.rb +1 -0
- data/lib/tasks/docs.rake +9 -0
- data/static/arguments.yml +186 -15
- data/static/audited_at.json +12 -2
- data/static/classes.yml +200 -154
- data/static/constants.json +168 -146
- data/static/statuses.json +12 -2
- metadata +65 -13
- data/lib/ariadne/classify/utilities.rb +0 -199
- data/lib/ariadne/classify/utilities.yml +0 -1817
- data/lib/ariadne/classify/validation.rb +0 -18
- data/lib/ariadne/classify.rb +0 -218
- data/lib/rubocop/cop/ariadne/ariadne_heroicon.rb +0 -252
- data/lib/rubocop/cop/ariadne/component_name_migration.rb +0 -35
- data/lib/rubocop/cop/ariadne/system_argument_instead_of_class.rb +0 -57
- data/lib/tasks/utilities.rake +0 -121
@@ -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
|
-
|
28
|
-
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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
|
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
|
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:
|
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(
|
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[:
|
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
|
-
|
102
|
-
|
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
|
-
|
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
|
-
|
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 = "
|
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(
|
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(
|
31
|
-
options[:class] = class_names(DEFAULT_LABEL_CLASSES, options
|
32
|
-
super(
|
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
|
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(
|
44
|
-
super(method,
|
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
|
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
|
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
|
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
|
67
|
+
options[:class] = class_names(DEFAULT_PASSWORD_CLASSES, options.delete(:classes))
|
68
68
|
super(method, **options)
|
69
69
|
end
|
70
70
|
end
|
@@ -1,10 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "rails/engine"
|
4
|
-
|
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
|
-
|
32
|
-
|
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
|
@@ -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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
44
|
+
# dump generates the requested stat hash and outputs it to a file.
|
45
|
+
def dump(stats)
|
46
|
+
require "json"
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
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
|
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,
|