phlexi-form 0.3.0.rc1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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