phlexi-form 0.2.0 → 0.3.0

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