phlexi-form 0.2.0 → 0.3.0.rc1

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +4 -9
  3. data/README.md +115 -316
  4. data/TODO +4 -0
  5. data/config.ru +0 -3
  6. data/gemfiles/default.gemfile.lock +22 -2
  7. data/gemfiles/rails_7.gemfile +8 -0
  8. data/gemfiles/rails_7.gemfile.lock +282 -0
  9. data/lib/phlexi/form/base.rb +52 -35
  10. data/lib/phlexi/form/components/base.rb +12 -6
  11. data/lib/phlexi/form/components/checkbox.rb +5 -0
  12. data/lib/phlexi/form/components/collection_checkboxes.rb +28 -14
  13. data/lib/phlexi/form/components/collection_radio_buttons.rb +19 -13
  14. data/lib/phlexi/form/components/concerns/handles_array_input.rb +21 -0
  15. data/lib/phlexi/form/components/concerns/handles_input.rb +53 -0
  16. data/lib/phlexi/form/components/concerns/has_options.rb +6 -2
  17. data/lib/phlexi/form/components/concerns/submits_form.rb +39 -0
  18. data/lib/phlexi/form/components/file_input.rb +32 -0
  19. data/lib/phlexi/form/components/input.rb +39 -33
  20. data/lib/phlexi/form/components/input_array.rb +45 -0
  21. data/lib/phlexi/form/components/label.rb +2 -1
  22. data/lib/phlexi/form/components/radio_button.rb +11 -1
  23. data/lib/phlexi/form/components/select.rb +21 -5
  24. data/lib/phlexi/form/components/submit_button.rb +41 -0
  25. data/lib/phlexi/form/field_options/associations.rb +21 -0
  26. data/lib/phlexi/form/field_options/autofocus.rb +1 -1
  27. data/lib/phlexi/form/field_options/collection.rb +26 -9
  28. data/lib/phlexi/form/field_options/errors.rb +10 -0
  29. data/lib/phlexi/form/field_options/{type.rb → inferred_types.rb} +12 -12
  30. data/lib/phlexi/form/field_options/multiple.rb +2 -0
  31. data/lib/phlexi/form/field_options/themes.rb +207 -0
  32. data/lib/phlexi/form/option_mapper.rb +2 -2
  33. data/lib/phlexi/form/structure/dom.rb +19 -14
  34. data/lib/phlexi/form/structure/field_builder.rb +145 -108
  35. data/lib/phlexi/form/structure/field_collection.rb +14 -5
  36. data/lib/phlexi/form/structure/namespace.rb +31 -19
  37. data/lib/phlexi/form/structure/namespace_collection.rb +20 -20
  38. data/lib/phlexi/form/structure/node.rb +1 -1
  39. data/lib/phlexi/form/version.rb +1 -1
  40. data/lib/phlexi/form.rb +4 -1
  41. metadata +30 -6
  42. data/CODE_OF_CONDUCT.md +0 -84
@@ -0,0 +1,282 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ phlexi-form (0.2.0)
5
+ activesupport
6
+ phlex (~> 1.11)
7
+ zeitwerk
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actioncable (7.1.3.4)
13
+ actionpack (= 7.1.3.4)
14
+ activesupport (= 7.1.3.4)
15
+ nio4r (~> 2.0)
16
+ websocket-driver (>= 0.6.1)
17
+ zeitwerk (~> 2.6)
18
+ actionmailbox (7.1.3.4)
19
+ actionpack (= 7.1.3.4)
20
+ activejob (= 7.1.3.4)
21
+ activerecord (= 7.1.3.4)
22
+ activestorage (= 7.1.3.4)
23
+ activesupport (= 7.1.3.4)
24
+ mail (>= 2.7.1)
25
+ net-imap
26
+ net-pop
27
+ net-smtp
28
+ actionmailer (7.1.3.4)
29
+ actionpack (= 7.1.3.4)
30
+ actionview (= 7.1.3.4)
31
+ activejob (= 7.1.3.4)
32
+ activesupport (= 7.1.3.4)
33
+ mail (~> 2.5, >= 2.5.4)
34
+ net-imap
35
+ net-pop
36
+ net-smtp
37
+ rails-dom-testing (~> 2.2)
38
+ actionpack (7.1.3.4)
39
+ actionview (= 7.1.3.4)
40
+ activesupport (= 7.1.3.4)
41
+ nokogiri (>= 1.8.5)
42
+ racc
43
+ rack (>= 2.2.4)
44
+ rack-session (>= 1.0.1)
45
+ rack-test (>= 0.6.3)
46
+ rails-dom-testing (~> 2.2)
47
+ rails-html-sanitizer (~> 1.6)
48
+ actiontext (7.1.3.4)
49
+ actionpack (= 7.1.3.4)
50
+ activerecord (= 7.1.3.4)
51
+ activestorage (= 7.1.3.4)
52
+ activesupport (= 7.1.3.4)
53
+ globalid (>= 0.6.0)
54
+ nokogiri (>= 1.8.5)
55
+ actionview (7.1.3.4)
56
+ activesupport (= 7.1.3.4)
57
+ builder (~> 3.1)
58
+ erubi (~> 1.11)
59
+ rails-dom-testing (~> 2.2)
60
+ rails-html-sanitizer (~> 1.6)
61
+ activejob (7.1.3.4)
62
+ activesupport (= 7.1.3.4)
63
+ globalid (>= 0.3.6)
64
+ activemodel (7.1.3.4)
65
+ activesupport (= 7.1.3.4)
66
+ activerecord (7.1.3.4)
67
+ activemodel (= 7.1.3.4)
68
+ activesupport (= 7.1.3.4)
69
+ timeout (>= 0.4.0)
70
+ activestorage (7.1.3.4)
71
+ actionpack (= 7.1.3.4)
72
+ activejob (= 7.1.3.4)
73
+ activerecord (= 7.1.3.4)
74
+ activesupport (= 7.1.3.4)
75
+ marcel (~> 1.0)
76
+ activesupport (7.1.3.4)
77
+ base64
78
+ bigdecimal
79
+ concurrent-ruby (~> 1.0, >= 1.0.2)
80
+ connection_pool (>= 2.2.5)
81
+ drb
82
+ i18n (>= 1.6, < 2)
83
+ minitest (>= 5.1)
84
+ mutex_m
85
+ tzinfo (~> 2.0)
86
+ addressable (2.8.7)
87
+ public_suffix (>= 2.0.2, < 7.0)
88
+ ansi (1.5.0)
89
+ appraisal (2.5.0)
90
+ bundler
91
+ rake
92
+ thor (>= 0.14.0)
93
+ ast (2.4.2)
94
+ base64 (0.2.0)
95
+ bigdecimal (3.1.8)
96
+ builder (3.3.0)
97
+ bundle-audit (0.1.0)
98
+ bundler-audit
99
+ bundler-audit (0.9.1)
100
+ bundler (>= 1.2.0, < 3)
101
+ thor (~> 1.0)
102
+ capybara (3.40.0)
103
+ addressable
104
+ matrix
105
+ mini_mime (>= 0.1.3)
106
+ nokogiri (~> 1.11)
107
+ rack (>= 1.6.0)
108
+ rack-test (>= 0.6.3)
109
+ regexp_parser (>= 1.5, < 3.0)
110
+ xpath (~> 3.2)
111
+ combustion (1.5.0)
112
+ activesupport (>= 3.0.0)
113
+ railties (>= 3.0.0)
114
+ thor (>= 0.14.6)
115
+ concurrent-ruby (1.3.3)
116
+ connection_pool (2.4.1)
117
+ crass (1.0.6)
118
+ date (3.3.4)
119
+ drb (2.2.1)
120
+ erubi (1.13.0)
121
+ globalid (1.2.1)
122
+ activesupport (>= 6.1)
123
+ i18n (1.14.5)
124
+ concurrent-ruby (~> 1.0)
125
+ io-console (0.7.2)
126
+ irb (1.14.0)
127
+ rdoc (>= 4.0.0)
128
+ reline (>= 0.4.2)
129
+ json (2.7.2)
130
+ language_server-protocol (3.17.0.3)
131
+ lint_roller (1.1.0)
132
+ loofah (2.22.0)
133
+ crass (~> 1.0.2)
134
+ nokogiri (>= 1.12.0)
135
+ mail (2.8.1)
136
+ mini_mime (>= 0.1.1)
137
+ net-imap
138
+ net-pop
139
+ net-smtp
140
+ marcel (1.0.4)
141
+ matrix (0.4.2)
142
+ mini_mime (1.1.5)
143
+ minitest (5.24.1)
144
+ minitest-reporters (1.7.1)
145
+ ansi
146
+ builder
147
+ minitest (>= 5.0)
148
+ ruby-progressbar
149
+ mutex_m (0.2.0)
150
+ net-imap (0.4.14)
151
+ date
152
+ net-protocol
153
+ net-pop (0.1.2)
154
+ net-protocol
155
+ net-protocol (0.2.2)
156
+ timeout
157
+ net-smtp (0.5.0)
158
+ net-protocol
159
+ nio4r (2.7.3)
160
+ nokogiri (1.16.7-x86_64-darwin)
161
+ racc (~> 1.4)
162
+ parallel (1.25.1)
163
+ parser (3.3.4.0)
164
+ ast (~> 2.4.1)
165
+ racc
166
+ phlex (1.11.0)
167
+ phlex-testing-capybara (0.1.0)
168
+ capybara (~> 3.38)
169
+ phlex (>= 0.5)
170
+ psych (5.1.2)
171
+ stringio
172
+ public_suffix (6.0.1)
173
+ racc (1.8.1)
174
+ rack (3.1.7)
175
+ rack-session (2.0.0)
176
+ rack (>= 3.0.0)
177
+ rack-test (2.1.0)
178
+ rack (>= 1.3)
179
+ rackup (2.1.0)
180
+ rack (>= 3)
181
+ webrick (~> 1.8)
182
+ rails (7.1.3.4)
183
+ actioncable (= 7.1.3.4)
184
+ actionmailbox (= 7.1.3.4)
185
+ actionmailer (= 7.1.3.4)
186
+ actionpack (= 7.1.3.4)
187
+ actiontext (= 7.1.3.4)
188
+ actionview (= 7.1.3.4)
189
+ activejob (= 7.1.3.4)
190
+ activemodel (= 7.1.3.4)
191
+ activerecord (= 7.1.3.4)
192
+ activestorage (= 7.1.3.4)
193
+ activesupport (= 7.1.3.4)
194
+ bundler (>= 1.15.0)
195
+ railties (= 7.1.3.4)
196
+ rails-dom-testing (2.2.0)
197
+ activesupport (>= 5.0.0)
198
+ minitest
199
+ nokogiri (>= 1.6)
200
+ rails-html-sanitizer (1.6.0)
201
+ loofah (~> 2.21)
202
+ nokogiri (~> 1.14)
203
+ railties (7.1.3.4)
204
+ actionpack (= 7.1.3.4)
205
+ activesupport (= 7.1.3.4)
206
+ irb
207
+ rackup (>= 1.0.0)
208
+ rake (>= 12.2)
209
+ thor (~> 1.0, >= 1.2.2)
210
+ zeitwerk (~> 2.6)
211
+ rainbow (3.1.1)
212
+ rake (13.2.1)
213
+ rdoc (6.7.0)
214
+ psych (>= 4.0.0)
215
+ regexp_parser (2.9.2)
216
+ reline (0.5.9)
217
+ io-console (~> 0.5)
218
+ rexml (3.3.2)
219
+ strscan
220
+ rubocop (1.64.1)
221
+ json (~> 2.3)
222
+ language_server-protocol (>= 3.17.0)
223
+ parallel (~> 1.10)
224
+ parser (>= 3.3.0.2)
225
+ rainbow (>= 2.2.2, < 4.0)
226
+ regexp_parser (>= 1.8, < 3.0)
227
+ rexml (>= 3.2.5, < 4.0)
228
+ rubocop-ast (>= 1.31.1, < 2.0)
229
+ ruby-progressbar (~> 1.7)
230
+ unicode-display_width (>= 2.4.0, < 3.0)
231
+ rubocop-ast (1.31.3)
232
+ parser (>= 3.3.1.0)
233
+ rubocop-performance (1.21.1)
234
+ rubocop (>= 1.48.1, < 2.0)
235
+ rubocop-ast (>= 1.31.1, < 2.0)
236
+ ruby-progressbar (1.13.0)
237
+ sqlite3 (1.7.3-x86_64-darwin)
238
+ standard (1.39.2)
239
+ language_server-protocol (~> 3.17.0.2)
240
+ lint_roller (~> 1.0)
241
+ rubocop (~> 1.64.0)
242
+ standard-custom (~> 1.0.0)
243
+ standard-performance (~> 1.4)
244
+ standard-custom (1.0.2)
245
+ lint_roller (~> 1.0)
246
+ rubocop (~> 1.50)
247
+ standard-performance (1.4.0)
248
+ lint_roller (~> 1.1)
249
+ rubocop-performance (~> 1.21.0)
250
+ stringio (3.1.1)
251
+ strscan (3.1.0)
252
+ thor (1.3.1)
253
+ timeout (0.4.1)
254
+ tzinfo (2.0.6)
255
+ concurrent-ruby (~> 1.0)
256
+ unicode-display_width (2.5.0)
257
+ webrick (1.8.1)
258
+ websocket-driver (0.7.6)
259
+ websocket-extensions (>= 0.1.0)
260
+ websocket-extensions (0.1.5)
261
+ xpath (3.2.0)
262
+ nokogiri (~> 1.8)
263
+ zeitwerk (2.6.17)
264
+
265
+ PLATFORMS
266
+ x86_64-darwin
267
+
268
+ DEPENDENCIES
269
+ appraisal
270
+ bundle-audit
271
+ combustion
272
+ minitest
273
+ minitest-reporters
274
+ phlex-testing-capybara
275
+ phlexi-form!
276
+ rails (~> 7.1.3, >= 7.1.3.4)
277
+ rake
278
+ sqlite3 (~> 1.4)
279
+ standard
280
+
281
+ BUNDLED WITH
282
+ 2.5.16
@@ -1,28 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "action_view/model_naming"
4
3
  require "active_support/core_ext/module/delegation"
5
4
  require "active_support/string_inquirer"
6
- require "active_support/core_ext/object/blank"
5
+ require "active_support/core_ext/hash/deep_merge"
6
+ require "active_support/core_ext/string/inflections"
7
7
 
8
8
  module Phlexi
9
9
  module Form
10
10
  # A form component for building flexible and customizable forms.
11
11
  #
12
12
  # @example Basic usage
13
- # Form.new(user, action: '/users', method: 'post') do |f|
14
- # f.field :name
15
- # f.field :email
13
+ # Phlexi::Form.new(user, action: '/users', method: 'post') do |f|
14
+ # render field(:name).placeholder("Name").input_tag
15
+ # render field(:email).placeholder("Email").input_tag
16
16
  # end
17
17
  #
18
18
  # @attr_reader [Symbol] key The form's key, derived from the record or explicitly set
19
19
  # @attr_reader [ActiveModel::Model, nil] object The form's associated object
20
- class Base < BaseComponent
21
- include ActionView::ModelNaming
20
+ class Base < COMPONENT_BASE
21
+ class Namespace < Structure::Namespace; end
22
+
23
+ class FieldBuilder < Structure::FieldBuilder; end
24
+
25
+ class << self
26
+ def inherited(subclass)
27
+ subclass.const_set(:Namespace, Class.new(self::Namespace)) unless subclass.const_defined?(:Namespace)
28
+ subclass.const_set(:FieldBuilder, Class.new(self::FieldBuilder)) unless subclass.const_defined?(:FieldBuilder)
29
+ super
30
+ end
31
+ end
22
32
 
23
33
  attr_reader :key, :object
24
34
 
25
- delegate :field, :nest_one, :nest_many, to: :@namespace
35
+ delegate :field, :submit_button, :nest_one, :nest_many, to: :@namespace
26
36
 
27
37
  # Initializes a new Form instance.
28
38
  #
@@ -39,6 +49,8 @@ module Phlexi
39
49
  @form_method = method
40
50
  @form_class = options.delete(:class)
41
51
  @attributes = attributes
52
+ @namespace_klass = options.delete(:namespace_klass) || default_namespace_klass
53
+ @builder_klass = options.delete(:builder_klass) || default_builder_klass
42
54
  @options = options
43
55
 
44
56
  initialize_object_and_key(record)
@@ -65,7 +77,7 @@ module Phlexi
65
77
  #
66
78
  # @yield The form's content
67
79
  # @return [void]
68
- def form_tag(&block)
80
+ def form_tag(&)
69
81
  form(**form_attributes) do
70
82
  render_hidden_method_field
71
83
  render_authenticity_token if authenticity_token?
@@ -73,26 +85,33 @@ module Phlexi
73
85
  end
74
86
  end
75
87
 
88
+ def extract_input(params)
89
+ call unless @_rendered
90
+ @namespace.extract_input(params)
91
+ end
92
+
76
93
  protected
77
94
 
78
- attr_reader :options, :attributes
95
+ attr_reader :options, :attributes, :namespace_klass, :builder_klass
79
96
 
80
97
  # Initializes the object and key based on the given record.
81
98
  #
82
99
  # @param record [ActiveModel::Model, Symbol, String] The form's associated record or key
83
100
  # @return [void]
84
101
  def initialize_object_and_key(record)
102
+ # always pop these keys
103
+ # add support for `as` to make it more rails friendly
104
+ @key = options.delete(:key) || options.delete(:as)
105
+
85
106
  case record
86
107
  when String, Symbol
87
108
  @object = nil
88
109
  @key = record
89
110
  else
90
- @object = convert_to_model(record)
91
- @key = options.delete(:as)
92
-
111
+ @object = record
93
112
  if @key.nil?
94
- unless object.respond_to?(:model_name) && object.model_name.respond_to?(:param_key) && object.model_name.param_key
95
- raise ArgumentError, "record must respond to #model_name.param_key with a non nil value"
113
+ unless object.respond_to?(:model_name) && object.model_name.respond_to?(:param_key) && object.model_name.param_key.present?
114
+ raise ArgumentError, "record must respond to #model_name.param_key with a non nil value or set `key` option e.g. Phlexi::Form(record, key: :record)"
96
115
  end
97
116
  @key = object.model_name.param_key
98
117
  end
@@ -111,7 +130,7 @@ module Phlexi
111
130
  #
112
131
  # @return [void]
113
132
  def initialize_attributes
114
- attributes[:accept_charset] ||= "UTF-8"
133
+ attributes.fetch(:accept_charset) { attributes[:accept_charset] = "UTF-8" }
115
134
  end
116
135
 
117
136
  # Determines the form's action URL.
@@ -140,9 +159,7 @@ module Phlexi
140
159
  # Retrieves the form's CSS classes.
141
160
  #
142
161
  # @return [String] The form's CSS classes
143
- def form_class
144
- @form_class || "flex flex-col space-y-6 px-4 py-2"
145
- end
162
+ attr_reader :form_class
146
163
 
147
164
  # Checks if the authenticity token should be included.
148
165
  #
@@ -171,22 +188,11 @@ module Phlexi
171
188
  #
172
189
  # @return [String, nil] The appropriate form method
173
190
  def object_form_method
174
- return unless object
175
- object.persisted? ? "patch" : "post"
176
- end
177
-
178
- # Retrieves the namespace class.
179
- #
180
- # @return [Class] The namespace class
181
- def namespace_klass
182
- @namespace_klass ||= options.delete(:namespace_klass) || Structure::Namespace
183
- end
184
-
185
- # Retrieves the builder class.
186
- #
187
- # @return [Class] The builder class
188
- def builder_klass
189
- @builder_klass ||= options.delete(:builder_klass) || Structure::FieldBuilder
191
+ if object.respond_to?(:persisted?)
192
+ object.persisted? ? "patch" : "post"
193
+ elsif object.present?
194
+ "post"
195
+ end
190
196
  end
191
197
 
192
198
  # Renders the hidden method field for non-standard HTTP methods.
@@ -216,6 +222,7 @@ module Phlexi
216
222
  # @return [Hash] The form attributes
217
223
  def form_attributes
218
224
  {
225
+ id: @namespace.dom_id,
219
226
  action: form_action,
220
227
  method: standardized_form_method,
221
228
  class: form_class,
@@ -229,6 +236,16 @@ module Phlexi
229
236
  def render_authenticity_token
230
237
  authenticity_token_field
231
238
  end
239
+
240
+ private
241
+
242
+ def default_namespace_klass
243
+ self.class::Namespace
244
+ end
245
+
246
+ def default_builder_klass
247
+ self.class::FieldBuilder
248
+ end
232
249
  end
233
250
  end
234
251
  end
@@ -3,7 +3,7 @@
3
3
  module Phlexi
4
4
  module Form
5
5
  module Components
6
- class Base < BaseComponent
6
+ class Base < COMPONENT_BASE
7
7
  attr_reader :field, :attributes
8
8
 
9
9
  def initialize(field, **attributes)
@@ -11,20 +11,26 @@ module Phlexi
11
11
  @attributes = attributes
12
12
 
13
13
  build_attributes
14
+ append_attribute_classes
14
15
  end
15
16
 
16
17
  protected
17
18
 
18
19
  def build_attributes
19
- attributes[:id] ||= "#{field.dom.id}_#{component_name}"
20
+ attributes.fetch(:id) { attributes[:id] = "#{field.dom.id}_#{component_name}" }
21
+ end
22
+
23
+ def append_attribute_classes
24
+ return if attributes[:class] == false
25
+
20
26
  attributes[:class] = tokens(
21
27
  component_name,
22
28
  attributes[:class],
23
- -> { field.required? } => "required",
24
- -> { !field.required? } => "optional",
29
+ -> { attributes[:required] } => "required",
30
+ -> { !attributes[:required] } => "optional",
25
31
  -> { field.has_errors? } => "invalid",
26
- -> { field.readonly? } => "readonly",
27
- -> { field.disabled? } => "disabled"
32
+ -> { attributes[:readonly] } => "readonly",
33
+ -> { attributes[:disabled] } => "disabled"
28
34
  )
29
35
  end
30
36
 
@@ -37,6 +37,11 @@ module Phlexi
37
37
  field.dom.value == @checked_value
38
38
  end
39
39
  end
40
+
41
+ def normalize_input(...)
42
+ input_value = super
43
+ [@checked_value, @unchecked_value].include?(input_value) ? input_value : @unchecked_value
44
+ end
40
45
  end
41
46
  end
42
47
  end
@@ -4,26 +4,40 @@ module Phlexi
4
4
  module Form
5
5
  module Components
6
6
  class CollectionCheckboxes < Base
7
+ include Concerns::HandlesInput
8
+ include Concerns::HandlesArrayInput
7
9
  include Concerns::HasOptions
8
10
 
9
11
  def view_template
10
- render field.input_tag(type: :hidden, value: "", theme: false, hidden: true, autocomplete: "off", multiple: true)
11
- field.multi(option_mapper.values) do |builder|
12
- field = builder.field(
13
- label: option_mapper[builder.key],
14
- attributes: {
15
- checked_value: builder.key,
16
- include_hidden: false
17
- }
18
- )
19
- if block_given?
20
- yield field
21
- else
22
- render field.checkbox_tag
23
- render field.label_tag
12
+ div(**attributes.slice(:id, :class)) do
13
+ field.multi(option_mapper.values) do |builder|
14
+ render builder.hidden_field_tag if builder.index == 0
15
+
16
+ field = builder.field(
17
+ label: option_mapper[builder.key],
18
+ # We set the attributes here so they are applied to all input components even if the user decides to use a block
19
+ input_attributes: {
20
+ checked_value: builder.key,
21
+ include_hidden: false,
22
+ checked: selected?(builder.key)
23
+ }
24
+ )
25
+ if block_given?
26
+ yield field
27
+ else
28
+ render field.checkbox_tag
29
+ render field.label_tag
30
+ end
24
31
  end
25
32
  end
26
33
  end
34
+
35
+ protected
36
+
37
+ def build_attributes
38
+ super
39
+ attributes[:multiple] = true
40
+ end
27
41
  end
28
42
  end
29
43
  end
@@ -4,22 +4,28 @@ module Phlexi
4
4
  module Form
5
5
  module Components
6
6
  class CollectionRadioButtons < Base
7
+ include Concerns::HandlesInput
7
8
  include Concerns::HasOptions
8
9
 
9
10
  def view_template
10
- render field.input_tag(type: :hidden, value: "", theme: false, hidden: true, autocomplete: "off")
11
- field.multi(option_mapper.values) do |builder|
12
- field = builder.field(
13
- label: option_mapper[builder.key],
14
- attributes: {
15
- checked_value: builder.key
16
- }
17
- )
18
- if block_given?
19
- yield field
20
- else
21
- render field.radio_button_tag
22
- render field.label_tag
11
+ div(**attributes.slice(:id, :class)) do
12
+ field.multi(option_mapper.values) do |builder|
13
+ render builder.hidden_field_tag if builder.index == 0
14
+
15
+ field = builder.field(
16
+ label: option_mapper[builder.key],
17
+ # We set the attributes here so they are applied to all input components even if the user decides to use a block
18
+ input_attributes: {
19
+ checked_value: builder.key,
20
+ checked: selected?(builder.key)
21
+ }
22
+ )
23
+ if block_given?
24
+ yield field
25
+ else
26
+ render field.radio_button_tag
27
+ render field.label_tag
28
+ end
23
29
  end
24
30
  end
25
31
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Form
5
+ module Components
6
+ module Concerns
7
+ module HandlesArrayInput
8
+ protected
9
+
10
+ def normalize_input(input_value)
11
+ normalize_array_input(input_value)
12
+ end
13
+
14
+ def normalize_array_input(input_value)
15
+ Array(input_value).map { |nested_input_value| normalize_simple_input(nested_input_value) }.compact
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Form
5
+ module Components
6
+ module Concerns
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
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