phlexi-form 0.2.0 → 0.3.0.rc1

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