phlexi-form 0.3.0.rc1 → 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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -8
  3. data/gemfiles/default.gemfile.lock +34 -32
  4. data/gemfiles/rails_7.gemfile.lock +16 -18
  5. data/lib/phlexi/form/base.rb +42 -41
  6. data/lib/phlexi/form/builder.rb +297 -0
  7. data/lib/phlexi/form/components/base.rb +3 -3
  8. data/lib/phlexi/form/components/collection_checkboxes.rb +1 -1
  9. data/lib/phlexi/form/components/collection_radio_buttons.rb +1 -1
  10. data/lib/phlexi/form/components/concerns/extracts_input.rb +53 -0
  11. data/lib/phlexi/form/components/concerns/handles_input.rb +4 -34
  12. data/lib/phlexi/form/components/concerns/submits_form.rb +8 -0
  13. data/lib/phlexi/form/components/error.rb +1 -1
  14. data/lib/phlexi/form/components/file_input.rb +1 -0
  15. data/lib/phlexi/form/components/hint.rb +1 -1
  16. data/lib/phlexi/form/components/input.rb +16 -6
  17. data/lib/phlexi/form/components/input_array.rb +1 -1
  18. data/lib/phlexi/form/components/select.rb +4 -3
  19. data/lib/phlexi/form/components/textarea.rb +2 -3
  20. data/lib/phlexi/form/html.rb +18 -0
  21. data/lib/phlexi/form/{field_options → options}/autofocus.rb +1 -1
  22. data/lib/phlexi/form/{field_options → options}/collection.rb +14 -10
  23. data/lib/phlexi/form/{field_options → options}/disabled.rb +1 -1
  24. data/lib/phlexi/form/{field_options → options}/errors.rb +18 -14
  25. data/lib/phlexi/form/options/hints.rb +13 -0
  26. data/lib/phlexi/form/options/inferred_types.rb +32 -0
  27. data/lib/phlexi/form/{field_options → options}/length.rb +3 -3
  28. data/lib/phlexi/form/{field_options → options}/limit.rb +2 -2
  29. data/lib/phlexi/form/options/max.rb +55 -0
  30. data/lib/phlexi/form/options/min.rb +55 -0
  31. data/lib/phlexi/form/{field_options → options}/pattern.rb +2 -2
  32. data/lib/phlexi/form/{field_options → options}/readonly.rb +1 -1
  33. data/lib/phlexi/form/{field_options → options}/required.rb +3 -3
  34. data/lib/phlexi/form/options/step.rb +39 -0
  35. data/lib/phlexi/form/options/validators.rb +24 -0
  36. data/lib/phlexi/form/structure/field_collection.rb +12 -27
  37. data/lib/phlexi/form/structure/namespace.rb +4 -111
  38. data/lib/phlexi/form/structure/namespace_collection.rb +1 -32
  39. data/lib/phlexi/form/theme.rb +160 -0
  40. data/lib/phlexi/form/version.rb +1 -1
  41. data/lib/phlexi/form.rb +3 -6
  42. metadata +36 -24
  43. data/lib/phlexi/form/field_options/associations.rb +0 -21
  44. data/lib/phlexi/form/field_options/hints.rb +0 -22
  45. data/lib/phlexi/form/field_options/inferred_types.rb +0 -155
  46. data/lib/phlexi/form/field_options/labels.rb +0 -28
  47. data/lib/phlexi/form/field_options/min_max.rb +0 -92
  48. data/lib/phlexi/form/field_options/multiple.rb +0 -65
  49. data/lib/phlexi/form/field_options/placeholder.rb +0 -18
  50. data/lib/phlexi/form/field_options/themes.rb +0 -207
  51. data/lib/phlexi/form/field_options/validators.rb +0 -48
  52. data/lib/phlexi/form/structure/dom.rb +0 -62
  53. data/lib/phlexi/form/structure/field_builder.rb +0 -236
  54. data/lib/phlexi/form/structure/node.rb +0 -18
@@ -0,0 +1,297 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Form
5
+ # Builder class is responsible for building form fields with various options and components.
6
+ class Builder < Phlexi::Field::Builder
7
+ include Phlexi::Form::HTML::Behaviour
8
+ include Options::Validators
9
+ include Options::InferredTypes
10
+ include Options::Errors
11
+ include Options::Collection
12
+ include Options::Hints
13
+ include Options::Required
14
+ include Options::Autofocus
15
+ include Options::Disabled
16
+ include Options::Readonly
17
+ include Options::Length
18
+ include Options::Max
19
+ include Options::Min
20
+ include Options::Pattern
21
+ include Options::Limit
22
+ include Options::Step
23
+
24
+ class FieldCollection < Phlexi::Form::Structure::FieldCollection; end
25
+
26
+ attr_reader :input_attributes
27
+
28
+ # Initializes a new Builder instance.
29
+ #
30
+ # @param key [Symbol, String] The key for the field.
31
+ # @param parent [Structure::Namespace] The parent object.
32
+ # @param object [Object, nil] The associated object.
33
+ # @param value [Object] The initial value for the field.
34
+ # @param input_attributes [Hash] Default attributes to apply to input fields.
35
+ # @param options [Hash] Additional options for the field.
36
+ def initialize(*, input_attributes: {}, **)
37
+ super(*, **)
38
+
39
+ @input_attributes = input_attributes
40
+ end
41
+
42
+ # Creates an input tag for the field.
43
+ #
44
+ # @param attributes [Hash] Additional attributes for the input.
45
+ # @return [Components::Input] The input component.
46
+ def input_tag(**, &)
47
+ create_component(Components::Input, :input, **, &)
48
+ end
49
+
50
+ def string_tag(**, &)
51
+ input_tag(type: :text, theme: :string, **, &)
52
+ end
53
+
54
+ def number_tag(**, &)
55
+ input_tag(type: :number, theme: :number, **, &)
56
+ end
57
+
58
+ def date_tag(**, &)
59
+ input_tag(type: :date, theme: :date, **, &)
60
+ end
61
+
62
+ def time_tag(**, &)
63
+ input_tag(type: :time, theme: :time, **, &)
64
+ end
65
+
66
+ def datetime_local_tag(**, &)
67
+ input_tag(type: :"datetime-local", theme: :datetime, **, &)
68
+ end
69
+ alias_method :datetime_tag, :datetime_local_tag
70
+
71
+ def email_tag(**, &)
72
+ input_tag(type: :email, theme: :email, **, &)
73
+ end
74
+
75
+ def password_tag(**, &)
76
+ input_tag(type: :password, theme: :password, **, &)
77
+ end
78
+
79
+ def phone_tag(**, &)
80
+ input_tag(type: :tel, theme: :phone, **, &)
81
+ end
82
+
83
+ def color_tag(**, &)
84
+ input_tag(type: :color, theme: :color, **, &)
85
+ end
86
+
87
+ def url_tag(**, &)
88
+ input_tag(type: :url, theme: :url, **, &)
89
+ end
90
+
91
+ def search_tag(**, &)
92
+ input_tag(type: :search, theme: :search, **, &)
93
+ end
94
+
95
+ # Creates a checkbox tag for the field.
96
+ #
97
+ # @param attributes [Hash] Additional attributes for the checkbox.
98
+ # @return [Components::Checkbox] The checkbox component.
99
+ def checkbox_tag(**, &)
100
+ create_component(Components::Checkbox, :checkbox, **, &)
101
+ end
102
+
103
+ def boolean_tag(**, &)
104
+ checkbox_tag(**, theme: :boolean, &)
105
+ end
106
+
107
+ def file_input_tag(**, &)
108
+ create_component(Components::FileInput, :file, **, &)
109
+ end
110
+ alias_method :file_tag, :file_input_tag
111
+
112
+ # Creates collection checkboxes for the field.
113
+ #
114
+ # @param attributes [Hash] Additional attributes for the collection checkboxes.
115
+ # @yield [block] The block to be executed for each checkbox.
116
+ # @return [Components::CollectionCheckboxes] The collection checkboxes component.
117
+ def collection_checkboxes_tag(**, &)
118
+ create_component(Components::CollectionCheckboxes, :collection_checkboxes, **, &)
119
+ end
120
+
121
+ # Creates a radio button tag for the field.
122
+ #
123
+ # @param attributes [Hash] Additional attributes for the radio button.
124
+ # @return [Components::RadioButton] The radio button component.
125
+ def radio_button_tag(**, &)
126
+ create_component(Components::RadioButton, :radio, **, &)
127
+ end
128
+
129
+ # Creates collection radio buttons for the field.
130
+ #
131
+ # @param attributes [Hash] Additional attributes for the collection radio buttons.
132
+ # @yield [block] The block to be executed for each radio button.
133
+ # @return [Components::CollectionRadioButtons] The collection radio buttons component.
134
+ def collection_radio_buttons_tag(**, &)
135
+ create_component(Components::CollectionRadioButtons, :collection_radio_buttons, **, &)
136
+ end
137
+
138
+ # Creates a textarea tag for the field.
139
+ #
140
+ # @param attributes [Hash] Additional attributes for the textarea.
141
+ # @return [Components::Textarea] The textarea component.
142
+ def textarea_tag(**, &)
143
+ create_component(Components::Textarea, :textarea, **, &)
144
+ end
145
+ alias_method :text_tag, :textarea_tag
146
+
147
+ def hstore_tag(**, &)
148
+ @value = @value.to_s.tr("{}", "")
149
+ textarea_tag(**, theme: :hstore, &)
150
+ end
151
+
152
+ # Creates a select tag for the field.
153
+ #
154
+ # @param attributes [Hash] Additional attributes for the select.
155
+ # @return [Components::Select] The select component.
156
+ def select_tag(**, &)
157
+ create_component(Components::Select, :select, **, &)
158
+ end
159
+
160
+ def belongs_to_tag(**options, &)
161
+ options.fetch(:input_param) {
162
+ options[:input_param] = if association_reflection.respond_to?(:options) && association_reflection.options[:foreign_key]
163
+ association_reflection.options[:foreign_key]
164
+ else
165
+ :"#{association_reflection.name}_id"
166
+ end
167
+ }
168
+ select_tag(**options, &)
169
+ end
170
+
171
+ def polymorphic_belongs_to_tag(**, &)
172
+ # TODO: this requires a grouped_select component
173
+ # see: Plutonium::Core::Fields::Inputs::PolymorphicBelongsToAssociationInput
174
+ raise NotImplementedError, "polymorphic belongs_to associations are not YET supported"
175
+ end
176
+
177
+ def has_one_tag(**, &)
178
+ raise NotImplementedError, "has_one associations are NOT supported"
179
+ end
180
+
181
+ def has_many_tag(**options, &)
182
+ options.fetch(:input_param) {
183
+ options[:input_param] = :"#{association_reflection.name.to_s.singularize}_ids"
184
+ }
185
+
186
+ select_tag(**options, &)
187
+ end
188
+
189
+ def input_array_tag(**, &)
190
+ create_component(Components::InputArray, :array, **, &)
191
+ end
192
+
193
+ # Creates a label tag for the field.
194
+ #
195
+ # @param attributes [Hash] Additional attributes for the label.
196
+ # @return [Components::Label] The label component.
197
+ def label_tag(**, &)
198
+ create_component(Components::Label, :label, **, &)
199
+ end
200
+
201
+ # Creates a hint tag for the field.
202
+ #
203
+ # @param attributes [Hash] Additional attributes for the hint.
204
+ # @return [Components::Hint] The hint component.
205
+ def hint_tag(**, &)
206
+ create_component(Components::Hint, :hint, **, &)
207
+ end
208
+
209
+ # Creates an error tag for the field.
210
+ #
211
+ # @param attributes [Hash] Additional attributes for the error.
212
+ # @return [Components::Error] The error component.
213
+ def error_tag(**, &)
214
+ create_component(Components::Error, :error, **, &)
215
+ end
216
+
217
+ # Creates a full error tag for the field.
218
+ #
219
+ # @param attributes [Hash] Additional attributes for the full error.
220
+ # @return [Components::FullError] The full error component.
221
+ def full_error_tag(**, &)
222
+ create_component(Components::FullError, :full_error, **, &)
223
+ end
224
+
225
+ # Wraps the field with additional markup.
226
+ #
227
+ # @param inner [Hash] Attributes for the inner wrapper.
228
+ # @param attributes [Hash] Additional attributes for the wrapper.
229
+ # @yield [block] The block to be executed within the wrapper.
230
+ # @return [Components::Wrapper] The wrapper component.
231
+ def wrapped(inner: {}, **attributes, &)
232
+ attributes = apply_component_theme(attributes, :wrapper)
233
+ inner = apply_component_theme(inner, :inner_wrapper)
234
+ Components::Wrapper.new(self, inner: inner, **attributes, &)
235
+ end
236
+
237
+ # Creates a submit button
238
+ #
239
+ # @param attributes [Hash] Additional attributes for the submit.
240
+ # @return [Components::SubmitButton] The submit button component.
241
+ def submit_button_tag(**, &)
242
+ create_component(Components::SubmitButton, :submit_button, **, &)
243
+ end
244
+
245
+ def extract_input(params)
246
+ raise "field##{dom.name} did not define an input component" unless @field_input_extractor
247
+
248
+ @field_input_extractor.extract_input(params)
249
+ end
250
+
251
+ protected
252
+
253
+ def create_component(component_class, theme_key, **attributes, &)
254
+ attributes = mix(input_attributes, attributes) if component_class.include?(Phlexi::Form::Components::Concerns::HandlesInput)
255
+ component = component_class.new(self, **apply_component_theme(attributes, theme_key), &)
256
+ if component_class.include?(Components::Concerns::ExtractsInput)
257
+ raise "input component already defined: #{@field_input_extractor.inspect}" if @field_input_extractor
258
+
259
+ @field_input_extractor = component
260
+ end
261
+
262
+ component
263
+ end
264
+
265
+ def apply_component_theme(attributes, theme_key)
266
+ return attributes if attributes.key?(:class!)
267
+
268
+ theme_key = attributes.delete(:theme) || theme_key
269
+ mix({class: themed(theme_key, self)}, attributes)
270
+ end
271
+
272
+ def determine_initial_value(value)
273
+ return value unless value == NIL_VALUE
274
+
275
+ determine_value_from_association || super
276
+ end
277
+
278
+ def determine_value_from_object
279
+ object.respond_to?(key) ? object.public_send(key) : nil
280
+ end
281
+
282
+ def determine_value_from_association
283
+ return nil unless association_reflection.present?
284
+
285
+ value = object.public_send(key)
286
+ case association_reflection.macro
287
+ when :has_many, :has_and_belongs_to_many
288
+ value&.map { |v| v.public_send(association_reflection.klass.primary_key) }
289
+ when :belongs_to, :has_one
290
+ value&.public_send(association_reflection.klass.primary_key)
291
+ else
292
+ raise ArgumentError, "Unsupported association type: #{association_reflection.macro}"
293
+ end
294
+ end
295
+ end
296
+ end
297
+ end
@@ -3,7 +3,7 @@
3
3
  module Phlexi
4
4
  module Form
5
5
  module Components
6
- class Base < COMPONENT_BASE
6
+ class Base < Phlexi::Form::HTML
7
7
  attr_reader :field, :attributes
8
8
 
9
9
  def initialize(field, **attributes)
@@ -23,15 +23,15 @@ module Phlexi
23
23
  def append_attribute_classes
24
24
  return if attributes[:class] == false
25
25
 
26
- attributes[:class] = tokens(
26
+ default_classes = tokens(
27
27
  component_name,
28
- attributes[:class],
29
28
  -> { attributes[:required] } => "required",
30
29
  -> { !attributes[:required] } => "optional",
31
30
  -> { field.has_errors? } => "invalid",
32
31
  -> { attributes[:readonly] } => "readonly",
33
32
  -> { attributes[:disabled] } => "disabled"
34
33
  )
34
+ attributes[:class] = tokens(default_classes, attributes[:class])
35
35
  end
36
36
 
37
37
  def component_name
@@ -10,7 +10,7 @@ module Phlexi
10
10
 
11
11
  def view_template
12
12
  div(**attributes.slice(:id, :class)) do
13
- field.multi(option_mapper.values) do |builder|
13
+ field.repeated(option_mapper.values) do |builder|
14
14
  render builder.hidden_field_tag if builder.index == 0
15
15
 
16
16
  field = builder.field(
@@ -9,7 +9,7 @@ module Phlexi
9
9
 
10
10
  def view_template
11
11
  div(**attributes.slice(:id, :class)) do
12
- field.multi(option_mapper.values) do |builder|
12
+ field.repeated(option_mapper.values) do |builder|
13
13
  render builder.hidden_field_tag if builder.index == 0
14
14
 
15
15
  field = builder.field(
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Form
5
+ module Components
6
+ module Concerns
7
+ module ExtractsInput
8
+ # Collects parameters matching the input field's param key.
9
+ #
10
+ # @param params [Hash] the parameters to collect from.
11
+ # @return [Hash] the collected parameters.
12
+ def extract_input(params)
13
+ # # Handles multi parameter attributes
14
+ # # https://www.cookieshq.co.uk/posts/rails-spelunking-date-select
15
+ # # https://www.cookieshq.co.uk/posts/multiparameter-attributes
16
+
17
+ # # Matches
18
+ # # - parameter
19
+ # # - parameter(1)
20
+ # # - parameter(2)
21
+ # # - parameter(1i)
22
+ # # - parameter(2f)
23
+ # regex = /^#{param}(\(\d+[if]?\))?$/
24
+ # keys = params.select { |key, _| regex.match?(key) }.keys
25
+ # params.slice(*keys)
26
+
27
+ params ||= {}
28
+ {input_param => normalize_input(params[field.key])}
29
+ end
30
+
31
+ protected
32
+
33
+ def build_attributes
34
+ super
35
+ @input_param = attributes.delete(:input_param) || field.key
36
+ end
37
+
38
+ def input_param
39
+ @input_param
40
+ end
41
+
42
+ def normalize_input(input_value)
43
+ normalize_simple_input(input_value)
44
+ end
45
+
46
+ def normalize_simple_input(input_value)
47
+ input_value.to_s.presence
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -5,46 +5,16 @@ module Phlexi
5
5
  module Components
6
6
  module Concerns
7
7
  module HandlesInput
8
- # Collects parameters matching the input field's param key.
9
- #
10
- # @param params [Hash] the parameters to collect from.
11
- # @return [Hash] the collected parameters.
12
- def extract_input(params)
13
- # # Handles multi parameter attributes
14
- # # https://www.cookieshq.co.uk/posts/rails-spelunking-date-select
15
- # # https://www.cookieshq.co.uk/posts/multiparameter-attributes
16
-
17
- # # Matches
18
- # # - parameter
19
- # # - parameter(1)
20
- # # - parameter(2)
21
- # # - parameter(1i)
22
- # # - parameter(2f)
23
- # regex = /^#{param}(\(\d+[if]?\))?$/
24
- # keys = params.select { |key, _| regex.match?(key) }.keys
25
- # params.slice(*keys)
26
-
27
- params ||= {}
28
- {input_param => normalize_input(params[field.key])}
29
- end
8
+ include Phlexi::Form::Components::Concerns::ExtractsInput
30
9
 
31
10
  protected
32
11
 
33
12
  def build_attributes
34
13
  super
35
- @input_param = attributes.delete(:input_param) || field.key
36
- end
37
-
38
- def input_param
39
- @input_param
40
- end
41
-
42
- def normalize_input(input_value)
43
- normalize_simple_input(input_value)
44
- end
45
14
 
46
- def normalize_simple_input(input_value)
47
- input_value.to_s.presence
15
+ # only overwrite id if it was set in Base
16
+ attributes[:id] = field.dom.id if attributes[:id] == "#{field.dom.id}_#{component_name}"
17
+ attributes[:name] = field.dom.name
48
18
  end
49
19
  end
50
20
  end
@@ -5,6 +5,14 @@ module Phlexi
5
5
  module Components
6
6
  module Concerns
7
7
  module SubmitsForm
8
+ include Phlexi::Form::Components::Concerns::ExtractsInput
9
+
10
+ def extract_input(params)
11
+ {}
12
+ end
13
+
14
+ protected
15
+
8
16
  def submit_type_value
9
17
  if field.object.respond_to?(:persisted?)
10
18
  field.object.persisted? ? :update : :create
@@ -13,7 +13,7 @@ module Phlexi
13
13
  private
14
14
 
15
15
  def render?
16
- field.show_errors? && field.has_errors?
16
+ field.show_errors?
17
17
  end
18
18
  end
19
19
  end
@@ -14,6 +14,7 @@ module Phlexi
14
14
  def build_input_attributes
15
15
  attributes[:type] = :file
16
16
  super
17
+ # ensure we are always setting it to false
17
18
  attributes[:value] = false
18
19
  end
19
20
 
@@ -13,7 +13,7 @@ module Phlexi
13
13
  private
14
14
 
15
15
  def render?
16
- field.hint.present? && (!field.show_errors? || !field.has_errors?)
16
+ field.show_hint?
17
17
  end
18
18
  end
19
19
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "time"
4
+
3
5
  module Phlexi
4
6
  module Form
5
7
  module Components
@@ -15,17 +17,13 @@ module Phlexi
15
17
  def build_attributes
16
18
  super
17
19
 
18
- # only overwrite id if it was set in Base
19
- attributes[:id] = field.dom.id if attributes[:id] == "#{field.dom.id}_#{component_name}"
20
-
21
- attributes[:name] = field.dom.name
22
20
  attributes[:value] = field.dom.value
23
21
 
24
22
  build_input_attributes
25
23
  end
26
24
 
27
25
  def build_input_attributes
28
- attributes.fetch(:type) { attributes[:type] = field.inferred_input_type }
26
+ attributes.fetch(:type) { attributes[:type] = field.inferred_string_field_type }
29
27
  attributes.fetch(:disabled) { attributes[:disabled] = field.disabled? }
30
28
 
31
29
  case attributes[:type]
@@ -52,12 +50,24 @@ module Phlexi
52
50
  attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
53
51
  attributes.fetch(:required) { attributes[:required] = field.required? }
54
52
  attributes.fetch(:multiple) { attributes[:multiple] = field.multiple? }
55
- when :date, :time, :datetime_local
53
+ when :date, :time, :"datetime-local"
56
54
  attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
57
55
  attributes.fetch(:readonly) { attributes[:readonly] = field.readonly? }
58
56
  attributes.fetch(:required) { attributes[:required] = field.required? }
59
57
  attributes.fetch(:min) { attributes[:min] = field.min }
60
58
  attributes.fetch(:max) { attributes[:max] = field.max }
59
+
60
+ # TODO: Investigate if this is Timezone complaint
61
+ if field.value.respond_to?(:strftime)
62
+ attributes[:value] = case attributes[:type]
63
+ when :date
64
+ field.value.strftime("%Y-%m-%d")
65
+ when :time
66
+ field.value.strftime("%H:%M:%S")
67
+ when :"datetime-local"
68
+ field.value.strftime("%Y-%m-%dT%H:%M:%S")
69
+ end
70
+ end
61
71
  when :color
62
72
  attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
63
73
  when :range
@@ -9,7 +9,7 @@ module Phlexi
9
9
 
10
10
  def view_template
11
11
  div(**attributes.slice(:id, :class)) do
12
- field.multi(values.length) do |builder|
12
+ field.repeated(values.length) do |builder|
13
13
  render builder.hidden_field_tag if builder.index == 0
14
14
 
15
15
  field = builder.field(
@@ -31,9 +31,6 @@ module Phlexi
31
31
  def build_attributes
32
32
  super
33
33
 
34
- attributes[:id] = field.dom.id
35
- attributes[:name] = field.dom.name
36
-
37
34
  build_select_attributes
38
35
  end
39
36
 
@@ -46,6 +43,10 @@ module Phlexi
46
43
  attributes[:disabled] = attributes.fetch(:disabled, field.disabled?)
47
44
  attributes[:multiple] = attributes.fetch(:multiple, field.multiple?)
48
45
  attributes[:size] = attributes.fetch(:size, field.limit)
46
+
47
+ if attributes[:multiple]
48
+ attributes[:name] = "#{attributes[:name].sub(/\[\]$/, "")}[]"
49
+ end
49
50
  end
50
51
 
51
52
  def blank_option_text
@@ -4,6 +4,8 @@ module Phlexi
4
4
  module Form
5
5
  module Components
6
6
  class Textarea < Base
7
+ include Concerns::HandlesInput
8
+
7
9
  def view_template
8
10
  textarea(**attributes) { field.dom.value }
9
11
  end
@@ -13,9 +15,6 @@ module Phlexi
13
15
  def build_attributes
14
16
  super
15
17
 
16
- attributes[:id] = field.dom.id
17
- attributes[:name] = field.dom.name
18
-
19
18
  build_textarea_attributes
20
19
  end
21
20
 
@@ -0,0 +1,18 @@
1
+ module Phlexi
2
+ module Form
3
+ class HTML < (defined?(::ApplicationComponent) ? ::ApplicationComponent : Phlex::HTML)
4
+ module Behaviour
5
+ protected
6
+
7
+ def themed(component, field)
8
+ base_theme = Phlexi::Form::Theme.instance.resolve_theme(component)
9
+ validity_theme = Phlexi::Form::Theme.instance.resolve_validity_theme(component, field)
10
+
11
+ tokens(base_theme, validity_theme)
12
+ end
13
+ end
14
+
15
+ include Behaviour
16
+ end
17
+ end
18
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Phlexi
4
4
  module Form
5
- module FieldOptions
5
+ module Options
6
6
  module Autofocus
7
7
  def focused?
8
8
  options[:autofocus] == true
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Phlexi
4
4
  module Form
5
- module FieldOptions
5
+ module Options
6
6
  module Collection
7
7
  def collection(collection = nil)
8
8
  if collection.nil?
9
- options[:collection] = options.fetch(:collection) { infer_collection }
9
+ options.fetch(:collection) { options[:collection] = infer_collection }
10
10
  else
11
11
  options[:collection] = collection
12
12
  self
@@ -16,23 +16,27 @@ module Phlexi
16
16
  private
17
17
 
18
18
  def infer_collection
19
+ if object.class.respond_to?(:defined_enums)
20
+ return object.class.defined_enums.fetch(key.to_s).keys if object.class.defined_enums.key?(key.to_s)
21
+ end
22
+
19
23
  collection_value_from_association || collection_value_from_validator
20
24
  end
21
25
 
22
26
  def collection_value_from_association
23
- return unless reflection
27
+ return unless association_reflection
24
28
 
25
- relation = reflection.klass.all
29
+ relation = association_reflection.klass.all
26
30
 
27
- if reflection.respond_to?(:scope) && reflection.scope
28
- relation = if reflection.scope.parameters.any?
29
- reflection.klass.instance_exec(object, &reflection.scope)
31
+ if association_reflection.respond_to?(:scope) && association_reflection.scope
32
+ relation = if association_reflection.scope.parameters.any?
33
+ association_reflection.klass.instance_exec(object, &association_reflection.scope)
30
34
  else
31
- reflection.klass.instance_exec(&reflection.scope)
35
+ association_reflection.klass.instance_exec(&association_reflection.scope)
32
36
  end
33
37
  else
34
- order = reflection.options[:order]
35
- conditions = reflection.options[:conditions]
38
+ order = association_reflection.options[:order]
39
+ conditions = association_reflection.options[:conditions]
36
40
  conditions = object.instance_exec(&conditions) if conditions.respond_to?(:call)
37
41
 
38
42
  relation = relation.where(conditions) if relation.respond_to?(:where) && conditions.present?
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Phlexi
4
4
  module Form
5
- module FieldOptions
5
+ module Options
6
6
  module Disabled
7
7
  def disabled?
8
8
  options[:disabled] == true