stimulus_plumbers 0.3.2 → 0.4.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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -0
  3. data/README.md +3 -1
  4. data/app/assets/javascripts/stimulus-plumbers/controllers.manifest.json +273 -0
  5. data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.es.js +269 -160
  6. data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.umd.js +1 -1
  7. data/app/assets/stylesheets/stimulus_plumbers/tokens.css +56 -13
  8. data/config/locales/en.yml +10 -0
  9. data/lib/stimulus_plumbers/components/avatar.rb +24 -17
  10. data/lib/stimulus_plumbers/components/button/group.rb +15 -4
  11. data/lib/stimulus_plumbers/components/button/slots.rb +11 -0
  12. data/lib/stimulus_plumbers/components/button.rb +45 -11
  13. data/lib/stimulus_plumbers/components/calendar/turbo/days_of_month.rb +151 -0
  14. data/lib/stimulus_plumbers/components/calendar/turbo/days_of_week.rb +62 -0
  15. data/lib/stimulus_plumbers/components/calendar/turbo/months_of_year.rb +99 -0
  16. data/lib/stimulus_plumbers/components/calendar/turbo/years_of_decade.rb +86 -0
  17. data/lib/stimulus_plumbers/components/calendar/turbo.rb +65 -0
  18. data/lib/stimulus_plumbers/components/calendar.rb +70 -26
  19. data/lib/stimulus_plumbers/components/card/slots.rb +26 -0
  20. data/lib/stimulus_plumbers/components/card.rb +56 -10
  21. data/lib/stimulus_plumbers/components/combobox/builder.rb +45 -0
  22. data/lib/stimulus_plumbers/components/combobox/date/navigation.rb +72 -0
  23. data/lib/stimulus_plumbers/components/combobox/date/navigator.rb +25 -0
  24. data/lib/stimulus_plumbers/components/combobox/date.rb +37 -23
  25. data/lib/stimulus_plumbers/components/combobox/dropdown.rb +30 -21
  26. data/lib/stimulus_plumbers/components/combobox/options/option.rb +8 -2
  27. data/lib/stimulus_plumbers/components/combobox/options/option_group.rb +8 -2
  28. data/lib/stimulus_plumbers/components/combobox/options.rb +9 -5
  29. data/lib/stimulus_plumbers/components/combobox/time/drum.rb +8 -2
  30. data/lib/stimulus_plumbers/components/combobox/time.rb +50 -47
  31. data/lib/stimulus_plumbers/components/combobox/trigger.rb +62 -14
  32. data/lib/stimulus_plumbers/components/combobox/typeahead.rb +96 -0
  33. data/lib/stimulus_plumbers/components/combobox.rb +62 -38
  34. data/lib/stimulus_plumbers/components/divider.rb +25 -4
  35. data/lib/stimulus_plumbers/components/icon.rb +11 -17
  36. data/lib/stimulus_plumbers/components/input_group.rb +29 -0
  37. data/lib/stimulus_plumbers/components/link/slots.rb +11 -0
  38. data/lib/stimulus_plumbers/components/link.rb +63 -0
  39. data/lib/stimulus_plumbers/components/list/item/slots.rb +13 -0
  40. data/lib/stimulus_plumbers/components/list/item.rb +83 -0
  41. data/lib/stimulus_plumbers/components/list/section.rb +73 -0
  42. data/lib/stimulus_plumbers/components/list.rb +31 -0
  43. data/lib/stimulus_plumbers/components/popover/panel.rb +32 -0
  44. data/lib/stimulus_plumbers/components/popover/trigger.rb +27 -0
  45. data/lib/stimulus_plumbers/components/popover.rb +44 -14
  46. data/lib/stimulus_plumbers/engine.rb +1 -0
  47. data/lib/stimulus_plumbers/form/base.rb +103 -0
  48. data/lib/stimulus_plumbers/form/builder.rb +71 -24
  49. data/lib/stimulus_plumbers/form/field.rb +56 -88
  50. data/lib/stimulus_plumbers/form/fields/error.rb +1 -1
  51. data/lib/stimulus_plumbers/form/fields/fieldset.rb +11 -8
  52. data/lib/stimulus_plumbers/form/fields/hint.rb +1 -1
  53. data/lib/stimulus_plumbers/form/fields/inputs/checkbox.rb +115 -0
  54. data/lib/stimulus_plumbers/form/fields/inputs/combobox.rb +24 -0
  55. data/lib/stimulus_plumbers/form/fields/inputs/datetime.rb +42 -48
  56. data/lib/stimulus_plumbers/form/fields/inputs/file.rb +9 -8
  57. data/lib/stimulus_plumbers/form/fields/inputs/password.rb +32 -25
  58. data/lib/stimulus_plumbers/form/fields/inputs/radio.rb +60 -0
  59. data/lib/stimulus_plumbers/form/fields/inputs/search.rb +34 -57
  60. data/lib/stimulus_plumbers/form/fields/inputs/select/grouped.rb +22 -29
  61. data/lib/stimulus_plumbers/form/fields/inputs/select/timezone.rb +3 -44
  62. data/lib/stimulus_plumbers/form/fields/inputs/select/weekday.rb +3 -28
  63. data/lib/stimulus_plumbers/form/fields/inputs/select.rb +62 -49
  64. data/lib/stimulus_plumbers/form/fields/inputs/submit.rb +10 -7
  65. data/lib/stimulus_plumbers/form/fields/inputs/text.rb +29 -22
  66. data/lib/stimulus_plumbers/form/fields/inputs/text_area.rb +9 -8
  67. data/lib/stimulus_plumbers/form/fields/label/floating.rb +41 -0
  68. data/lib/stimulus_plumbers/form/fields/label.rb +9 -3
  69. data/lib/stimulus_plumbers/form/fields/renderer.rb +39 -0
  70. data/lib/stimulus_plumbers/helpers/avatar_helper.rb +2 -2
  71. data/lib/stimulus_plumbers/helpers/button_helper.rb +4 -8
  72. data/lib/stimulus_plumbers/helpers/calendar_helper.rb +14 -11
  73. data/lib/stimulus_plumbers/helpers/calendar_turbo_helper.rb +49 -11
  74. data/lib/stimulus_plumbers/helpers/card_helper.rb +2 -12
  75. data/lib/stimulus_plumbers/helpers/combobox_helper.rb +27 -47
  76. data/lib/stimulus_plumbers/helpers/divider_helper.rb +2 -2
  77. data/lib/stimulus_plumbers/helpers/icon_helper.rb +11 -0
  78. data/lib/stimulus_plumbers/helpers/link_helper.rb +11 -0
  79. data/lib/stimulus_plumbers/helpers/list_helper.rb +11 -0
  80. data/lib/stimulus_plumbers/helpers/plumber_helper.rb +3 -6
  81. data/lib/stimulus_plumbers/helpers/popover_helper.rb +2 -2
  82. data/lib/stimulus_plumbers/helpers.rb +6 -2
  83. data/lib/stimulus_plumbers/logger.rb +4 -3
  84. data/lib/stimulus_plumbers/plumber/base.rb +6 -1
  85. data/lib/stimulus_plumbers/plumber/dispatcher/klass_proxy.rb +4 -3
  86. data/lib/stimulus_plumbers/plumber/dispatcher/method_call.rb +4 -3
  87. data/lib/stimulus_plumbers/plumber/dispatcher.rb +4 -4
  88. data/lib/stimulus_plumbers/plumber/options/aria.rb +17 -0
  89. data/lib/stimulus_plumbers/plumber/options/html.rb +29 -0
  90. data/lib/stimulus_plumbers/plumber/options/stimulus.rb +29 -0
  91. data/lib/stimulus_plumbers/plumber/options/theme.rb +19 -0
  92. data/lib/stimulus_plumbers/plumber/options/token_list.rb +29 -0
  93. data/lib/stimulus_plumbers/plumber/renderer.rb +136 -41
  94. data/lib/stimulus_plumbers/plumber/slots.rb +74 -0
  95. data/lib/stimulus_plumbers/themes/base.rb +20 -23
  96. data/lib/stimulus_plumbers/themes/icons/external.rb +60 -0
  97. data/lib/stimulus_plumbers/themes/icons/registry.rb +36 -0
  98. data/lib/stimulus_plumbers/themes/schema/avatar/ranges.rb +13 -0
  99. data/lib/stimulus_plumbers/themes/schema/button/ranges.rb +16 -0
  100. data/lib/stimulus_plumbers/themes/schema/card/ranges.rb +13 -0
  101. data/lib/stimulus_plumbers/themes/schema/form/checkbox/ranges.rb +16 -0
  102. data/lib/stimulus_plumbers/themes/schema/form/radio/ranges.rb +16 -0
  103. data/lib/stimulus_plumbers/themes/schema/form/ranges.rb +1 -2
  104. data/lib/stimulus_plumbers/themes/schema/icon.rb +57 -15
  105. data/lib/stimulus_plumbers/themes/schema/link/ranges.rb +14 -0
  106. data/lib/stimulus_plumbers/themes/schema/ranges.rb +1 -5
  107. data/lib/stimulus_plumbers/themes/schema.rb +142 -67
  108. data/lib/stimulus_plumbers/version.rb +1 -1
  109. data/lib/stimulus_plumbers.rb +22 -17
  110. metadata +46 -17
  111. data/lib/stimulus_plumbers/components/action_list/item.rb +0 -27
  112. data/lib/stimulus_plumbers/components/action_list/section.rb +0 -22
  113. data/lib/stimulus_plumbers/components/action_list.rb +0 -23
  114. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_month.rb +0 -145
  115. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_week.rb +0 -39
  116. data/lib/stimulus_plumbers/components/calendar/month/turbo.rb +0 -55
  117. data/lib/stimulus_plumbers/components/card/section.rb +0 -25
  118. data/lib/stimulus_plumbers/components/combobox/autocomplete.rb +0 -47
  119. data/lib/stimulus_plumbers/components/combobox/popover.rb +0 -24
  120. data/lib/stimulus_plumbers/components/date_picker/navigation.rb +0 -41
  121. data/lib/stimulus_plumbers/components/date_picker/navigator.rb +0 -31
  122. data/lib/stimulus_plumbers/components/popover/builder.rb +0 -25
  123. data/lib/stimulus_plumbers/form/fields/input_group.rb +0 -25
  124. data/lib/stimulus_plumbers/form/fields/inputs/choice.rb +0 -69
  125. data/lib/stimulus_plumbers/helpers/action_list_helper.rb +0 -25
  126. data/lib/stimulus_plumbers/plumber/html_options.rb +0 -52
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module Checkbox
8
+ def check_box(attribute, options = {}, checked_value = "1", unchecked_value = "0")
9
+ html_options = merge_html_options(theme.resolve(:form_field_input_checkbox), options)
10
+ super(attribute, html_options, checked_value, unchecked_value)
11
+ end
12
+
13
+ def collection_check_boxes(
14
+ attribute,
15
+ collection,
16
+ value_method,
17
+ text_method,
18
+ options = {},
19
+ html_options = {},
20
+ &block
21
+ )
22
+ item_opts = merge_html_options(theme.resolve(:form_field_input_checkbox), html_options)
23
+ if block_given?
24
+ super(attribute, collection, value_method, text_method, options, item_opts, &block)
25
+ else
26
+ super(attribute, collection, value_method, text_method, options, item_opts) do |builder|
27
+ render_check_box_label(builder, theme.resolve(:form_field_checkbox_label))
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def render_check_box(attribute, collection, value_method, text_method, field_opts, **kwargs)
35
+ if collection
36
+ render_collection_check_box(attribute, collection, value_method, text_method, field_opts, **kwargs)
37
+ else
38
+ render_single_check_box(attribute, field_opts, **kwargs)
39
+ end
40
+ end
41
+
42
+ def render_collection_check_box(attribute, collection, value_method, text_method, field_opts, **kwargs)
43
+ type = kwargs.delete(:type) { :default }
44
+ variant = kwargs.delete(:variant) { :default }
45
+ field = Field.new(@template, **{ layout: :inline }.deep_merge(field_opts))
46
+ render_fieldset(attribute, field) do |error|
47
+ item_opts = merge_html_options(
48
+ theme.resolve(:form_field_input_checkbox, error: error, type: type, variant: variant),
49
+ kwargs,
50
+ field.required ? { aria: { required: "true" } } : {}
51
+ )
52
+ @template.collection_check_boxes(
53
+ @object_name, attribute, collection, value_method, text_method, {}, item_opts
54
+ ) do |builder|
55
+ render_check_box_label(builder, theme.resolve(:form_field_checkbox_label, type: type, variant: variant), type)
56
+ end
57
+ end
58
+ end
59
+
60
+ def render_check_box_label(builder, label_opts, type = :default)
61
+ html_options = merge_html_options(label_opts)
62
+ case type
63
+ when :card
64
+ builder.label(**html_options) { @template.safe_join([builder.text, builder.check_box]) }
65
+ else
66
+ builder.label(**html_options) { @template.safe_join([builder.check_box, builder.text]) }
67
+ end
68
+ end
69
+
70
+ def render_single_check_box(attribute, field_opts, checked_value: "1", unchecked_value: "0", **kwargs)
71
+ field = Field.new(@template, **field_opts)
72
+ input_id = field_id(attribute)
73
+ error = field.error?(object, attribute)
74
+
75
+ Fields::Group.new(@template).render(layout: :stacked, error: error) do
76
+ check_box_html = build_check_box_input(field, attribute, input_id, error, checked_value, unchecked_value, **kwargs)
77
+ @template.safe_join(
78
+ [
79
+ build_check_box_label(field, attribute, input_id, check_box_html),
80
+ field.render_hint(input_id),
81
+ field.render_errors(object, attribute, input_id)
82
+ ]
83
+ )
84
+ end
85
+ end
86
+
87
+ def build_check_box_input(field, attribute, input_id, error, checked_value, unchecked_value, **kwargs)
88
+ check_box_opts = merge_html_options(
89
+ theme.resolve(:form_checkbox, error: error),
90
+ kwargs,
91
+ {
92
+ id: input_id,
93
+ aria: {
94
+ describedby: field.described_by(object, attribute, input_id),
95
+ invalid: error ? "true" : nil,
96
+ required: field.required ? "true" : nil
97
+ }.compact
98
+ },
99
+ field.required ? { required: true } : {}
100
+ )
101
+ @template.check_box(@object_name, attribute, objectify_options(check_box_opts), checked_value, unchecked_value)
102
+ end
103
+
104
+ def build_check_box_label(field, attribute, input_id, check_box_html)
105
+ label_opts = merge_html_options(theme.resolve(:form_field_checkbox_label))
106
+ label_text = field.label || attribute.to_s.humanize
107
+ @template.content_tag(:label, for: input_id, **label_opts) do
108
+ @template.safe_join([check_box_html, label_text])
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module Combobox
8
+ private
9
+
10
+ def render_combobox(attribute, input_id:, opts:, error:, **kwargs, &block)
11
+ combobox_opts = opts.deep_merge(input: { name: field_name(attribute) })
12
+
13
+ Components::Combobox.new(@template).render(
14
+ id: input_id,
15
+ **combobox_opts,
16
+ **merge_html_options(theme.resolve(:form_field_input_combobox, error: error), kwargs),
17
+ &block
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,78 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "combobox"
4
+
3
5
  module StimulusPlumbers
4
6
  module Form
5
7
  module Fields
6
8
  module Inputs
7
9
  module Datetime
10
+ include Combobox
11
+
8
12
  def date_field(attribute, options = {})
9
- html_native = options.delete(:html_native) { false }
10
- Field.new(@template, **options).render(
11
- object,
12
- attribute,
13
- input_id: field_id(attribute)
14
- ) do |html_opts, opts, error|
15
- if html_native
16
- html_options = merge_html_options(opts, html_opts, field_theme(:form_input, error: error))
17
- super(attribute, html_options)
18
- else
19
- render_date_combobox(attribute, html_opts, error)
20
- end
21
- end
13
+ html_options = merge_html_options(theme.resolve(:form_field_input), options)
14
+ super(attribute, html_options)
22
15
  end
23
16
 
24
17
  def time_field(attribute, options = {})
25
- html_native = options.delete(:html_native) { false }
26
- format = options.delete(:format) { :h12 }
27
- step = options.delete(:step) { 1 }
28
- Field.new(@template, **options).render(
29
- object,
30
- attribute,
31
- input_id: field_id(attribute)
32
- ) do |html_opts, opts, error|
33
- if html_native
34
- html_options = merge_html_options(opts, html_opts, field_theme(:form_input, error: error))
35
- super(attribute, html_options)
36
- else
37
- render_time_combobox(attribute, html_opts, error, format: format, step: step)
38
- end
39
- end
18
+ html_options = merge_html_options(theme.resolve(:form_field_input), options)
19
+ super(attribute, html_options)
40
20
  end
41
21
 
42
22
  private
43
23
 
44
- def render_date_combobox(attribute, html_opts, error)
24
+ def render_combobox_date(attribute, html_opts, opts, error, icon_leading: nil, icon_trailing: nil, **kwargs)
45
25
  current_value = object.respond_to?(attribute) ? object.public_send(attribute) : nil
46
- opts = Components::Combobox::Date.default_opts.deep_merge(
47
- input: { value: current_value, data: { combobox_date_date_value: current_value } },
48
- trigger: { aria: html_opts[:aria] },
49
- popover: { labelledby: Field.label_id(html_opts[:id]) }
50
- )
26
+ labelledby = Field.label_id(html_opts[:id])
27
+ combobox_opts = {
28
+ input: { value: current_value },
29
+ trigger: { aria: html_opts[:aria], icon_leading: icon_leading, icon_trailing: icon_trailing }.compact,
30
+ **opts
31
+ }
51
32
  render_combobox(
52
33
  attribute,
53
34
  input_id: html_opts[:id],
54
- opts: opts,
55
- err: error,
56
- data: { input_format_type_value: "date" }
57
- ) do |popover_id|
58
- Components::Combobox::Date.new(@template).render(value: current_value, popover_id: popover_id)
35
+ opts: combobox_opts,
36
+ error: error,
37
+ **kwargs
38
+ ) do |c|
39
+ c.date(value: current_value, labelledby: labelledby)
59
40
  end
60
41
  end
61
42
 
62
- def render_time_combobox(attribute, html_opts, error, format:, step:)
43
+ def render_combobox_time(
44
+ attribute,
45
+ html_opts,
46
+ opts,
47
+ error,
48
+ format: :h12,
49
+ step: 1,
50
+ icon_leading: nil,
51
+ icon_trailing: nil,
52
+ **kwargs
53
+ )
63
54
  current_value = object.respond_to?(attribute) ? object.public_send(attribute) : nil
64
- opts = Components::Combobox::Time.default_opts.deep_merge(
55
+ labelledby = Field.label_id(html_opts[:id])
56
+ combobox_opts = {
65
57
  input: { value: current_value },
66
- trigger: { aria: html_opts[:aria] },
67
- popover: { labelledby: Field.label_id(html_opts[:id]) }
68
- )
58
+ trigger: { aria: html_opts[:aria], icon_leading: icon_leading, icon_trailing: icon_trailing }.compact,
59
+ **opts
60
+ }
69
61
  render_combobox(
70
62
  attribute,
71
63
  input_id: html_opts[:id],
72
- opts: opts,
73
- err: error,
74
- data: { input_format_type_value: "time", input_format_options_value: { format: format }.to_json }
75
- ) { Components::Combobox::Time.new(@template).render(format: format, step: step, value: current_value) }
64
+ opts: combobox_opts,
65
+ error: error,
66
+ **kwargs
67
+ ) do |c|
68
+ c.time(format: format, step: step, value: current_value, labelledby: labelledby)
69
+ end
76
70
  end
77
71
  end
78
72
  end
@@ -6,14 +6,15 @@ module StimulusPlumbers
6
6
  module Inputs
7
7
  module File
8
8
  def file_field(attribute, options = {})
9
- Field.new(@template, **options).render(
10
- object,
11
- attribute,
12
- input_id: field_id(attribute)
13
- ) do |html_opts, opts, error|
14
- html_options = merge_html_options(opts, html_opts, field_theme(:form_file, error: error))
15
- super(attribute, html_options)
16
- end
9
+ html_options = merge_html_options(theme.resolve(:form_field_input_file), options)
10
+ super(attribute, html_options)
11
+ end
12
+
13
+ private
14
+
15
+ def render_file_input(attribute, html_opts, opts, error, **kwargs)
16
+ html_options = merge_html_options(theme.resolve(:form_field_input_file, error: error), opts, html_opts, kwargs)
17
+ @template.file_field(@object_name, attribute, objectify_options(html_options))
17
18
  end
18
19
  end
19
20
  end
@@ -6,48 +6,55 @@ module StimulusPlumbers
6
6
  module Inputs
7
7
  module Password
8
8
  def password_field(attribute, options = {})
9
- reveal = options.delete(:reveal) { false }
10
- Field.new(@template, **options).render(
11
- object,
12
- attribute,
13
- input_id: field_id(attribute)
14
- ) do |html_opts, opts, error|
15
- if reveal
16
- render_reveal_password(merge_html_options(opts, html_opts), error) do |html_options|
17
- super(attribute, html_options)
18
- end
19
- else
20
- html_options = merge_html_options(opts, html_opts, field_theme(:form_input, error: error))
21
- super(attribute, html_options)
9
+ revealable = options.delete(:revealable) { false }
10
+ html_options = merge_html_options(theme.resolve(:form_field_input), options)
11
+ if revealable
12
+ render_revealable_password(false) do
13
+ super(attribute, merge_html_options(html_options, { data: { input_formatter_target: "input" } }))
22
14
  end
15
+ else
16
+ super(attribute, html_options)
23
17
  end
24
18
  end
25
19
 
26
20
  private
27
21
 
28
- def render_reveal_password(html_opts, error, &block)
29
- html_options = merge_html_options(
30
- html_opts,
31
- field_theme(:form_input, error: error),
32
- { data: { input_format_target: "input" } }
33
- )
22
+ def render_password_input(attribute, html_opts, opts, error, revealable: false, **kwargs)
23
+ if revealable
24
+ html_options = merge_html_options(
25
+ theme.resolve(:form_field_input, error: error),
26
+ opts,
27
+ html_opts,
28
+ kwargs,
29
+ { data: { input_formatter_target: "input" } }
30
+ )
31
+ render_revealable_password(error) do
32
+ @template.password_field(@object_name, attribute, objectify_options(html_options))
33
+ end
34
+ else
35
+ html_options = merge_html_options(theme.resolve(:form_field_input, error: error), opts, html_opts, kwargs)
36
+ @template.password_field(@object_name, attribute, objectify_options(html_options))
37
+ end
38
+ end
39
+
40
+ def render_revealable_password(error, &block)
34
41
  render_input_group(
35
42
  error: error,
36
43
  trailing: method(:reveal_button),
37
44
  **merge_html_options(
38
- field_theme(:form_input_reveal, error: error),
39
- { data: { controller: "input-format", input_format_type_value: "password" } }
45
+ theme.resolve(:form_field_input_reveal, error: error),
46
+ { data: { controller: "input-formatter", input_formatter_format_value: "password" } }
40
47
  )
41
- ) { @template.capture(html_options, &block) }
48
+ ) { @template.capture(&block) }
42
49
  end
43
50
 
44
51
  def reveal_button
45
52
  html_options = merge_html_options(
46
- field_theme(:form_button_reveal),
53
+ theme.resolve(:form_field_input_button_reveal),
47
54
  {
48
55
  type: "button",
49
- aria: { label: "Show password", pressed: "false" },
50
- data: { input_format_target: "toggle", action: "click->input-format#toggle" }
56
+ aria: { label: I18n.t("stimulus_plumbers.form.password.show", default: "Show password"), pressed: "false" },
57
+ data: { input_formatter_target: "toggle", action: "click->input-formatter#toggle" }
51
58
  }
52
59
  )
53
60
  @template.content_tag(:button, "", **html_options)
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module Radio
8
+ def radio_button(attribute, tag_value, options = {})
9
+ html_options = merge_html_options(theme.resolve(:form_field_input_radio), options)
10
+ super(attribute, tag_value, html_options)
11
+ end
12
+
13
+ def collection_radio_buttons(
14
+ attribute,
15
+ collection,
16
+ value_method,
17
+ text_method,
18
+ options = {},
19
+ html_options = {},
20
+ &block
21
+ )
22
+ item_opts = merge_html_options(theme.resolve(:form_field_input_radio), html_options)
23
+ if block_given?
24
+ super(attribute, collection, value_method, text_method, options, item_opts, &block)
25
+ else
26
+ super(attribute, collection, value_method, text_method, options, item_opts) do |builder|
27
+ render_radio_button_label(builder, theme.resolve(:form_field_radio_label))
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def render_collection_radio_button(attribute, collection, value_method, text_method, field_opts, **kwargs)
35
+ type = kwargs.delete(:type) { :default }
36
+ variant = kwargs.delete(:variant) { :default }
37
+ field = Field.new(@template, **{ layout: :inline }.deep_merge(field_opts))
38
+ render_fieldset(attribute, field) do |error|
39
+ item_opts = merge_html_options(
40
+ theme.resolve(:form_field_input_radio, error: error, type: type, variant: variant),
41
+ kwargs,
42
+ field.required ? { aria: { required: "true" } } : {}
43
+ )
44
+ @template.collection_radio_buttons(
45
+ @object_name, attribute, collection, value_method, text_method, {}, item_opts
46
+ ) do |builder|
47
+ render_radio_button_label(builder, theme.resolve(:form_field_radio_label, type: type, variant: variant))
48
+ end
49
+ end
50
+ end
51
+
52
+ def render_radio_button_label(builder, label_opts)
53
+ html_options = merge_html_options(label_opts)
54
+ builder.label(**html_options) { @template.safe_join([builder.radio_button, builder.text]) }
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,96 +1,73 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "combobox"
4
+
3
5
  module StimulusPlumbers
4
6
  module Form
5
7
  module Fields
6
8
  module Inputs
7
9
  module Search
8
- def search_field(attribute, options = {})
9
- html_native = options.delete(:html_native) { false }
10
- clearable = options.delete(:clearable) { false }
11
- url = options.delete(:url) { nil }
12
- choices = options.delete(:options) { [] }
13
-
14
- Field.new(@template, **options).render(
15
- object,
16
- attribute,
17
- input_id: field_id(attribute)
18
- ) do |html_opts, opts, error|
19
- if html_native
20
- render_search_input(html_opts, opts, error, clearable: clearable) do |html_options|
21
- super(attribute, html_options)
22
- end
23
- else
24
- render_search_combobox(
25
- attribute,
26
- html_opts,
27
- error,
28
- url: url,
29
- clearable: clearable
30
- ) do |combobox_opts, input_id, current_value|
31
- render_search_autocomplete(attribute, input_id, combobox_opts, error, choices, current_value)
32
- end
33
- end
34
- end
35
- end
36
-
37
- private
38
-
39
- def render_search_input(html_opts, opts, error, clearable:, &block)
40
- data = clearable ? { data: { input_search_target: "input" } } : {}
41
- html_options = merge_html_options(opts, html_opts, field_theme(:form_input, error: error), data)
42
- input_html = @template.capture(html_options, &block)
10
+ include Combobox
43
11
 
12
+ def search_field(attribute, options = {})
13
+ clearable = options.delete(:clearable) { false }
14
+ data = clearable ? { data: { input_clearable_target: "input" } } : {}
15
+ html_options = merge_html_options(theme.resolve(:form_field_input), options, data)
16
+ input_html = super(attribute, html_options)
44
17
  return input_html unless clearable
45
18
 
46
19
  render_input_group(
47
20
  trailing: method(:clear_button),
48
- error: !!error,
49
- **merge_html_options(field_theme(:form_input_search), { data: { controller: "input-search" } })
21
+ error: false,
22
+ **merge_html_options(theme.resolve(:form_field_input_clearable), { data: { controller: "input-clearable" } })
50
23
  ) { input_html }
51
24
  end
52
25
 
53
- def render_search_combobox(attribute, html_opts, error, url:, clearable:, &block)
26
+ private
27
+
28
+ def render_combobox_typeahead(attribute, html_opts, opts, error, url: nil, clearable: false, choices: [], **kwargs)
54
29
  current_value = object.respond_to?(attribute) ? object.public_send(attribute) : nil
55
- input_id = html_opts[:id]
56
- opts = Components::Combobox::Autocomplete.default_opts.deep_merge(
57
- input: { value: current_value },
58
- trigger: { data: clearable ? { input_search_target: "input" } : {}, aria: html_opts[:aria] },
59
- popover: { data: url ? { combobox_dropdown_url_value: url } : {} }
30
+ combobox_html = render_typeahead_combobox(
31
+ attribute, html_opts, opts, error, current_value, clearable: clearable, choices: choices, url: url, **kwargs
60
32
  )
61
33
 
62
- combobox_html = @template.capture(opts, input_id, current_value, &block)
63
34
  return combobox_html unless clearable
64
35
 
65
36
  render_input_group(
66
37
  trailing: method(:clear_button),
67
- error: !!error,
68
- **merge_html_options(field_theme(:form_input_search), { data: { controller: "input-search" } })
38
+ error: error,
39
+ **merge_html_options(theme.resolve(:form_field_input_clearable), { data: { controller: "input-clearable" } })
69
40
  ) { combobox_html }
70
41
  end
71
42
 
72
- def render_search_autocomplete(attribute, input_id, combobox_opts, error, choices, current_value)
43
+ def render_typeahead_combobox(attribute, html_opts, opts, error, current_value, clearable:, choices:, url:, **kwargs)
44
+ input_id = html_opts[:id]
45
+ labelledby = Field.label_id(input_id)
46
+ combobox_opts = {
47
+ input: { value: current_value },
48
+ trigger: { data: clearable ? { input_clearable_target: "input" } : {}, aria: html_opts[:aria] },
49
+ **opts
50
+ }
73
51
  render_combobox(
74
52
  attribute,
75
53
  input_id: input_id,
76
54
  opts: combobox_opts,
77
- err: error,
78
- data: {
79
- input_combobox_combobox_dropdown_outlet: "##{Components::Combobox.popover_id_for(input_id)}",
80
- action: "input->input-combobox#onInput"
81
- }
82
- ) { Components::Combobox::Autocomplete.new(@template).render(options: choices, value: current_value, labelledby: Field.label_id(input_id)) }
55
+ error: error,
56
+ **kwargs
57
+ ) do |c|
58
+ c.typeahead(options: choices, value: current_value, labelledby: labelledby, url: url)
59
+ end
83
60
  end
84
61
 
85
62
  def clear_button
86
63
  Components::Button.new(@template).render(
87
- "",
64
+ icon_leading: "close",
88
65
  **merge_html_options(
89
- field_theme(:form_button_clear),
66
+ theme.resolve(:form_field_input_button_clear),
90
67
  {
91
- aria: { label: "Clear search" },
68
+ aria: { label: I18n.t("stimulus_plumbers.form.search.clear", default: "Clear search") },
92
69
  hidden: true,
93
- data: { input_search_target: "clear", action: "click->input-search#clear" }
70
+ data: { input_clearable_target: "clear", action: "click->input-clearable#clear" }
94
71
  }
95
72
  )
96
73
  )
@@ -14,38 +14,31 @@ module StimulusPlumbers
14
14
  option_key_method,
15
15
  option_value_method,
16
16
  options = {},
17
- html_options = {}
17
+ html_opts = {}
18
18
  )
19
- html_native = options.delete(:html_native) { false }
20
- Field.new(@template, **options).render(
21
- object,
19
+ html_options = merge_html_options(theme.resolve(:form_select), html_opts)
20
+ super(
22
21
  attribute,
23
- input_id: field_id(attribute)
24
- ) do |html_opts, opts, error|
25
- merged_html_opts = merge_html_options(html_options, html_opts, field_theme(:form_select, error: error))
26
- if html_native
27
- super(
28
- attribute,
29
- collection,
30
- group_method,
31
- group_label_method,
32
- option_key_method,
33
- option_value_method,
34
- opts,
35
- merged_html_opts
36
- )
37
- else
38
- render_select_dropdown(attribute, opts, merged_html_opts, err: error) do
39
- collection.map do |group|
40
- {
41
- label: group.public_send(group_label_method),
42
- options: group.public_send(group_method).map do |item|
43
- [item.public_send(option_value_method), item.public_send(option_key_method)]
44
- end
45
- }
46
- end
22
+ collection,
23
+ group_method,
24
+ group_label_method,
25
+ option_key_method,
26
+ option_value_method,
27
+ options,
28
+ html_options
29
+ )
30
+ end
31
+
32
+ private
33
+
34
+ def build_grouped_choices(collection, group_label_method, group_method, option_key_method, option_value_method)
35
+ collection.map do |group|
36
+ {
37
+ label: group.public_send(group_label_method),
38
+ options: group.public_send(group_method).map do |item|
39
+ [item.public_send(option_value_method), item.public_send(option_key_method)]
47
40
  end
48
- end
41
+ }
49
42
  end
50
43
  end
51
44
  end