sdr_view_components 0.1.0
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +75 -0
- data/Rakefile +15 -0
- data/app/assets/stylesheets/styles.scss +118 -0
- data/app/components/base_component.rb +31 -0
- data/app/components/component_support/button_support.rb +14 -0
- data/app/components/component_support/css_classes.rb +16 -0
- data/app/components/component_support/file_hierarchy.rb +22 -0
- data/app/components/sdr_view_components/elements/alert_component.html.erb +14 -0
- data/app/components/sdr_view_components/elements/alert_component.rb +65 -0
- data/app/components/sdr_view_components/elements/banner_component.html.erb +20 -0
- data/app/components/sdr_view_components/elements/banner_component.rb +41 -0
- data/app/components/sdr_view_components/elements/breadcrumb_component.html.erb +7 -0
- data/app/components/sdr_view_components/elements/breadcrumb_component.rb +28 -0
- data/app/components/sdr_view_components/elements/breadcrumb_nav_component.html.erb +10 -0
- data/app/components/sdr_view_components/elements/breadcrumb_nav_component.rb +24 -0
- data/app/components/sdr_view_components/elements/button_component.rb +31 -0
- data/app/components/sdr_view_components/elements/button_link_component.rb +29 -0
- data/app/components/sdr_view_components/elements/navigation/dropdown_menu_component.html.erb +13 -0
- data/app/components/sdr_view_components/elements/navigation/dropdown_menu_component.rb +19 -0
- data/app/components/sdr_view_components/elements/navigation/nav_item_component.rb +25 -0
- data/app/components/sdr_view_components/elements/skip_links_component.html.erb +5 -0
- data/app/components/sdr_view_components/elements/skip_links_component.rb +8 -0
- data/app/components/sdr_view_components/elements/toast_component.html.erb +40 -0
- data/app/components/sdr_view_components/elements/toast_component.rb +35 -0
- data/app/components/sdr_view_components/elements/tooltip_component.html.erb +11 -0
- data/app/components/sdr_view_components/elements/tooltip_component.rb +39 -0
- data/app/components/sdr_view_components/forms/basic_checkbox_component.rb +22 -0
- data/app/components/sdr_view_components/forms/button_component.rb +42 -0
- data/app/components/sdr_view_components/forms/checkbox_component.html.erb +6 -0
- data/app/components/sdr_view_components/forms/checkbox_component.rb +23 -0
- data/app/components/sdr_view_components/forms/field_component.html.erb +6 -0
- data/app/components/sdr_view_components/forms/field_component.rb +74 -0
- data/app/components/sdr_view_components/forms/help_text_component.html.erb +3 -0
- data/app/components/sdr_view_components/forms/help_text_component.rb +23 -0
- data/app/components/sdr_view_components/forms/invalid_feedback_component.rb +43 -0
- data/app/components/sdr_view_components/forms/invalid_feedback_support.rb +21 -0
- data/app/components/sdr_view_components/forms/label_component.html.erb +6 -0
- data/app/components/sdr_view_components/forms/label_component.rb +33 -0
- data/app/components/sdr_view_components/forms/submit_component.html.erb +3 -0
- data/app/components/sdr_view_components/forms/submit_component.rb +25 -0
- data/app/components/sdr_view_components/forms/toggle_component.html.erb +10 -0
- data/app/components/sdr_view_components/forms/toggle_component.rb +24 -0
- data/app/components/sdr_view_components/forms/toggle_option_component.html.erb +2 -0
- data/app/components/sdr_view_components/forms/toggle_option_component.rb +26 -0
- data/app/components/sdr_view_components/structure/footer_component.html.erb +141 -0
- data/app/components/sdr_view_components/structure/footer_component.rb +9 -0
- data/app/components/sdr_view_components/structure/header_component.html.erb +65 -0
- data/app/components/sdr_view_components/structure/header_component.rb +63 -0
- data/app/components/sdr_view_components/structure/style_override_dark_component.html.erb +9 -0
- data/app/components/sdr_view_components/structure/style_override_dark_component.rb +22 -0
- data/app/components/sdr_view_components/structure/style_override_light_component.html.erb +6 -0
- data/app/components/sdr_view_components/structure/style_override_light_component.rb +9 -0
- data/config/routes.rb +4 -0
- data/lib/sdr_view_components/engine.rb +25 -0
- data/lib/sdr_view_components/version.rb +5 -0
- data/lib/sdr_view_components.rb +12 -0
- metadata +141 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
.toast:not(.show) {
|
|
3
|
+
display: block;
|
|
4
|
+
}
|
|
5
|
+
</style>
|
|
6
|
+
<div class="toast-container position-relative bottom-0 end-0 p-3">
|
|
7
|
+
<div
|
|
8
|
+
class="toast align-items-center"
|
|
9
|
+
role="alert"
|
|
10
|
+
aria-live="assertive"
|
|
11
|
+
aria-atomic="true"
|
|
12
|
+
>
|
|
13
|
+
<div class="<%= toast_class %>">
|
|
14
|
+
<div class="d-flex">
|
|
15
|
+
<div
|
|
16
|
+
class="bi bi-exclamation-circle-fill fs-3 me-3 align-self-center d-flex justify-content-center"
|
|
17
|
+
></div>
|
|
18
|
+
<div>
|
|
19
|
+
<div class="fw-semibold"><%= title %> </div>
|
|
20
|
+
<div><%= text %></div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="me-2 m-auto">
|
|
23
|
+
<% if close_text.present? %>
|
|
24
|
+
<button
|
|
25
|
+
type="button"
|
|
26
|
+
class="btn btn-text text-uppercase text-white"
|
|
27
|
+
>
|
|
28
|
+
<%= close_text %>
|
|
29
|
+
</button>
|
|
30
|
+
<% end %>
|
|
31
|
+
<button
|
|
32
|
+
type="button"
|
|
33
|
+
class="btn btn-close btn-close-white"
|
|
34
|
+
aria-label="Close"
|
|
35
|
+
></button>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
# Component for rendering a toast element.
|
|
6
|
+
class ToastComponent < BaseComponent
|
|
7
|
+
def initialize(title:, text:, close_text: nil, variant: :black)
|
|
8
|
+
@title = title
|
|
9
|
+
@text = text
|
|
10
|
+
@close_text = close_text
|
|
11
|
+
@variant = variant
|
|
12
|
+
super()
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :title, :text, :close_text, :variant
|
|
16
|
+
|
|
17
|
+
def toast_class
|
|
18
|
+
merge_classes([background_color], %w[toast-body text-white])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def background_color
|
|
22
|
+
case variant
|
|
23
|
+
when :red
|
|
24
|
+
'bg-stanford-cardinal'
|
|
25
|
+
when :green
|
|
26
|
+
'bg-stanford-digital-green'
|
|
27
|
+
when :poppy
|
|
28
|
+
'bg-poppy-dark'
|
|
29
|
+
else
|
|
30
|
+
'bg-stanford-black'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Elements
|
|
5
|
+
# Component for rendering a tooltip.
|
|
6
|
+
class TooltipComponent < BaseComponent
|
|
7
|
+
def initialize(target_label:, tooltip: nil)
|
|
8
|
+
@target_label = target_label
|
|
9
|
+
@tooltip = tooltip
|
|
10
|
+
super()
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
attr_reader :tooltip, :target_label
|
|
16
|
+
|
|
17
|
+
def render?
|
|
18
|
+
tooltip.present?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def data # rubocop:disable Metrics/MethodLength
|
|
22
|
+
{
|
|
23
|
+
bs_html: true,
|
|
24
|
+
bs_toggle: 'tooltip',
|
|
25
|
+
bs_title: tooltip,
|
|
26
|
+
bs_trigger: 'focus',
|
|
27
|
+
tooltips_target: 'icon'
|
|
28
|
+
}.tap do |data|
|
|
29
|
+
if Settings.ahoy.tooltip
|
|
30
|
+
data[:controller] = 'ahoy-tooltip'
|
|
31
|
+
data[:ahoy_tooltip_label_value] = target_label
|
|
32
|
+
# Note that this is only tracking tooltip when shown by clicking, not when shown by focus.
|
|
33
|
+
data[:action] = 'click->ahoy-tooltip#track'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Component for form checkbox field
|
|
6
|
+
class BasicCheckboxComponent < BaseComponent
|
|
7
|
+
def initialize(form:, field_name:, data: nil, **args)
|
|
8
|
+
@form = form
|
|
9
|
+
@field_name = field_name
|
|
10
|
+
@args = args
|
|
11
|
+
@data = data
|
|
12
|
+
super()
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :args, :data, :form, :field_name, :value
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
form.check_box field_name, data:, **args
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Component for a button that is wrapped in a form
|
|
6
|
+
class ButtonComponent < BaseComponent
|
|
7
|
+
def initialize(link:, label: nil, variant: :primary, classes: [], method: :get, confirm: nil, # rubocop:disable Metrics/ParameterLists
|
|
8
|
+
top: true, data: {})
|
|
9
|
+
@link = link
|
|
10
|
+
@label = label
|
|
11
|
+
@variant = variant
|
|
12
|
+
@classes = classes
|
|
13
|
+
@method = method
|
|
14
|
+
@confirm = confirm
|
|
15
|
+
@top = top
|
|
16
|
+
@data = data
|
|
17
|
+
super()
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_reader :link
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
button_to(link, method: @method,
|
|
24
|
+
class: ComponentSupport::ButtonSupport.classes(variant: @variant, classes:),
|
|
25
|
+
form: { data: }) do
|
|
26
|
+
@label || content
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def classes
|
|
31
|
+
merge_classes(@classes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def data
|
|
35
|
+
@data.tap do |data|
|
|
36
|
+
data[:turbo_frame] = '_top' if @top
|
|
37
|
+
data[:turbo_confirm] = @confirm if @confirm
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%= tag.div container_args do %>
|
|
2
|
+
<%= render SdrViewComponents::Forms::BasicCheckboxComponent.new(form:, field_name:, data:, **input_args) %>
|
|
3
|
+
<%= render SdrViewComponents::Forms::LabelComponent.new(form:, field_name:, **label_args) %>
|
|
4
|
+
<%= render SdrViewComponents::Forms::HelpTextComponent.new(**help_text_args) %>
|
|
5
|
+
<%= render SdrViewComponents::Forms::InvalidFeedbackComponent.new(form:, field_name:) %>
|
|
6
|
+
<% end %>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Component for form checkbox field
|
|
6
|
+
class CheckboxComponent < FieldComponent
|
|
7
|
+
def initialize(**args)
|
|
8
|
+
args[:container_class] = merge_classes('form-check', args[:container_class])
|
|
9
|
+
args[:input_class] = merge_classes('form-check-input', args[:input_class])
|
|
10
|
+
args[:label_default_class] = merge_classes('form-check-label', args[:input_class])
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :value
|
|
15
|
+
|
|
16
|
+
# The component must implement a `default_component` method in order
|
|
17
|
+
# to render in the component slot of the FieldComponent.
|
|
18
|
+
def default_component
|
|
19
|
+
render SdrViewComponents::Forms::BasicCheckboxComponent.new(form:, field_name:, data:, **input_args)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%= tag.div container_args do %>
|
|
2
|
+
<%= component %>
|
|
3
|
+
<%= render SdrViewComponents::Forms::LabelComponent.new(form:, field_name:, **label_args) %>
|
|
4
|
+
<%= render SdrViewComponents::Forms::HelpTextComponent.new(**help_text_args) %>
|
|
5
|
+
<%= render SdrViewComponents::Forms::InvalidFeedbackComponent.new(form:, field_name:) %>
|
|
6
|
+
<% end %>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Base component for all form fields.
|
|
6
|
+
class FieldComponent < BaseComponent
|
|
7
|
+
renders_one :component
|
|
8
|
+
|
|
9
|
+
def initialize(**args)
|
|
10
|
+
@args = args
|
|
11
|
+
super()
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :args
|
|
15
|
+
|
|
16
|
+
def input_args
|
|
17
|
+
args_for(args:, prefix: 'input_').merge({ aria: field_aria, data: })
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def container_args
|
|
21
|
+
args_for(args:, prefix: 'container_')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def data
|
|
25
|
+
@data ||= args.delete(:data) || {}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def error_aria
|
|
29
|
+
InvalidFeedbackSupport.arias_for(field_name:, form:).tap do |arias|
|
|
30
|
+
arias[:describedby] = merge_actions(arias[:describedby], help_text_id) if help_text_args[:text].present?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def error_classes
|
|
35
|
+
merge_classes(args.fetch(:error_classes, []))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def field_aria
|
|
39
|
+
error_aria.tap do |arias|
|
|
40
|
+
# Set aria-required if we want to indicate required, but the field
|
|
41
|
+
# does not actually have a required attribute
|
|
42
|
+
#
|
|
43
|
+
# This is used for collection/work forms where we do server-side
|
|
44
|
+
# validation and don't want to block form submission on empty fields
|
|
45
|
+
arias[:required] = true if @mark_required
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def field_name
|
|
50
|
+
@field_name ||= args.delete(:field_name)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def form
|
|
54
|
+
@form ||= args.delete(:form)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def help_text_args
|
|
58
|
+
args_for(args:, prefix: 'help_').merge({
|
|
59
|
+
id: help_text_id
|
|
60
|
+
})
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def help_text_id
|
|
64
|
+
@help_text_id ||= form.field_id(field_name, 'help')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def label_args
|
|
68
|
+
args_for(args:, prefix: 'label_')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
delegate :id, to: :form, prefix: true
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Component for rendering help text for form fields.
|
|
4
|
+
module SdrViewComponents
|
|
5
|
+
module Forms
|
|
6
|
+
# Component for rendering help text for form fields.
|
|
7
|
+
class HelpTextComponent < BaseComponent
|
|
8
|
+
# this component can take plain text via 'help_text' or a block (which can contain html)
|
|
9
|
+
# it will render the help_text if provided, else it will render the block content
|
|
10
|
+
def initialize(id:, text: nil)
|
|
11
|
+
@text = text
|
|
12
|
+
@id = id
|
|
13
|
+
super()
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :text, :id
|
|
17
|
+
|
|
18
|
+
def render?
|
|
19
|
+
text.present? || content.present?
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Component for rendering invalid feedback for a form field.
|
|
6
|
+
class InvalidFeedbackComponent < BaseComponent
|
|
7
|
+
def initialize(field_name:, form:, classes: [], data: {})
|
|
8
|
+
@field_name = field_name
|
|
9
|
+
@form = form
|
|
10
|
+
@classes = classes
|
|
11
|
+
@data = data
|
|
12
|
+
super()
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :field_name, :form, :data
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
tag.div(class: classes, id:, data:) do
|
|
19
|
+
errors.join(', ')
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def render?
|
|
24
|
+
field_name.present? && errors.present?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def id
|
|
30
|
+
InvalidFeedbackSupport.id_for(field_name:, form:)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def errors
|
|
34
|
+
@errors ||= form.object&.errors&.[](field_name)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def classes
|
|
38
|
+
# Adding is-invalid to trigger the tab error.
|
|
39
|
+
merge_classes(%w[invalid-feedback is-invalid d-block], @classes)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Methods to support invalid feedback for form fields.
|
|
6
|
+
class InvalidFeedbackSupport
|
|
7
|
+
def self.id_for(field_name:, form:)
|
|
8
|
+
form.field_id(field_name, 'error')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.arias_for(field_name:, form:)
|
|
12
|
+
return {} if field_name.nil? || form.object&.errors&.[](field_name).blank?
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
invalid: true,
|
|
16
|
+
describedby: id_for(field_name:, form:)
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%= form.label field_name, class: classes do %>
|
|
2
|
+
<%= content || text %>
|
|
3
|
+
<% end %>
|
|
4
|
+
<%# This is outside the label so that clicking on it doesn't toggle some inputs like checkboxes. %>
|
|
5
|
+
<%= render SdrViewComponents::Elements::TooltipComponent.new(target_label: text, tooltip:) %>
|
|
6
|
+
<%= tag.span { caption } if caption %>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Component for rendering a form label.
|
|
6
|
+
class LabelComponent < BaseComponent
|
|
7
|
+
def initialize(form:, field_name:, text: nil, default_class: 'form-label', hidden: false, # rubocop:disable Metrics/ParameterLists
|
|
8
|
+
classes: [], tooltip: nil, caption: nil)
|
|
9
|
+
@form = form
|
|
10
|
+
@text = text
|
|
11
|
+
@field_name = field_name
|
|
12
|
+
@hidden = hidden
|
|
13
|
+
@default_class = default_class
|
|
14
|
+
@classes = classes
|
|
15
|
+
@tooltip = tooltip
|
|
16
|
+
@caption = caption
|
|
17
|
+
super()
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_reader :field_name, :form, :tooltip, :caption
|
|
21
|
+
|
|
22
|
+
def text
|
|
23
|
+
return field_name if @text.blank?
|
|
24
|
+
|
|
25
|
+
@text
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def classes
|
|
29
|
+
merge_classes(@default_class, @classes, @hidden ? 'visually-hidden' : nil)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Component for a form submit button
|
|
6
|
+
class SubmitComponent < BaseComponent
|
|
7
|
+
def initialize(label: nil, value: nil, form_id: nil, variant: :primary, classes: [], **options) # rubocop:disable Metrics/ParameterLists
|
|
8
|
+
# Either provide label OR value and content
|
|
9
|
+
@form_id = form_id
|
|
10
|
+
@label = label
|
|
11
|
+
@variant = variant
|
|
12
|
+
@options = options
|
|
13
|
+
@classes = classes
|
|
14
|
+
@value = value || label
|
|
15
|
+
super()
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
attr_reader :form, :label, :options, :form_id, :value
|
|
19
|
+
|
|
20
|
+
def classes
|
|
21
|
+
ComponentSupport::ButtonSupport.classes(variant: @variant, classes: @classes)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%= tag.div **container_args do %>
|
|
2
|
+
<div class="d-flex"> <%# Aligns label and tooltip %>
|
|
3
|
+
<%= render SdrViewComponents::Forms::LabelComponent.new(form:, field_name:, **label_args) %>
|
|
4
|
+
</div>
|
|
5
|
+
<div class="btn-group btn-group-toggle" role="group">
|
|
6
|
+
<%= left_toggle_option %>
|
|
7
|
+
<%= right_toggle_option %>
|
|
8
|
+
</div>
|
|
9
|
+
<%= render SdrViewComponents::Forms::InvalidFeedbackComponent.new(form:, field_name:) %>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Component for a toggle-like radio button group field
|
|
6
|
+
class ToggleComponent < FieldComponent
|
|
7
|
+
renders_one :left_toggle_option, lambda { |**args|
|
|
8
|
+
Forms::ToggleOptionComponent.new(position: :left, **args)
|
|
9
|
+
}
|
|
10
|
+
renders_one :right_toggle_option, lambda { |**args|
|
|
11
|
+
Forms::ToggleOptionComponent.new(position: :right, **args)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
def initialize(form:, field_name:, **args)
|
|
15
|
+
@form = form
|
|
16
|
+
@field_name = field_name
|
|
17
|
+
args[:label_classes] = merge_classes('d-block', args[:label_classes])
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_reader :form, :field_name
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SdrViewComponents
|
|
4
|
+
module Forms
|
|
5
|
+
# Component for a toggle option
|
|
6
|
+
class ToggleOptionComponent < BaseComponent
|
|
7
|
+
def initialize(form:, field_name:, label:, value:, data: {}, position: :left) # rubocop:disable Metrics/ParameterLists
|
|
8
|
+
raise ArgumentError, 'position must be :left or :right' unless %i[left right].include?(position)
|
|
9
|
+
|
|
10
|
+
@form = form
|
|
11
|
+
@field_name = field_name
|
|
12
|
+
@label = label
|
|
13
|
+
@value = value
|
|
14
|
+
@data = data
|
|
15
|
+
@position = position
|
|
16
|
+
super()
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :form, :field_name, :label, :value, :data
|
|
20
|
+
|
|
21
|
+
def label_classes
|
|
22
|
+
merge_classes('btn', @position == :left ? 'rounded-start-pill' : 'rounded-end-pill')
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|