simple_form 3.0.4 → 5.0.3

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 (107) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +199 -33
  3. data/MIT-LICENSE +2 -1
  4. data/README.md +453 -128
  5. data/lib/generators/simple_form/install_generator.rb +4 -3
  6. data/lib/generators/simple_form/templates/README +3 -5
  7. data/lib/generators/simple_form/templates/_form.html.erb +2 -0
  8. data/lib/generators/simple_form/templates/_form.html.haml +2 -0
  9. data/lib/generators/simple_form/templates/_form.html.slim +1 -0
  10. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +47 -16
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +418 -23
  12. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +101 -5
  13. data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +7 -2
  14. data/lib/simple_form/action_view_extensions/builder.rb +2 -0
  15. data/lib/simple_form/action_view_extensions/form_helper.rb +10 -3
  16. data/lib/simple_form/components/errors.rb +39 -6
  17. data/lib/simple_form/components/hints.rb +3 -2
  18. data/lib/simple_form/components/html5.rb +16 -5
  19. data/lib/simple_form/components/label_input.rb +21 -2
  20. data/lib/simple_form/components/labels.rb +22 -11
  21. data/lib/simple_form/components/maxlength.rb +9 -5
  22. data/lib/simple_form/components/min_max.rb +2 -1
  23. data/lib/simple_form/components/minlength.rb +38 -0
  24. data/lib/simple_form/components/pattern.rb +2 -1
  25. data/lib/simple_form/components/placeholders.rb +4 -3
  26. data/lib/simple_form/components/readonly.rb +2 -1
  27. data/lib/simple_form/components.rb +2 -0
  28. data/lib/simple_form/error_notification.rb +1 -0
  29. data/lib/simple_form/form_builder.rb +220 -89
  30. data/lib/simple_form/helpers/autofocus.rb +1 -0
  31. data/lib/simple_form/helpers/disabled.rb +1 -0
  32. data/lib/simple_form/helpers/readonly.rb +1 -0
  33. data/lib/simple_form/helpers/required.rb +1 -0
  34. data/lib/simple_form/helpers/validators.rb +2 -1
  35. data/lib/simple_form/helpers.rb +6 -5
  36. data/lib/simple_form/i18n_cache.rb +1 -0
  37. data/lib/simple_form/inputs/base.rb +62 -16
  38. data/lib/simple_form/inputs/block_input.rb +2 -1
  39. data/lib/simple_form/inputs/boolean_input.rb +40 -16
  40. data/lib/simple_form/inputs/collection_check_boxes_input.rb +3 -2
  41. data/lib/simple_form/inputs/collection_input.rb +37 -14
  42. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +9 -13
  43. data/lib/simple_form/inputs/collection_select_input.rb +5 -2
  44. data/lib/simple_form/inputs/color_input.rb +14 -0
  45. data/lib/simple_form/inputs/date_time_input.rb +24 -9
  46. data/lib/simple_form/inputs/file_input.rb +5 -2
  47. data/lib/simple_form/inputs/grouped_collection_select_input.rb +16 -3
  48. data/lib/simple_form/inputs/hidden_input.rb +5 -2
  49. data/lib/simple_form/inputs/numeric_input.rb +6 -4
  50. data/lib/simple_form/inputs/password_input.rb +6 -3
  51. data/lib/simple_form/inputs/priority_input.rb +5 -6
  52. data/lib/simple_form/inputs/range_input.rb +2 -1
  53. data/lib/simple_form/inputs/rich_text_area_input.rb +12 -0
  54. data/lib/simple_form/inputs/string_input.rb +7 -4
  55. data/lib/simple_form/inputs/text_input.rb +6 -3
  56. data/lib/simple_form/inputs.rb +3 -0
  57. data/lib/simple_form/map_type.rb +1 -0
  58. data/lib/simple_form/railtie.rb +8 -0
  59. data/lib/simple_form/tags.rb +13 -2
  60. data/lib/simple_form/version.rb +2 -1
  61. data/lib/simple_form/wrappers/builder.rb +7 -6
  62. data/lib/simple_form/wrappers/leaf.rb +29 -0
  63. data/lib/simple_form/wrappers/many.rb +7 -6
  64. data/lib/simple_form/wrappers/root.rb +10 -3
  65. data/lib/simple_form/wrappers/single.rb +7 -4
  66. data/lib/simple_form/wrappers.rb +2 -0
  67. data/lib/simple_form.rb +137 -21
  68. data/test/action_view_extensions/builder_test.rb +64 -45
  69. data/test/action_view_extensions/form_helper_test.rb +36 -16
  70. data/test/components/custom_components_test.rb +62 -0
  71. data/test/components/label_test.rb +70 -41
  72. data/test/form_builder/association_test.rb +85 -37
  73. data/test/form_builder/button_test.rb +11 -10
  74. data/test/form_builder/error_notification_test.rb +2 -1
  75. data/test/form_builder/error_test.rb +146 -33
  76. data/test/form_builder/general_test.rb +183 -81
  77. data/test/form_builder/hint_test.rb +24 -18
  78. data/test/form_builder/input_field_test.rb +105 -75
  79. data/test/form_builder/label_test.rb +68 -13
  80. data/test/form_builder/wrapper_test.rb +197 -22
  81. data/test/generators/simple_form_generator_test.rb +8 -7
  82. data/test/inputs/boolean_input_test.rb +97 -6
  83. data/test/inputs/collection_check_boxes_input_test.rb +117 -25
  84. data/test/inputs/collection_radio_buttons_input_test.rb +176 -54
  85. data/test/inputs/collection_select_input_test.rb +189 -77
  86. data/test/inputs/color_input_test.rb +10 -0
  87. data/test/inputs/datetime_input_test.rb +121 -50
  88. data/test/inputs/disabled_test.rb +29 -15
  89. data/test/inputs/discovery_test.rb +79 -6
  90. data/test/inputs/file_input_test.rb +3 -2
  91. data/test/inputs/general_test.rb +23 -22
  92. data/test/inputs/grouped_collection_select_input_test.rb +54 -17
  93. data/test/inputs/hidden_input_test.rb +5 -4
  94. data/test/inputs/numeric_input_test.rb +48 -44
  95. data/test/inputs/priority_input_test.rb +17 -16
  96. data/test/inputs/readonly_test.rb +20 -19
  97. data/test/inputs/required_test.rb +58 -13
  98. data/test/inputs/rich_text_area_input_test.rb +15 -0
  99. data/test/inputs/string_input_test.rb +58 -36
  100. data/test/inputs/text_input_test.rb +20 -7
  101. data/test/simple_form_test.rb +9 -0
  102. data/test/support/discovery_inputs.rb +40 -2
  103. data/test/support/misc_helpers.rb +113 -5
  104. data/test/support/mock_controller.rb +7 -1
  105. data/test/support/models.rb +162 -39
  106. data/test/test_helper.rb +19 -4
  107. metadata +51 -43
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  require 'simple_form/i18n_cache'
2
3
  require 'active_support/core_ext/string/output_safety'
4
+ require 'action_view/helpers'
3
5
 
4
6
  module SimpleForm
5
7
  module Inputs
6
8
  class Base
7
9
  include ERB::Util
10
+ include ActionView::Helpers::TranslationHelper
8
11
 
9
12
  extend I18nCache
10
13
 
@@ -19,6 +22,7 @@ module SimpleForm
19
22
  include SimpleForm::Components::HTML5
20
23
  include SimpleForm::Components::LabelInput
21
24
  include SimpleForm::Components::Maxlength
25
+ include SimpleForm::Components::Minlength
22
26
  include SimpleForm::Components::MinMax
23
27
  include SimpleForm::Components::Pattern
24
28
  include SimpleForm::Components::Placeholders
@@ -45,22 +49,22 @@ module SimpleForm
45
49
  end
46
50
 
47
51
  # Always enabled.
48
- enable :hint, :error
52
+ enable :hint
49
53
 
50
54
  # Usually disabled, needs to be enabled explicitly passing true as option.
51
- disable :maxlength, :placeholder, :pattern, :min_max
55
+ disable :maxlength, :minlength, :placeholder, :pattern, :min_max
52
56
 
53
57
  def initialize(builder, attribute_name, column, input_type, options = {})
54
58
  super
55
59
 
56
- options = options.dup
57
- @builder = builder
58
- @attribute_name = attribute_name
59
- @column = column
60
- @input_type = input_type
61
- @reflection = options.delete(:reflection)
62
- @options = options.reverse_merge!(self.class.default_options)
63
- @required = calculate_required
60
+ options = options.dup
61
+ @builder = builder
62
+ @attribute_name = attribute_name
63
+ @column = column
64
+ @input_type = input_type
65
+ @reflection = options.delete(:reflection)
66
+ @options = options.reverse_merge!(self.class.default_options)
67
+ @required = calculate_required
64
68
 
65
69
  # Notice that html_options_for receives a reference to input_html_classes.
66
70
  # This means that classes added dynamically to input_html_classes will
@@ -68,7 +72,10 @@ module SimpleForm
68
72
  @html_classes = SimpleForm.additional_classes_for(:input) { additional_classes }
69
73
 
70
74
  @input_html_classes = @html_classes.dup
71
- if SimpleForm.input_class && !input_html_classes.empty?
75
+
76
+ input_html_classes = self.input_html_classes
77
+
78
+ if SimpleForm.input_class && input_html_classes.any?
72
79
  input_html_classes << SimpleForm.input_class
73
80
  end
74
81
 
@@ -79,7 +86,7 @@ module SimpleForm
79
86
  end
80
87
  end
81
88
 
82
- def input
89
+ def input(wrapper_options = nil)
83
90
  raise NotImplementedError
84
91
  end
85
92
 
@@ -92,7 +99,7 @@ module SimpleForm
92
99
  end
93
100
 
94
101
  def input_class
95
- "#{lookup_model_names.join("_")}_#{reflection_or_attribute_name}"
102
+ "#{lookup_model_names.join('_')}_#{reflection_or_attribute_name}"
96
103
  end
97
104
 
98
105
  private
@@ -113,7 +120,7 @@ module SimpleForm
113
120
  end
114
121
 
115
122
  def decimal_or_float?
116
- column.number? && column.type != :integer
123
+ column.type == :float || column.type == :decimal
117
124
  end
118
125
 
119
126
  def nested_boolean_style?
@@ -167,7 +174,7 @@ module SimpleForm
167
174
  # email: 'E-mail.'
168
175
  #
169
176
  # Take a look at our locale example file.
170
- def translate(namespace, default='')
177
+ def translate_from_namespace(namespace, default = '')
171
178
  model_names = lookup_model_names.dup
172
179
  lookups = []
173
180
 
@@ -182,7 +189,46 @@ module SimpleForm
182
189
  lookups << :"defaults.#{reflection_or_attribute_name}"
183
190
  lookups << default
184
191
 
185
- I18n.t(lookups.shift, scope: :"simple_form.#{namespace}", default: lookups).presence
192
+ I18n.t(lookups.shift, scope: :"#{i18n_scope}.#{namespace}", default: lookups).presence
193
+ end
194
+
195
+ def merge_wrapper_options(options, wrapper_options)
196
+ if wrapper_options
197
+ wrapper_options = set_input_classes(wrapper_options)
198
+
199
+ wrapper_options.merge(options) do |key, oldval, newval|
200
+ case key.to_s
201
+ when "class"
202
+ Array(oldval) + Array(newval)
203
+ when "data", "aria"
204
+ oldval.merge(newval)
205
+ else
206
+ newval
207
+ end
208
+ end
209
+ else
210
+ options
211
+ end
212
+ end
213
+
214
+ def set_input_classes(wrapper_options)
215
+ wrapper_options = wrapper_options.dup
216
+ error_class = wrapper_options.delete(:error_class)
217
+ valid_class = wrapper_options.delete(:valid_class)
218
+
219
+ if error_class.present? && has_errors?
220
+ wrapper_options[:class] = "#{wrapper_options[:class]} #{error_class}"
221
+ end
222
+
223
+ if valid_class.present? && valid?
224
+ wrapper_options[:class] = "#{wrapper_options[:class]} #{valid_class}"
225
+ end
226
+
227
+ wrapper_options
228
+ end
229
+
230
+ def i18n_scope
231
+ SimpleForm.i18n_scope
186
232
  end
187
233
  end
188
234
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class BlockInput < Base
@@ -6,7 +7,7 @@ module SimpleForm
6
7
  @block = block
7
8
  end
8
9
 
9
- def input
10
+ def input(wrapper_options = nil)
10
11
  template.capture(&@block)
11
12
  end
12
13
  end
@@ -1,48 +1,58 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class BooleanInput < Base
4
- def input
5
+ def input(wrapper_options = nil)
6
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
7
+
5
8
  if nested_boolean_style?
6
9
  build_hidden_field_for_checkbox +
7
- template.label_tag(nil, class: "checkbox") {
8
- build_check_box_without_hidden_field + inline_label
10
+ template.label_tag(nil, class: boolean_label_class) {
11
+ build_check_box_without_hidden_field(merged_input_options) +
12
+ inline_label
9
13
  }
10
14
  else
11
- build_check_box
15
+ build_check_box(unchecked_value, merged_input_options)
12
16
  end
13
17
  end
14
18
 
15
- def label_input
16
- if options[:label] == false
17
- input
19
+ def label_input(wrapper_options = nil)
20
+ if options[:label] == false || inline_label?
21
+ input(wrapper_options)
18
22
  elsif nested_boolean_style?
19
23
  html_options = label_html_options.dup
20
24
  html_options[:class] ||= []
21
- html_options[:class].push(:checkbox)
25
+ html_options[:class].push(boolean_label_class) if boolean_label_class
26
+
27
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
22
28
 
23
29
  build_hidden_field_for_checkbox +
24
30
  @builder.label(label_target, html_options) {
25
- build_check_box_without_hidden_field + label_text
31
+ build_check_box_without_hidden_field(merged_input_options) + label_text
26
32
  }
27
33
  else
28
- input + label
34
+ input(wrapper_options) + label(wrapper_options)
29
35
  end
30
36
  end
31
37
 
32
38
  private
33
39
 
40
+ def boolean_label_class
41
+ options[:boolean_label_class] || SimpleForm.boolean_label_class
42
+ end
43
+
34
44
  # Build a checkbox tag using default unchecked value. This allows us to
35
45
  # reuse the method for nested boolean style, but with no unchecked value,
36
46
  # which won't generate the hidden checkbox. This is the default functionality
37
47
  # in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
38
- def build_check_box(unchecked_value = unchecked_value())
39
- @builder.check_box(attribute_name, input_html_options, checked_value, unchecked_value)
48
+ def build_check_box(unchecked_value, options)
49
+ @builder.check_box(attribute_name, options, checked_value, unchecked_value)
40
50
  end
41
51
 
42
52
  # Build a checkbox without generating the hidden field. See
43
53
  # #build_hidden_field_for_checkbox for more info.
44
- def build_check_box_without_hidden_field
45
- build_check_box(nil)
54
+ def build_check_box_without_hidden_field(options)
55
+ build_check_box(nil, options)
46
56
  end
47
57
 
48
58
  # Create a hidden field for the current checkbox, so we can simulate Rails
@@ -50,15 +60,25 @@ module SimpleForm
50
60
  # we need the hidden field to be *outside* the label (otherwise it
51
61
  # generates invalid html - html5 only).
52
62
  def build_hidden_field_for_checkbox
63
+ return "" if !include_hidden? || !unchecked_value
53
64
  options = { value: unchecked_value, id: nil, disabled: input_html_options[:disabled] }
54
- options[:name] = input_html_options[:name] if input_html_options.has_key?(:name)
65
+ options[:name] = input_html_options[:name] if input_html_options.key?(:name)
66
+ options[:form] = input_html_options[:form] if input_html_options.key?(:form)
55
67
 
56
68
  @builder.hidden_field(attribute_name, options)
57
69
  end
58
70
 
71
+ def inline_label?
72
+ nested_boolean_style? && options[:inline_label]
73
+ end
74
+
59
75
  def inline_label
60
76
  inline_option = options[:inline_label]
61
- inline_option == true ? label_text : inline_option
77
+
78
+ if inline_option
79
+ label = inline_option == true ? label_text : html_escape(inline_option)
80
+ " #{label}".html_safe
81
+ end
62
82
  end
63
83
 
64
84
  # Booleans are not required by default because in most of the cases
@@ -68,6 +88,10 @@ module SimpleForm
68
88
  false
69
89
  end
70
90
 
91
+ def include_hidden?
92
+ options.fetch(:include_hidden, true)
93
+ end
94
+
71
95
  def checked_value
72
96
  options.fetch(:checked_value, '1')
73
97
  end
@@ -1,16 +1,17 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class CollectionCheckBoxesInput < CollectionRadioButtonsInput
4
5
  protected
5
6
 
6
7
  # Checkbox components do not use the required html tag.
7
- # More info: https://github.com/plataformatec/simple_form/issues/340#issuecomment-2871956
8
+ # More info: https://github.com/heartcombo/simple_form/issues/340#issuecomment-2871956
8
9
  def has_required?
9
10
  false
10
11
  end
11
12
 
12
13
  def build_nested_boolean_style_item_tag(collection_builder)
13
- collection_builder.check_box + collection_builder.text
14
+ collection_builder.check_box + collection_builder.text.to_s
14
15
  end
15
16
 
16
17
  def item_wrapper_class
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class CollectionInput < Base
5
+ BASIC_OBJECT_CLASSES = [String, Integer, Float, NilClass, Symbol, TrueClass, FalseClass]
6
+ BASIC_OBJECT_CLASSES.push(Fixnum, Bignum) unless 1.class == Integer
7
+
4
8
  # Default boolean collection for use with selects/radios when no
5
9
  # collection is given. Always fallback to this boolean collection.
6
10
  # Texts can be translated using i18n in "simple_form.yes" and
@@ -12,14 +16,18 @@ module SimpleForm
12
16
  end
13
17
  end
14
18
 
15
- def input
19
+ def input(wrapper_options = nil)
16
20
  raise NotImplementedError,
17
21
  "input should be implemented by classes inheriting from CollectionInput"
18
22
  end
19
23
 
20
24
  def input_options
21
25
  options = super
26
+
22
27
  options[:include_blank] = true unless skip_include_blank?
28
+ translate_option options, :prompt
29
+ translate_option options, :include_blank
30
+
23
31
  options
24
32
  end
25
33
 
@@ -33,12 +41,12 @@ module SimpleForm
33
41
  end
34
42
 
35
43
  def has_required?
36
- super && (input_options[:include_blank] || multiple?)
44
+ super && (input_options[:include_blank] || input_options[:prompt].present? || multiple?)
37
45
  end
38
46
 
39
47
  # Check if :include_blank must be included by default.
40
48
  def skip_include_blank?
41
- (options.keys & [:prompt, :include_blank, :default, :selected]).any? || multiple?
49
+ (options.keys & %i[prompt include_blank default selected]).any? || multiple?
42
50
  end
43
51
 
44
52
  def multiple?
@@ -70,32 +78,47 @@ module SimpleForm
70
78
  elsif collection_includes_basic_objects?(collection_classes)
71
79
  { label: :to_s, value: :to_s }
72
80
  else
73
- sample = collection.first || collection.last
74
-
75
- { label: SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
76
- value: SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
81
+ detect_method_from_class(collection_classes)
77
82
  end
78
83
  end
79
84
 
85
+ def detect_method_from_class(collection_classes)
86
+ sample = collection.first || collection.last
87
+
88
+ { label: SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
89
+ value: SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
90
+ end
91
+
80
92
  def detect_collection_classes(some_collection = collection)
81
- some_collection.map { |e| e.class }.uniq
93
+ some_collection.map(&:class).uniq
82
94
  end
83
95
 
84
96
  def collection_includes_basic_objects?(collection_classes)
85
- (collection_classes & [
86
- String, Integer, Fixnum, Bignum, Float, NilClass, Symbol, TrueClass, FalseClass
87
- ]).any?
97
+ (collection_classes & BASIC_OBJECT_CLASSES).any?
88
98
  end
89
99
 
90
100
  def translate_collection
91
- if translated_collection = translate(:options)
101
+ if translated_collection = translate_from_namespace(:options)
92
102
  @collection = collection.map do |key|
93
- [translated_collection[key] || key, key]
103
+ html_key = "#{key}_html".to_sym
104
+
105
+ if translated_collection[html_key]
106
+ [translated_collection[html_key].html_safe || key, key.to_s]
107
+ else
108
+ [translated_collection[key] || key, key.to_s]
109
+ end
94
110
  end
95
111
  true
96
112
  end
97
113
  end
114
+
115
+ def translate_option(options, key)
116
+ if options[key] == :translate
117
+ namespace = key.to_s.pluralize
118
+
119
+ options[key] = translate_from_namespace(namespace, true)
120
+ end
121
+ end
98
122
  end
99
123
  end
100
124
  end
101
-
@@ -1,19 +1,22 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class CollectionRadioButtonsInput < CollectionInput
4
- def input
5
+ def input(wrapper_options = nil)
5
6
  label_method, value_method = detect_collection_methods
6
7
 
7
- @builder.send("collection_#{input_type}",
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
10
+ @builder.send(:"collection_#{input_type}",
8
11
  attribute_name, collection, value_method, label_method,
9
- input_options, input_html_options, &collection_block_for_nested_boolean_style
12
+ input_options, merged_input_options,
13
+ &collection_block_for_nested_boolean_style
10
14
  )
11
15
  end
12
16
 
13
17
  def input_options
14
18
  options = super
15
19
  apply_default_collection_options!(options)
16
- apply_nested_boolean_collection_options!(options) if nested_boolean_style?
17
20
  options
18
21
  end
19
22
 
@@ -23,7 +26,7 @@ module SimpleForm
23
26
  options[:item_wrapper_tag] ||= options.fetch(:item_wrapper_tag, SimpleForm.item_wrapper_tag)
24
27
  options[:item_wrapper_class] = [
25
28
  item_wrapper_class, options[:item_wrapper_class], SimpleForm.item_wrapper_class
26
- ].compact.presence
29
+ ].compact.presence if SimpleForm.include_default_input_wrapper_class
27
30
 
28
31
  options[:collection_wrapper_tag] ||= options.fetch(:collection_wrapper_tag, SimpleForm.collection_wrapper_tag)
29
32
  options[:collection_wrapper_class] = [
@@ -31,13 +34,6 @@ module SimpleForm
31
34
  ].compact.presence
32
35
  end
33
36
 
34
- # Force item wrapper to be a label when using nested boolean, to support
35
- # configuring classes through :item_wrapper_class, and to maintain
36
- # compatibility with :inline style and default :item_wrapper_tag.
37
- def apply_nested_boolean_collection_options!(options)
38
- options[:item_wrapper_tag] = :label
39
- end
40
-
41
37
  def collection_block_for_nested_boolean_style
42
38
  return unless nested_boolean_style?
43
39
 
@@ -45,7 +41,7 @@ module SimpleForm
45
41
  end
46
42
 
47
43
  def build_nested_boolean_style_item_tag(collection_builder)
48
- collection_builder.radio_button + collection_builder.text
44
+ collection_builder.radio_button + collection_builder.text.to_s
49
45
  end
50
46
 
51
47
  def item_wrapper_class
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class CollectionSelectInput < CollectionInput
4
- def input
5
+ def input(wrapper_options = nil)
5
6
  label_method, value_method = detect_collection_methods
6
7
 
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
7
10
  @builder.collection_select(
8
11
  attribute_name, collection, value_method, label_method,
9
- input_options, input_html_options
12
+ input_options, merged_input_options
10
13
  )
11
14
  end
12
15
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module SimpleForm
3
+ module Inputs
4
+ class ColorInput < Base
5
+ def input(wrapper_options = nil)
6
+ input_html_options[:type] ||= "color" if html5?
7
+
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
10
+ @builder.text_field(attribute_name, merged_input_options)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,23 +1,38 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class DateTimeInput < Base
4
- def input
5
- @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options)
5
+ def input(wrapper_options = nil)
6
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
7
+
8
+ if use_html5_inputs?
9
+ @builder.send(:"#{input_type}_field", attribute_name, merged_input_options)
10
+ else
11
+ @builder.send(:"#{input_type}_select", attribute_name, input_options, merged_input_options)
12
+ end
6
13
  end
7
14
 
8
15
  private
9
16
 
10
17
  def label_target
11
- position = case input_type
12
- when :date, :datetime
13
- date_order = input_options[:order] || I18n.t('date.order')
14
- date_order.first.to_sym
18
+ if use_html5_inputs?
19
+ attribute_name
15
20
  else
16
- :hour
21
+ position = case input_type
22
+ when :date, :datetime
23
+ date_order = input_options[:order] || I18n.t('date.order')
24
+ date_order.first.to_sym
25
+ else
26
+ :hour
27
+ end
28
+
29
+ position = ActionView::Helpers::DateTimeSelector::POSITION[position]
30
+ "#{attribute_name}_#{position}i"
17
31
  end
32
+ end
18
33
 
19
- position = ActionView::Helpers::DateTimeSelector::POSITION[position]
20
- "#{attribute_name}_#{position}i"
34
+ def use_html5_inputs?
35
+ input_options[:html5]
21
36
  end
22
37
  end
23
38
  end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class FileInput < Base
4
- def input
5
- @builder.file_field(attribute_name, input_html_options)
5
+ def input(wrapper_options = nil)
6
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
7
+
8
+ @builder.file_field(attribute_name, merged_input_options)
6
9
  end
7
10
  end
8
11
  end
@@ -1,11 +1,15 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class GroupedCollectionSelectInput < CollectionInput
4
- def input
5
+ def input(wrapper_options = nil)
5
6
  label_method, value_method = detect_collection_methods
7
+
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
6
10
  @builder.grouped_collection_select(attribute_name, grouped_collection,
7
11
  group_method, group_label_method, value_method, label_method,
8
- input_options, input_html_options)
12
+ input_options, merged_input_options)
9
13
  end
10
14
 
11
15
  private
@@ -19,7 +23,7 @@ module SimpleForm
19
23
 
20
24
  # Sample collection
21
25
  def collection
22
- @collection ||= grouped_collection.first.try(:send, group_method) || []
26
+ @collection ||= grouped_collection.map { |collection| collection.try(:send, group_method) }.detect(&:present?) || []
23
27
  end
24
28
 
25
29
  def group_method
@@ -36,6 +40,15 @@ module SimpleForm
36
40
 
37
41
  label
38
42
  end
43
+
44
+ def detect_method_from_class(collection_classes)
45
+ return {} if collection_classes.empty?
46
+
47
+ sample = collection_classes.first
48
+
49
+ { label: SimpleForm.collection_label_methods.find { |m| sample.instance_methods.include?(m) },
50
+ value: SimpleForm.collection_value_methods.find { |m| sample.instance_methods.include?(m) } }
51
+ end
39
52
  end
40
53
  end
41
54
  end
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class HiddenInput < Base
4
5
  disable :label, :errors, :hint, :required
5
6
 
6
- def input
7
- @builder.hidden_field(attribute_name, input_html_options)
7
+ def input(wrapper_options = nil)
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
10
+ @builder.hidden_field(attribute_name, merged_input_options)
8
11
  end
9
12
 
10
13
  private
@@ -1,18 +1,20 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class NumericInput < Base
4
5
  enable :placeholder, :min_max
5
6
 
6
- def input
7
+ def input(wrapper_options = nil)
7
8
  input_html_classes.unshift("numeric")
8
9
  if html5?
9
10
  input_html_options[:type] ||= "number"
10
11
  input_html_options[:step] ||= integer? ? 1 : "any"
11
12
  end
12
- @builder.text_field(attribute_name, input_html_options)
13
- end
14
13
 
15
- private
14
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
15
+
16
+ @builder.text_field(attribute_name, merged_input_options)
17
+ end
16
18
  end
17
19
  end
18
20
  end
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class PasswordInput < Base
4
- enable :placeholder, :maxlength
5
+ enable :placeholder, :maxlength, :minlength
5
6
 
6
- def input
7
- @builder.password_field(attribute_name, input_html_options)
7
+ def input(wrapper_options = nil)
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
10
+ @builder.password_field(attribute_name, merged_input_options)
8
11
  end
9
12
  end
10
13
  end