simple_form_awesome 2.2.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 (103) hide show
  1. data/CHANGELOG.md +327 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +25 -0
  4. data/lib/generators/simple_form/USAGE +3 -0
  5. data/lib/generators/simple_form/install_generator.rb +48 -0
  6. data/lib/generators/simple_form/templates/AUI_README +19 -0
  7. data/lib/generators/simple_form/templates/README +12 -0
  8. data/lib/generators/simple_form/templates/_form.html.erb +13 -0
  9. data/lib/generators/simple_form/templates/_form.html.haml +10 -0
  10. data/lib/generators/simple_form/templates/_form.html.slim +10 -0
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +142 -0
  12. data/lib/generators/simple_form/templates/config/initializers/simple_form_aui.rb +21 -0
  13. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +45 -0
  14. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +26 -0
  15. data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +26 -0
  16. data/lib/simple_form/action_view_extensions/builder.rb +340 -0
  17. data/lib/simple_form/action_view_extensions/form_helper.rb +72 -0
  18. data/lib/simple_form/components/errors.rb +35 -0
  19. data/lib/simple_form/components/hints.rb +18 -0
  20. data/lib/simple_form/components/html5.rb +26 -0
  21. data/lib/simple_form/components/label_input.rb +15 -0
  22. data/lib/simple_form/components/labels.rb +79 -0
  23. data/lib/simple_form/components/maxlength.rb +41 -0
  24. data/lib/simple_form/components/min_max.rb +50 -0
  25. data/lib/simple_form/components/pattern.rb +34 -0
  26. data/lib/simple_form/components/placeholders.rb +16 -0
  27. data/lib/simple_form/components/readonly.rb +22 -0
  28. data/lib/simple_form/components.rb +20 -0
  29. data/lib/simple_form/core_ext/hash.rb +16 -0
  30. data/lib/simple_form/error_notification.rb +48 -0
  31. data/lib/simple_form/form_builder.rb +482 -0
  32. data/lib/simple_form/helpers/autofocus.rb +11 -0
  33. data/lib/simple_form/helpers/disabled.rb +15 -0
  34. data/lib/simple_form/helpers/readonly.rb +15 -0
  35. data/lib/simple_form/helpers/required.rb +35 -0
  36. data/lib/simple_form/helpers/validators.rb +44 -0
  37. data/lib/simple_form/helpers.rb +12 -0
  38. data/lib/simple_form/i18n_cache.rb +22 -0
  39. data/lib/simple_form/inputs/aui_string_input.rb +10 -0
  40. data/lib/simple_form/inputs/base.rb +184 -0
  41. data/lib/simple_form/inputs/block_input.rb +14 -0
  42. data/lib/simple_form/inputs/boolean_input.rb +78 -0
  43. data/lib/simple_form/inputs/collection_check_boxes_input.rb +21 -0
  44. data/lib/simple_form/inputs/collection_input.rb +101 -0
  45. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +63 -0
  46. data/lib/simple_form/inputs/collection_select_input.rb +14 -0
  47. data/lib/simple_form/inputs/date_time_input.rb +28 -0
  48. data/lib/simple_form/inputs/file_input.rb +9 -0
  49. data/lib/simple_form/inputs/grouped_collection_select_input.rb +41 -0
  50. data/lib/simple_form/inputs/hidden_input.rb +17 -0
  51. data/lib/simple_form/inputs/numeric_input.rb +24 -0
  52. data/lib/simple_form/inputs/password_input.rb +12 -0
  53. data/lib/simple_form/inputs/priority_input.rb +24 -0
  54. data/lib/simple_form/inputs/range_input.rb +14 -0
  55. data/lib/simple_form/inputs/string_input.rb +23 -0
  56. data/lib/simple_form/inputs/text_area_input.rb +12 -0
  57. data/lib/simple_form/inputs/text_input.rb +11 -0
  58. data/lib/simple_form/inputs.rb +23 -0
  59. data/lib/simple_form/map_type.rb +16 -0
  60. data/lib/simple_form/version.rb +3 -0
  61. data/lib/simple_form/wrappers/builder.rb +103 -0
  62. data/lib/simple_form/wrappers/many.rb +73 -0
  63. data/lib/simple_form/wrappers/root.rb +36 -0
  64. data/lib/simple_form/wrappers/single.rb +24 -0
  65. data/lib/simple_form/wrappers.rb +8 -0
  66. data/lib/simple_form.rb +221 -0
  67. data/test/action_view_extensions/builder_test.rb +583 -0
  68. data/test/action_view_extensions/form_helper_test.rb +143 -0
  69. data/test/components/label_test.rb +327 -0
  70. data/test/form_builder/association_test.rb +186 -0
  71. data/test/form_builder/button_test.rb +47 -0
  72. data/test/form_builder/error_notification_test.rb +79 -0
  73. data/test/form_builder/error_test.rb +121 -0
  74. data/test/form_builder/general_test.rb +402 -0
  75. data/test/form_builder/hint_test.rb +138 -0
  76. data/test/form_builder/input_field_test.rb +63 -0
  77. data/test/form_builder/label_test.rb +71 -0
  78. data/test/form_builder/wrapper_test.rb +203 -0
  79. data/test/generators/simple_form_generator_test.rb +42 -0
  80. data/test/inputs/boolean_input_test.rb +140 -0
  81. data/test/inputs/collection_check_boxes_input_test.rb +224 -0
  82. data/test/inputs/collection_radio_buttons_input_test.rb +326 -0
  83. data/test/inputs/collection_select_input_test.rb +241 -0
  84. data/test/inputs/datetime_input_test.rb +99 -0
  85. data/test/inputs/disabled_test.rb +78 -0
  86. data/test/inputs/discovery_test.rb +69 -0
  87. data/test/inputs/file_input_test.rb +16 -0
  88. data/test/inputs/general_test.rb +116 -0
  89. data/test/inputs/grouped_collection_select_input_test.rb +118 -0
  90. data/test/inputs/hidden_input_test.rb +30 -0
  91. data/test/inputs/numeric_input_test.rb +173 -0
  92. data/test/inputs/priority_input_test.rb +43 -0
  93. data/test/inputs/readonly_test.rb +101 -0
  94. data/test/inputs/required_test.rb +113 -0
  95. data/test/inputs/string_input_test.rb +146 -0
  96. data/test/inputs/text_input_test.rb +24 -0
  97. data/test/simple_form_test.rb +9 -0
  98. data/test/support/discovery_inputs.rb +27 -0
  99. data/test/support/misc_helpers.rb +138 -0
  100. data/test/support/mock_controller.rb +24 -0
  101. data/test/support/models.rb +216 -0
  102. data/test/test_helper.rb +95 -0
  103. metadata +217 -0
@@ -0,0 +1,184 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class Base
4
+ extend I18nCache
5
+
6
+ include SimpleForm::Helpers::Autofocus
7
+ include SimpleForm::Helpers::Disabled
8
+ include SimpleForm::Helpers::Readonly
9
+ include SimpleForm::Helpers::Required
10
+ include SimpleForm::Helpers::Validators
11
+
12
+ include SimpleForm::Components::Errors
13
+ include SimpleForm::Components::Hints
14
+ include SimpleForm::Components::HTML5
15
+ include SimpleForm::Components::LabelInput
16
+ include SimpleForm::Components::Maxlength
17
+ include SimpleForm::Components::MinMax
18
+ include SimpleForm::Components::Pattern
19
+ include SimpleForm::Components::Placeholders
20
+ include SimpleForm::Components::Readonly
21
+
22
+ attr_reader :attribute_name, :column, :input_type, :reflection,
23
+ :options, :input_html_options, :input_html_classes, :html_classes
24
+
25
+ delegate :template, :object, :object_name, :lookup_model_names, :lookup_action, :to => :@builder
26
+
27
+ class_attribute :default_options
28
+ self.default_options = {}
29
+
30
+ def self.enable(*keys)
31
+ options = self.default_options.dup
32
+ keys.each { |key| options.delete(key) }
33
+ self.default_options = options
34
+ end
35
+
36
+ def self.disable(*keys)
37
+ options = self.default_options.dup
38
+ keys.each { |key| options[key] = false }
39
+ self.default_options = options
40
+ end
41
+
42
+ # Always enabled.
43
+ enable :hint
44
+
45
+ # Usually disabled, needs to be enabled explicitly passing true as option.
46
+ disable :maxlength, :placeholder, :pattern, :min_max
47
+
48
+ def initialize(builder, attribute_name, column, input_type, options = {})
49
+ super
50
+
51
+ options = options.dup
52
+ @builder = builder
53
+ @attribute_name = attribute_name
54
+ @column = column
55
+ @input_type = input_type
56
+ @reflection = options.delete(:reflection)
57
+ @options = options.reverse_merge!(self.class.default_options)
58
+ @required = calculate_required
59
+
60
+ # Notice that html_options_for receives a reference to input_html_classes.
61
+ # This means that classes added dynamically to input_html_classes will
62
+ # still propagate to input_html_options.
63
+ @html_classes = SimpleForm.additional_classes_for(:input) { additional_classes }
64
+
65
+ @input_html_classes = @html_classes.dup
66
+ @input_html_options = html_options_for(:input, input_html_classes).tap do |o|
67
+ o[:readonly] = true if has_readonly?
68
+ o[:disabled] = true if has_disabled?
69
+ o[:autofocus] = true if has_autofocus?
70
+ end
71
+ end
72
+
73
+ def input
74
+ raise NotImplementedError
75
+ end
76
+
77
+ def input_options
78
+ options
79
+ end
80
+
81
+ def additional_classes
82
+ @additional_classes ||= [input_type, required_class, readonly_class, disabled_class].compact
83
+ end
84
+
85
+ def input_class
86
+ "#{lookup_model_names.join("_")}_#{reflection_or_attribute_name}"
87
+ end
88
+
89
+ private
90
+
91
+ def add_size!
92
+ input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
93
+ end
94
+
95
+ def limit
96
+ if column
97
+ decimal_or_float? ? decimal_limit : column_limit
98
+ end
99
+ end
100
+
101
+ def column_limit
102
+ column.limit
103
+ end
104
+
105
+ # Add one for decimal point
106
+ def decimal_limit
107
+ column_limit && (column_limit + 1)
108
+ end
109
+
110
+ def decimal_or_float?
111
+ column.number? && column.type != :integer
112
+ end
113
+
114
+ def nested_boolean_style?
115
+ options.fetch(:boolean_style, SimpleForm.boolean_style) == :nested
116
+ end
117
+
118
+ # Find reflection name when available, otherwise use attribute
119
+ def reflection_or_attribute_name
120
+ @reflection_or_attribute_name ||= reflection ? reflection.name : attribute_name
121
+ end
122
+
123
+ # Retrieve options for the given namespace from the options hash
124
+ def html_options_for(namespace, css_classes)
125
+ html_options = options[:"#{namespace}_html"]
126
+ html_options = html_options ? html_options.dup : {}
127
+ css_classes << html_options[:class] if html_options.key?(:class)
128
+ html_options[:class] = css_classes unless css_classes.empty?
129
+ html_options
130
+ end
131
+
132
+ # Lookup translations for the given namespace using I18n, based on object name,
133
+ # actual action and attribute name. Lookup priority as follows:
134
+ #
135
+ # simple_form.{namespace}.{model}.{action}.{attribute}
136
+ # simple_form.{namespace}.{model}.{attribute}
137
+ # simple_form.{namespace}.defaults.{attribute}
138
+ #
139
+ # Namespace is used for :labels and :hints.
140
+ #
141
+ # Model is the actual object name, for a @user object you'll have :user.
142
+ # Action is the action being rendered, usually :new or :edit.
143
+ # And attribute is the attribute itself, :name for example.
144
+ #
145
+ # The lookup for nested attributes is also done in a nested format using
146
+ # both model and nested object names, such as follow:
147
+ #
148
+ # simple_form.{namespace}.{model}.{nested}.{action}.{attribute}
149
+ # simple_form.{namespace}.{model}.{nested}.{attribute}
150
+ # simple_form.{namespace}.{nested}.{action}.{attribute}
151
+ # simple_form.{namespace}.{nested}.{attribute}
152
+ # simple_form.{namespace}.defaults.{attribute}
153
+ #
154
+ # Example:
155
+ #
156
+ # simple_form:
157
+ # labels:
158
+ # user:
159
+ # new:
160
+ # email: 'E-mail para efetuar o sign in.'
161
+ # edit:
162
+ # email: 'E-mail.'
163
+ #
164
+ # Take a look at our locale example file.
165
+ def translate(namespace, default='')
166
+ model_names = lookup_model_names.dup
167
+ lookups = []
168
+
169
+ while !model_names.empty?
170
+ joined_model_names = model_names.join(".")
171
+ model_names.shift
172
+
173
+ lookups << :"#{joined_model_names}.#{lookup_action}.#{reflection_or_attribute_name}"
174
+ lookups << :"#{joined_model_names}.#{reflection_or_attribute_name}"
175
+ end
176
+ lookups << :"defaults.#{lookup_action}.#{reflection_or_attribute_name}"
177
+ lookups << :"defaults.#{reflection_or_attribute_name}"
178
+ lookups << default
179
+
180
+ I18n.t(lookups.shift, :scope => :"simple_form.#{namespace}", :default => lookups).presence
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class BlockInput < Base
4
+ def initialize(*args, &block)
5
+ super
6
+ @block = block
7
+ end
8
+
9
+ def input
10
+ template.capture(&@block)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,78 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class BooleanInput < Base
4
+ def input
5
+ if nested_boolean_style?
6
+ build_hidden_field_for_checkbox +
7
+ template.label_tag(nil, :class => "checkbox") {
8
+ build_check_box_without_hidden_field + inline_label
9
+ }
10
+ else
11
+ build_check_box
12
+ end
13
+ end
14
+
15
+ def label_input
16
+ if options[:label] == false
17
+ input
18
+ elsif nested_boolean_style?
19
+ html_options = label_html_options.dup
20
+ html_options[:class].push(:checkbox)
21
+
22
+ build_hidden_field_for_checkbox +
23
+ @builder.label(label_target, html_options) {
24
+ build_check_box_without_hidden_field + label_text
25
+ }
26
+ else
27
+ input + label
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ # Build a checkbox tag using default unchecked value. This allows us to
34
+ # reuse the method for nested boolean style, but with no unchecked value,
35
+ # which won't generate the hidden checkbox. This is the default functionality
36
+ # in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
37
+ def build_check_box(unchecked_value = unchecked_value)
38
+ @builder.check_box(attribute_name, input_html_options, checked_value, unchecked_value)
39
+ end
40
+
41
+ # Build a checkbox without generating the hidden field. See
42
+ # #build_hidden_field_for_checkbox for more info.
43
+ def build_check_box_without_hidden_field
44
+ build_check_box(nil)
45
+ end
46
+
47
+ # Create a hidden field for the current checkbox, so we can simulate Rails
48
+ # functionality with hidden + checkbox, but under a nested context, where
49
+ # we need the hidden field to be *outside* the label (otherwise it
50
+ # generates invalid html - html5 only).
51
+ def build_hidden_field_for_checkbox
52
+ @builder.hidden_field(attribute_name, :value => unchecked_value, :id => nil,
53
+ :disabled => input_html_options[:disabled],
54
+ :name => input_html_options[:name])
55
+ end
56
+
57
+ def inline_label
58
+ inline_option = options[:inline_label]
59
+ inline_option == true ? label_text : inline_option
60
+ end
61
+
62
+ # Booleans are not required by default because in most of the cases
63
+ # it makes no sense marking them as required. The only exception is
64
+ # Terms of Use usually presented at most sites sign up screen.
65
+ def required_by_default?
66
+ false
67
+ end
68
+
69
+ def checked_value
70
+ options.fetch(:checked_value, '1')
71
+ end
72
+
73
+ def unchecked_value
74
+ options.fetch(:unchecked_value, '0')
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,21 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class CollectionCheckBoxesInput < CollectionRadioButtonsInput
4
+ protected
5
+
6
+ # Checkbox components do not use the required html tag.
7
+ # More info: https://github.com/plataformatec/simple_form/issues/340#issuecomment-2871956
8
+ def has_required?
9
+ false
10
+ end
11
+
12
+ def build_nested_boolean_style_item_tag(collection_builder)
13
+ collection_builder.check_box + collection_builder.text
14
+ end
15
+
16
+ def item_wrapper_class
17
+ "checkbox"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,101 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class CollectionInput < Base
4
+ # Default boolean collection for use with selects/radios when no
5
+ # collection is given. Always fallback to this boolean collection.
6
+ # Texts can be translated using i18n in "simple_form.yes" and
7
+ # "simple_form.no" keys. See the example locale file.
8
+ def self.boolean_collection
9
+ i18n_cache :boolean_collection do
10
+ [ [I18n.t(:"simple_form.yes", :default => 'Yes'), true],
11
+ [I18n.t(:"simple_form.no", :default => 'No'), false] ]
12
+ end
13
+ end
14
+
15
+ def input
16
+ raise NotImplementedError,
17
+ "input should be implemented by classes inheriting from CollectionInput"
18
+ end
19
+
20
+ def input_options
21
+ options = super
22
+ options[:include_blank] = true unless skip_include_blank?
23
+ options
24
+ end
25
+
26
+ private
27
+
28
+ def collection
29
+ @collection ||= begin
30
+ collection = options.delete(:collection) || self.class.boolean_collection
31
+ collection.respond_to?(:call) ? collection.call : collection.to_a
32
+ end
33
+ end
34
+
35
+ def has_required?
36
+ super && (input_options[:include_blank] || multiple?)
37
+ end
38
+
39
+ # Check if :include_blank must be included by default.
40
+ def skip_include_blank?
41
+ (options.keys & [:prompt, :include_blank, :default, :selected]).any? || multiple?
42
+ end
43
+
44
+ def multiple?
45
+ !!options[:input_html].try(:[], :multiple)
46
+ end
47
+
48
+ # Detect the right method to find the label and value for a collection.
49
+ # If no label or value method are defined, will attempt to find them based
50
+ # on default label and value methods that can be configured through
51
+ # SimpleForm.collection_label_methods and
52
+ # SimpleForm.collection_value_methods.
53
+ def detect_collection_methods
54
+ label, value = options.delete(:label_method), options.delete(:value_method)
55
+
56
+ unless label && value
57
+ common_method_for = detect_common_display_methods
58
+ label ||= common_method_for[:label]
59
+ value ||= common_method_for[:value]
60
+ end
61
+
62
+ [label, value]
63
+ end
64
+
65
+ def detect_common_display_methods(collection_classes = detect_collection_classes)
66
+ collection_translated = translate_collection if collection_classes == [Symbol]
67
+
68
+ if collection_translated || collection_classes.include?(Array)
69
+ { :label => :first, :value => :last }
70
+ elsif collection_includes_basic_objects?(collection_classes)
71
+ { :label => :to_s, :value => :to_s }
72
+ 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) } }
77
+ end
78
+ end
79
+
80
+ def detect_collection_classes(some_collection = collection)
81
+ some_collection.map { |e| e.class }.uniq
82
+ end
83
+
84
+ def collection_includes_basic_objects?(collection_classes)
85
+ (collection_classes & [
86
+ String, Integer, Fixnum, Bignum, Float, NilClass, Symbol, TrueClass, FalseClass
87
+ ]).any?
88
+ end
89
+
90
+ def translate_collection
91
+ if translated_collection = translate(:options)
92
+ @collection = collection.map do |key|
93
+ [translated_collection[key] || key, key]
94
+ end
95
+ true
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
@@ -0,0 +1,63 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class CollectionRadioButtonsInput < CollectionInput
4
+ def input
5
+ label_method, value_method = detect_collection_methods
6
+
7
+ @builder.send("collection_#{input_type}",
8
+ attribute_name, collection, value_method, label_method,
9
+ input_options, input_html_options, &collection_block_for_nested_boolean_style
10
+ )
11
+ end
12
+
13
+ def input_options
14
+ options = super
15
+ apply_default_collection_options!(options)
16
+ apply_nested_boolean_collection_options!(options) if nested_boolean_style?
17
+ options
18
+ end
19
+
20
+ protected
21
+
22
+ def apply_default_collection_options!(options)
23
+ options[:item_wrapper_tag] ||= options.fetch(:item_wrapper_tag, SimpleForm.item_wrapper_tag)
24
+ options[:item_wrapper_class] = [
25
+ item_wrapper_class, options[:item_wrapper_class], SimpleForm.item_wrapper_class
26
+ ].compact.presence
27
+
28
+ options[:collection_wrapper_tag] ||= options.fetch(:collection_wrapper_tag, SimpleForm.collection_wrapper_tag)
29
+ options[:collection_wrapper_class] = [
30
+ options[:collection_wrapper_class], SimpleForm.collection_wrapper_class
31
+ ].compact.presence
32
+ end
33
+
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
+ def collection_block_for_nested_boolean_style
42
+ return unless nested_boolean_style?
43
+
44
+ proc { |builder| build_nested_boolean_style_item_tag(builder) }
45
+ end
46
+
47
+ def build_nested_boolean_style_item_tag(collection_builder)
48
+ collection_builder.radio_button + collection_builder.text
49
+ end
50
+
51
+ def item_wrapper_class
52
+ "radio"
53
+ end
54
+
55
+ # Do not attempt to generate label[for] attributes by default, unless an
56
+ # explicit html option is given. This avoids generating labels pointing to
57
+ # non existent fields.
58
+ def generate_label_for_attribute?
59
+ false
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class CollectionSelectInput < CollectionInput
4
+ def input
5
+ label_method, value_method = detect_collection_methods
6
+
7
+ @builder.collection_select(
8
+ attribute_name, collection, value_method, label_method,
9
+ input_options, input_html_options
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class DateTimeInput < Base
4
+ def input
5
+ @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options)
6
+ end
7
+
8
+ def has_required?
9
+ false
10
+ end
11
+
12
+ private
13
+
14
+ def label_target
15
+ position = case input_type
16
+ when :date, :datetime
17
+ date_order = input_options[:order] || I18n.t('date.order')
18
+ date_order.first
19
+ else
20
+ :hour
21
+ end
22
+
23
+ position = ActionView::Helpers::DateTimeSelector::POSITION[position]
24
+ "#{attribute_name}_#{position}i"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class FileInput < Base
4
+ def input
5
+ @builder.file_field(attribute_name, input_html_options)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,41 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class GroupedCollectionSelectInput < CollectionInput
4
+ def input
5
+ label_method, value_method = detect_collection_methods
6
+ @builder.grouped_collection_select(attribute_name, grouped_collection,
7
+ group_method, group_label_method, value_method, label_method,
8
+ input_options, input_html_options)
9
+ end
10
+
11
+ private
12
+
13
+ def grouped_collection
14
+ @grouped_collection ||= begin
15
+ grouped_collection = options.delete(:collection)
16
+ grouped_collection.respond_to?(:call) ? grouped_collection.call : grouped_collection.to_a
17
+ end
18
+ end
19
+
20
+ # Sample collection
21
+ def collection
22
+ @collection ||= grouped_collection.first.try(:send, group_method) || []
23
+ end
24
+
25
+ def group_method
26
+ @group_method ||= options.delete(:group_method)
27
+ end
28
+
29
+ def group_label_method
30
+ label = options.delete(:group_label_method)
31
+
32
+ unless label
33
+ common_method_for = detect_common_display_methods(detect_collection_classes(grouped_collection))
34
+ label = common_method_for[:label]
35
+ end
36
+
37
+ label
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,17 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class HiddenInput < Base
4
+ disable :label, :errors, :hint, :required
5
+
6
+ def input
7
+ @builder.hidden_field(attribute_name, input_html_options)
8
+ end
9
+
10
+ private
11
+
12
+ def required_class
13
+ nil
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class NumericInput < Base
4
+ enable :placeholder, :min_max
5
+
6
+ def input
7
+ add_size!
8
+ input_html_classes.unshift("numeric")
9
+ if html5?
10
+ input_html_options[:type] ||= "number"
11
+ input_html_options[:step] ||= integer? ? 1 : "any"
12
+ end
13
+ @builder.text_field(attribute_name, input_html_options)
14
+ end
15
+
16
+ private
17
+
18
+ # Rails adds the size attr by default, if the :size key does not exist.
19
+ def add_size!
20
+ input_html_options[:size] ||= nil
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class PasswordInput < Base
4
+ enable :placeholder, :maxlength
5
+
6
+ def input
7
+ add_size!
8
+ @builder.password_field(attribute_name, input_html_options)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class PriorityInput < CollectionSelectInput
4
+ def input
5
+ @builder.send(:"#{input_type}_select", attribute_name, input_priority,
6
+ input_options, input_html_options)
7
+ end
8
+
9
+ def input_priority
10
+ options[:priority] || SimpleForm.send(:"#{input_type}_priority")
11
+ end
12
+
13
+ protected
14
+
15
+ def has_required?
16
+ false
17
+ end
18
+
19
+ def skip_include_blank?
20
+ super || input_priority.present?
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class RangeInput < NumericInput
4
+ def input
5
+ if html5?
6
+ input_html_options[:type] ||= "range"
7
+ input_html_options[:step] ||= 1
8
+ end
9
+
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class StringInput < Base
4
+ enable :placeholder, :maxlength, :pattern
5
+
6
+ def input
7
+ unless string?
8
+ input_html_classes.unshift("string")
9
+ input_html_options[:type] ||= input_type if html5?
10
+ end
11
+
12
+ add_size!
13
+ @builder.text_field(attribute_name, input_html_options)
14
+ end
15
+
16
+ private
17
+
18
+ def string?
19
+ input_type == :string
20
+ end
21
+ end
22
+ end
23
+ end