stimulus_plumbers 0.2.9 → 0.3.1

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/README.md +5 -4
  4. data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.es.js +246 -269
  5. data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.umd.js +1 -1
  6. data/lib/stimulus_plumbers/components/action_list/section.rb +6 -5
  7. data/lib/stimulus_plumbers/components/action_list.rb +3 -3
  8. data/lib/stimulus_plumbers/components/avatar.rb +4 -3
  9. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_month.rb +62 -41
  10. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_week.rb +13 -10
  11. data/lib/stimulus_plumbers/components/calendar.rb +36 -13
  12. data/lib/stimulus_plumbers/components/card/section.rb +3 -3
  13. data/lib/stimulus_plumbers/components/card.rb +3 -3
  14. data/lib/stimulus_plumbers/components/combobox/autocomplete.rb +8 -14
  15. data/lib/stimulus_plumbers/components/combobox/date.rb +6 -2
  16. data/lib/stimulus_plumbers/components/combobox/dropdown.rb +6 -2
  17. data/lib/stimulus_plumbers/components/combobox/popover.rb +9 -5
  18. data/lib/stimulus_plumbers/components/combobox/time.rb +1 -1
  19. data/lib/stimulus_plumbers/components/combobox/trigger.rb +19 -10
  20. data/lib/stimulus_plumbers/components/combobox.rb +33 -23
  21. data/lib/stimulus_plumbers/components/date_picker/navigation.rb +14 -22
  22. data/lib/stimulus_plumbers/components/divider.rb +16 -0
  23. data/lib/stimulus_plumbers/components/icon.rb +14 -20
  24. data/lib/stimulus_plumbers/components/popover/builder.rb +2 -2
  25. data/lib/stimulus_plumbers/components/popover.rb +11 -6
  26. data/lib/stimulus_plumbers/configuration.rb +3 -18
  27. data/lib/stimulus_plumbers/engine.rb +2 -2
  28. data/lib/stimulus_plumbers/form/builder.rb +40 -44
  29. data/lib/stimulus_plumbers/form/field.rb +96 -45
  30. data/lib/stimulus_plumbers/form/fields/error.rb +2 -2
  31. data/lib/stimulus_plumbers/form/fields/fieldset.rb +54 -0
  32. data/lib/stimulus_plumbers/form/fields/group.rb +2 -2
  33. data/lib/stimulus_plumbers/form/fields/hint.rb +2 -2
  34. data/lib/stimulus_plumbers/form/fields/input_group.rb +25 -0
  35. data/lib/stimulus_plumbers/form/fields/inputs/choice.rb +69 -0
  36. data/lib/stimulus_plumbers/form/fields/inputs/datetime.rb +81 -0
  37. data/lib/stimulus_plumbers/form/fields/inputs/file.rb +22 -0
  38. data/lib/stimulus_plumbers/form/fields/inputs/password.rb +59 -0
  39. data/lib/stimulus_plumbers/form/fields/inputs/search.rb +102 -0
  40. data/lib/stimulus_plumbers/form/fields/inputs/select/grouped.rb +56 -0
  41. data/lib/stimulus_plumbers/form/fields/inputs/select/timezone.rb +59 -0
  42. data/lib/stimulus_plumbers/form/fields/inputs/select/weekday.rb +45 -0
  43. data/lib/stimulus_plumbers/form/fields/inputs/select.rb +91 -0
  44. data/lib/stimulus_plumbers/form/fields/inputs/submit.rb +25 -0
  45. data/lib/stimulus_plumbers/form/fields/inputs/text.rb +37 -0
  46. data/lib/stimulus_plumbers/form/fields/inputs/text_area.rb +22 -0
  47. data/lib/stimulus_plumbers/form/fields/label.rb +13 -9
  48. data/lib/stimulus_plumbers/helpers/calendar_helper.rb +1 -1
  49. data/lib/stimulus_plumbers/helpers/combobox_helper.rb +29 -36
  50. data/lib/stimulus_plumbers/helpers/divider_helper.rb +11 -0
  51. data/lib/stimulus_plumbers/helpers.rb +2 -0
  52. data/lib/stimulus_plumbers/plumber/base.rb +1 -1
  53. data/lib/stimulus_plumbers/plumber/dispatcher/callable_inspector.rb +19 -0
  54. data/lib/stimulus_plumbers/plumber/dispatcher/instance_exec.rb +35 -0
  55. data/lib/stimulus_plumbers/plumber/dispatcher/klass_proxy.rb +34 -0
  56. data/lib/stimulus_plumbers/plumber/dispatcher/method_call.rb +36 -0
  57. data/lib/stimulus_plumbers/plumber/dispatcher.rb +4 -87
  58. data/lib/stimulus_plumbers/plumber/html_options.rb +6 -5
  59. data/lib/stimulus_plumbers/plumber/renderer.rb +2 -2
  60. data/lib/stimulus_plumbers/themes/base.rb +26 -5
  61. data/lib/stimulus_plumbers/themes/configuration.rb +38 -0
  62. data/lib/stimulus_plumbers/themes/schema/form/ranges.rb +14 -0
  63. data/lib/stimulus_plumbers/themes/schema/icon.rb +32 -0
  64. data/lib/stimulus_plumbers/themes/schema/ranges.rb +1 -1
  65. data/lib/stimulus_plumbers/themes/schema.rb +17 -5
  66. data/lib/stimulus_plumbers/version.rb +1 -1
  67. data/lib/stimulus_plumbers.rb +4 -2
  68. metadata +25 -21
  69. data/lib/stimulus_plumbers/form/fields/choice.rb +0 -25
  70. data/lib/stimulus_plumbers/form/fields/combobox.rb +0 -41
  71. data/lib/stimulus_plumbers/form/fields/file.rb +0 -16
  72. data/lib/stimulus_plumbers/form/fields/password.rb +0 -55
  73. data/lib/stimulus_plumbers/form/fields/renderer.rb +0 -52
  74. data/lib/stimulus_plumbers/form/fields/search.rb +0 -54
  75. data/lib/stimulus_plumbers/form/fields/select.rb +0 -33
  76. data/lib/stimulus_plumbers/form/fields/submit.rb +0 -23
  77. data/lib/stimulus_plumbers/form/fields/text.rb +0 -33
  78. data/lib/stimulus_plumbers/form/fields/text_area.rb +0 -16
  79. data/lib/stimulus_plumbers/themes/tailwind/action_list.rb +0 -33
  80. data/lib/stimulus_plumbers/themes/tailwind/avatar.rb +0 -52
  81. data/lib/stimulus_plumbers/themes/tailwind/button.rb +0 -89
  82. data/lib/stimulus_plumbers/themes/tailwind/calendar.rb +0 -80
  83. data/lib/stimulus_plumbers/themes/tailwind/card.rb +0 -24
  84. data/lib/stimulus_plumbers/themes/tailwind/combobox.rb +0 -75
  85. data/lib/stimulus_plumbers/themes/tailwind/form.rb +0 -108
  86. data/lib/stimulus_plumbers/themes/tailwind/layout.rb +0 -25
  87. data/lib/stimulus_plumbers/themes/tailwind_theme.rb +0 -31
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ class Fieldset < Plumber::Base
7
+ def render(object, attribute, input_id, field, &block)
8
+ error = field.error?(object, attribute)
9
+ fieldset_opts = build_fieldset_aria(field, object, attribute, input_id, error)
10
+ Group.new(template).render(layout: field.layout, error: error) do
11
+ template.safe_join(
12
+ [
13
+ build_fieldset(fieldset_opts, field, attribute, error, &block),
14
+ field.render_hint(input_id),
15
+ field.render_errors(object, attribute, input_id)
16
+ ]
17
+ )
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def build_fieldset(fieldset_opts, field, attribute, error, &block)
24
+ template.content_tag(:fieldset, **fieldset_opts) do
25
+ template.safe_join(
26
+ [
27
+ legend(field, attribute),
28
+ template.capture(error, &block)
29
+ ]
30
+ )
31
+ end
32
+ end
33
+
34
+ def legend(field, attribute)
35
+ Label.new(template).render(
36
+ text: field.label || attribute.to_s.humanize,
37
+ required: field.required,
38
+ hidden: field.label_hidden?,
39
+ tag: :legend
40
+ )
41
+ end
42
+
43
+ def build_fieldset_aria(field, object, attribute, input_id, error)
44
+ opts = {}
45
+ db = field.described_by(object, attribute, input_id)
46
+ opts[:"aria-describedby"] = db if db
47
+ opts[:"aria-invalid"] = "true" if error
48
+ opts[:"aria-required"] = "true" if field.required
49
+ opts
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -5,8 +5,8 @@ module StimulusPlumbers
5
5
  module Fields
6
6
  class Group < Plumber::Base
7
7
  def render(layout: :stacked, error: false, &block)
8
- klass = theme.resolve(:form_group, layout: layout, error: error).fetch(:classes, "")
9
- template.content_tag(:div, class: klass.presence, &block)
8
+ html_options = merge_html_options(theme.resolve(:form_group, layout: layout, error: error))
9
+ template.content_tag(:div, **html_options, &block)
10
10
  end
11
11
  end
12
12
  end
@@ -5,8 +5,8 @@ module StimulusPlumbers
5
5
  module Fields
6
6
  class Hint < Plumber::Base
7
7
  def render(text:, id:)
8
- klass = theme.resolve(:form_details).fetch(:classes, "")
9
- template.content_tag(:p, text, id: id, class: klass.presence)
8
+ html_options = merge_html_options(theme.resolve(:form_details))
9
+ template.content_tag(:p, text, id: id, **html_options)
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ class InputGroup < Plumber::Base
7
+ def render(leading: nil, trailing: nil, error: false, **wrapper_opts, &block)
8
+ html_options = merge_html_options(
9
+ theme.resolve(:form_input_group, error: error),
10
+ wrapper_opts
11
+ )
12
+ template.content_tag(:div, **html_options) do
13
+ template.safe_join(
14
+ [
15
+ leading.respond_to?(:call) ? leading.call : leading,
16
+ template.capture(&block),
17
+ trailing.respond_to?(:call) ? trailing.call : trailing
18
+ ]
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module Choice
8
+ def check_box(attribute, options = {}, checked_value = "1", unchecked_value = "0")
9
+ options[:layout] ||= :inline
10
+ Field.new(@template, **options).render(
11
+ object,
12
+ attribute,
13
+ input_id: field_id(attribute)
14
+ ) do |html_opts, opts, error|
15
+ html_options = merge_html_options(opts, html_opts, field_theme(:form_checkbox, error: error))
16
+ super(attribute, html_options, checked_value, unchecked_value)
17
+ end
18
+ end
19
+
20
+ def collection_check_boxes(
21
+ attribute,
22
+ collection,
23
+ value_method,
24
+ text_method,
25
+ options = {},
26
+ html_options = {},
27
+ &block
28
+ )
29
+ options[:layout] ||= :inline
30
+ field = Field.new(@template, **options)
31
+ render_fieldset(attribute, field) do |error|
32
+ item_opts = merge_html_options(html_options, field_theme(:form_checkbox, error: error))
33
+ super(attribute, collection, value_method, text_method, {}, item_opts, &block)
34
+ end
35
+ end
36
+
37
+ def radio_button(attribute, tag_value, options = {})
38
+ options[:layout] ||= :inline
39
+ Field.new(@template, **options).render(
40
+ object,
41
+ attribute,
42
+ input_id: field_id(attribute, tag_value)
43
+ ) do |html_opts, opts, error|
44
+ html_options = merge_html_options(opts, html_opts, field_theme(:form_radio, error: error))
45
+ super(attribute, tag_value, html_options)
46
+ end
47
+ end
48
+
49
+ def collection_radio_buttons(
50
+ attribute,
51
+ collection,
52
+ value_method,
53
+ text_method,
54
+ options = {},
55
+ html_options = {},
56
+ &block
57
+ )
58
+ options[:layout] ||= :inline
59
+ field = Field.new(@template, **options)
60
+ render_fieldset(attribute, field) do |error|
61
+ item_opts = merge_html_options(html_options, field_theme(:form_radio, error: error))
62
+ super(attribute, collection, value_method, text_method, {}, item_opts, &block)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module Datetime
8
+ 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
22
+ end
23
+
24
+ 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
40
+ end
41
+
42
+ private
43
+
44
+ def render_date_combobox(attribute, html_opts, error)
45
+ 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
+ )
51
+ render_combobox(
52
+ attribute,
53
+ 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)
59
+ end
60
+ end
61
+
62
+ def render_time_combobox(attribute, html_opts, error, format:, step:)
63
+ current_value = object.respond_to?(attribute) ? object.public_send(attribute) : nil
64
+ opts = Components::Combobox::Time.default_opts.deep_merge(
65
+ input: { value: current_value },
66
+ trigger: { aria: html_opts[:aria] },
67
+ popover: { labelledby: Field.label_id(html_opts[:id]) }
68
+ )
69
+ render_combobox(
70
+ attribute,
71
+ 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) }
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module File
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
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module Password
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)
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
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
+ )
34
+ render_input_group(
35
+ error: error,
36
+ trailing: method(:reveal_button),
37
+ **merge_html_options(
38
+ field_theme(:form_input_reveal, error: error),
39
+ { data: { controller: "input-format", input_format_type_value: "password" } }
40
+ )
41
+ ) { @template.capture(html_options, &block) }
42
+ end
43
+
44
+ def reveal_button
45
+ html_options = merge_html_options(
46
+ field_theme(:form_button_reveal),
47
+ {
48
+ type: "button",
49
+ aria: { label: "Show password", pressed: "false" },
50
+ data: { input_format_target: "toggle", action: "click->input-format#toggle" }
51
+ }
52
+ )
53
+ @template.content_tag(:button, "", **html_options)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ 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)
43
+
44
+ return input_html unless clearable
45
+
46
+ render_input_group(
47
+ trailing: method(:clear_button),
48
+ error: !!error,
49
+ **merge_html_options(field_theme(:form_input_search), { data: { controller: "input-search" } })
50
+ ) { input_html }
51
+ end
52
+
53
+ def render_search_combobox(attribute, html_opts, error, url:, clearable:, &block)
54
+ 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 } : {} }
60
+ )
61
+
62
+ combobox_html = @template.capture(opts, input_id, current_value, &block)
63
+ return combobox_html unless clearable
64
+
65
+ render_input_group(
66
+ trailing: method(:clear_button),
67
+ error: !!error,
68
+ **merge_html_options(field_theme(:form_input_search), { data: { controller: "input-search" } })
69
+ ) { combobox_html }
70
+ end
71
+
72
+ def render_search_autocomplete(attribute, input_id, combobox_opts, error, choices, current_value)
73
+ render_combobox(
74
+ attribute,
75
+ input_id: input_id,
76
+ 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)) }
83
+ end
84
+
85
+ def clear_button
86
+ Components::Button.new(@template).render(
87
+ "",
88
+ **merge_html_options(
89
+ field_theme(:form_button_clear),
90
+ {
91
+ aria: { label: "Clear search" },
92
+ hidden: true,
93
+ data: { input_search_target: "clear", action: "click->input-search#clear" }
94
+ }
95
+ )
96
+ )
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module Select
8
+ module Grouped
9
+ def grouped_collection_select(
10
+ attribute,
11
+ collection,
12
+ group_method,
13
+ group_label_method,
14
+ option_key_method,
15
+ option_value_method,
16
+ options = {},
17
+ html_options = {}
18
+ )
19
+ html_native = options.delete(:html_native) { false }
20
+ Field.new(@template, **options).render(
21
+ object,
22
+ 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
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module Select
8
+ module Timezone
9
+ def time_zone_select(attribute, priority_zones = nil, options = {}, html_options = {})
10
+ html_native = options.delete(:html_native) { false }
11
+ Field.new(@template, **options).render(object, attribute, input_id: field_id(attribute)) do |html_opts, opts, error|
12
+ merged_html_opts = merge_html_options(html_options, html_opts, field_theme(:form_select, error: error))
13
+ if html_native
14
+ super(attribute, priority_zones, opts, merged_html_opts)
15
+ else
16
+ render_select_dropdown(attribute, opts, merged_html_opts, err: error) do
17
+ model = opts.delete(:model) { ActiveSupport::TimeZone }
18
+ build_zone_choices(priority_zones, model.all)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def build_zone_choices(priority_zones, all_zones)
27
+ return zone_options(all_zones) unless priority_zones
28
+
29
+ priority = filter_priority_zones(priority_zones, all_zones)
30
+ priority_names = priority.to_set(&:name)
31
+ remaining = all_zones.reject { |z| priority_names.include?(z.name) }
32
+ [
33
+ {
34
+ label: I18n.t("helpers.time_zone_select.priority_zones", default: "Suggested"),
35
+ options: zone_options(priority)
36
+ },
37
+ {
38
+ label: I18n.t("helpers.time_zone_select.other_zones", default: "Other"),
39
+ options: zone_options(remaining)
40
+ }
41
+ ]
42
+ end
43
+
44
+ def filter_priority_zones(priority_zones, all_zones)
45
+ case priority_zones
46
+ when Regexp then all_zones.select { |z| z.name.match?(priority_zones) }
47
+ else Array(priority_zones)
48
+ end
49
+ end
50
+
51
+ def zone_options(zones)
52
+ zones.map { |z| [z.to_s, z.name] }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Form
5
+ module Fields
6
+ module Inputs
7
+ module Select
8
+ module Weekday
9
+ if ActionView.version >= Gem::Version.new("7.1")
10
+ def weekday_select(attribute, options = {}, html_options = {})
11
+ html_native = options.delete(:html_native) { false }
12
+ Field.new(@template, **options).render(
13
+ object,
14
+ attribute,
15
+ input_id: field_id(attribute)
16
+ ) do |html_opts, opts, error|
17
+ merged_html_opts = merge_html_options(html_options, html_opts, field_theme(:form_select, error: error))
18
+ if html_native
19
+ super(attribute, opts, merged_html_opts)
20
+ else
21
+ render_select_dropdown(attribute, opts, merged_html_opts, err: error) do
22
+ build_weekday_choices(opts)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def build_weekday_choices(opts)
32
+ index_as_value = opts.delete(:index_as_value) { false }
33
+ day_format = opts.delete(:day_format) { :day_names }
34
+ beginning_of_week = opts.delete(:beginning_of_week) { Date.beginning_of_week }
35
+
36
+ day_names = I18n.t("date.#{day_format}")
37
+ choices = day_names.each_with_index.map { |name, i| index_as_value ? [name, i] : [name, name] }
38
+ choices.rotate(Date::DAYS_INTO_WEEK.fetch(beginning_of_week))
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end