phlexi-form 0.2.0 → 0.3.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +4 -9
  3. data/README.md +117 -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 +65 -56
  10. data/lib/phlexi/form/components/base.rb +14 -8
  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/extracts_input.rb +53 -0
  15. data/lib/phlexi/form/components/concerns/handles_array_input.rb +21 -0
  16. data/lib/phlexi/form/components/concerns/handles_input.rb +23 -0
  17. data/lib/phlexi/form/components/concerns/has_options.rb +6 -2
  18. data/lib/phlexi/form/components/concerns/submits_form.rb +47 -0
  19. data/lib/phlexi/form/components/error.rb +1 -1
  20. data/lib/phlexi/form/components/file_input.rb +33 -0
  21. data/lib/phlexi/form/components/hint.rb +1 -1
  22. data/lib/phlexi/form/components/input.rb +38 -36
  23. data/lib/phlexi/form/components/input_array.rb +45 -0
  24. data/lib/phlexi/form/components/label.rb +2 -1
  25. data/lib/phlexi/form/components/radio_button.rb +11 -1
  26. data/lib/phlexi/form/components/select.rb +21 -8
  27. data/lib/phlexi/form/components/submit_button.rb +41 -0
  28. data/lib/phlexi/form/components/textarea.rb +2 -3
  29. data/lib/phlexi/form/field_options/associations.rb +21 -0
  30. data/lib/phlexi/form/field_options/autofocus.rb +1 -1
  31. data/lib/phlexi/form/field_options/collection.rb +26 -9
  32. data/lib/phlexi/form/field_options/errors.rb +17 -3
  33. data/lib/phlexi/form/field_options/hints.rb +5 -1
  34. data/lib/phlexi/form/field_options/{type.rb → inferred_types.rb} +21 -17
  35. data/lib/phlexi/form/field_options/multiple.rb +2 -0
  36. data/lib/phlexi/form/field_options/required.rb +1 -1
  37. data/lib/phlexi/form/field_options/themes.rb +207 -0
  38. data/lib/phlexi/form/field_options/validators.rb +2 -2
  39. data/lib/phlexi/form/option_mapper.rb +2 -2
  40. data/lib/phlexi/form/structure/dom.rb +21 -16
  41. data/lib/phlexi/form/structure/field_builder.rb +165 -121
  42. data/lib/phlexi/form/structure/field_collection.rb +20 -6
  43. data/lib/phlexi/form/structure/namespace.rb +48 -31
  44. data/lib/phlexi/form/structure/namespace_collection.rb +20 -20
  45. data/lib/phlexi/form/structure/node.rb +13 -3
  46. data/lib/phlexi/form/version.rb +1 -1
  47. data/lib/phlexi/form.rb +4 -1
  48. metadata +32 -7
  49. data/CODE_OF_CONDUCT.md +0 -84
@@ -0,0 +1,282 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ phlexi-form (0.3.0.rc1)
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,30 @@
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(user, action: '/users', method: 'post') do
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
22
24
 
23
25
  attr_reader :key, :object
24
26
 
25
- delegate :field, :nest_one, :nest_many, to: :@namespace
27
+ delegate :field, :submit_button, :nest_one, :nest_many, to: :@namespace
26
28
 
27
29
  # Initializes a new Form instance.
28
30
  #
@@ -39,6 +41,8 @@ module Phlexi
39
41
  @form_method = method
40
42
  @form_class = options.delete(:class)
41
43
  @attributes = attributes
44
+ @namespace_klass = options.delete(:namespace_klass) || default_namespace_klass
45
+ @builder_klass = options.delete(:builder_klass) || default_builder_klass
42
46
  @options = options
43
47
 
44
48
  initialize_object_and_key(record)
@@ -61,40 +65,36 @@ module Phlexi
61
65
  instance_exec(&@_content_block) if @_content_block
62
66
  end
63
67
 
64
- # Renders the form tag with its contents.
65
- #
66
- # @yield The form's content
67
- # @return [void]
68
- def form_tag(&block)
69
- form(**form_attributes) do
70
- render_hidden_method_field
71
- render_authenticity_token if authenticity_token?
72
- yield
73
- end
68
+ def extract_input(params)
69
+ call unless @_rendered
70
+ @namespace.extract_input(params)
74
71
  end
75
72
 
76
73
  protected
77
74
 
78
- attr_reader :options, :attributes
75
+ attr_reader :options, :attributes, :namespace_klass, :builder_klass
79
76
 
80
77
  # Initializes the object and key based on the given record.
81
78
  #
82
79
  # @param record [ActiveModel::Model, Symbol, String] The form's associated record or key
83
80
  # @return [void]
84
81
  def initialize_object_and_key(record)
82
+ # always pop these keys
83
+ # add support for `as` to make it more rails friendly
84
+ @key = options.delete(:key) || options.delete(:as)
85
+
85
86
  case record
86
87
  when String, Symbol
87
88
  @object = nil
88
89
  @key = record
89
90
  else
90
- @object = convert_to_model(record)
91
- @key = options.delete(:as)
92
-
91
+ @object = record
93
92
  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"
93
+ @key = if object.respond_to?(:model_name) && object.model_name.respond_to?(:param_key) && object.model_name.param_key.present?
94
+ object.model_name.param_key
95
+ else
96
+ object.class.name.demodulize.underscore
96
97
  end
97
- @key = object.model_name.param_key
98
98
  end
99
99
  end
100
100
  @key = @key.to_sym
@@ -111,7 +111,24 @@ module Phlexi
111
111
  #
112
112
  # @return [void]
113
113
  def initialize_attributes
114
- attributes[:accept_charset] ||= "UTF-8"
114
+ attributes.fetch(:accept_charset) { attributes[:accept_charset] = "UTF-8" }
115
+ end
116
+
117
+ # Retrieves the form's CSS classes.
118
+ #
119
+ # @return [String] The form's CSS classes
120
+ attr_reader :form_class
121
+
122
+ # Renders the form tag with its contents.
123
+ #
124
+ # @yield The form's content
125
+ # @return [void]
126
+ def form_tag(&)
127
+ form(**form_attributes) do
128
+ render_hidden_method_field
129
+ render_authenticity_token if has_authenticity_token?
130
+ yield
131
+ end
115
132
  end
116
133
 
117
134
  # Determines the form's action URL.
@@ -137,18 +154,11 @@ module Phlexi
137
154
  ActiveSupport::StringInquirer.new(@form_method)
138
155
  end
139
156
 
140
- # Retrieves the form's CSS classes.
141
- #
142
- # @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
146
-
147
157
  # Checks if the authenticity token should be included.
148
158
  #
149
159
  # @return [Boolean] True if the authenticity token should be included, false otherwise
150
- def authenticity_token?
151
- defined?(helpers) && options.fetch(:authenticity_token) { !form_method.get? }
160
+ def has_authenticity_token?
161
+ !form_method.get? && ((defined?(helpers) && helpers) || options[:authenticity_token])
152
162
  end
153
163
 
154
164
  # Retrieves the authenticity token.
@@ -171,22 +181,11 @@ module Phlexi
171
181
  #
172
182
  # @return [String, nil] The appropriate form method
173
183
  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
184
+ if object.respond_to?(:persisted?)
185
+ object.persisted? ? "patch" : "post"
186
+ elsif object.present?
187
+ "post"
188
+ end
190
189
  end
191
190
 
192
191
  # Renders the hidden method field for non-standard HTTP methods.
@@ -215,12 +214,12 @@ module Phlexi
215
214
  #
216
215
  # @return [Hash] The form attributes
217
216
  def form_attributes
218
- {
219
- action: form_action,
220
- method: standardized_form_method,
217
+ mix({
218
+ id: @namespace.dom_id,
221
219
  class: form_class,
222
- **attributes
223
- }
220
+ action: form_action,
221
+ method: standardized_form_method
222
+ }, attributes)
224
223
  end
225
224
 
226
225
  # Renders the authenticity token if required.
@@ -229,6 +228,16 @@ module Phlexi
229
228
  def render_authenticity_token
230
229
  authenticity_token_field
231
230
  end
231
+
232
+ private
233
+
234
+ def default_namespace_klass
235
+ self.class::Namespace
236
+ end
237
+
238
+ def default_builder_klass
239
+ self.class::FieldBuilder
240
+ end
232
241
  end
233
242
  end
234
243
  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,21 +11,27 @@ 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[:class] = tokens(
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
+
26
+ default_classes = tokens(
21
27
  component_name,
22
- attributes[:class],
23
- -> { field.required? } => "required",
24
- -> { !field.required? } => "optional",
28
+ -> { attributes[:required] } => "required",
29
+ -> { !attributes[:required] } => "optional",
25
30
  -> { field.has_errors? } => "invalid",
26
- -> { field.readonly? } => "readonly",
27
- -> { field.disabled? } => "disabled"
31
+ -> { attributes[:readonly] } => "readonly",
32
+ -> { attributes[:disabled] } => "disabled"
28
33
  )
34
+ attributes[:class] = tokens(default_classes, attributes[:class])
29
35
  end
30
36
 
31
37
  def component_name
@@ -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.repeated(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.repeated(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,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