phlexi-form 0.3.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/gemfiles/default.gemfile.lock +34 -32
  4. data/gemfiles/rails_7.gemfile.lock +16 -18
  5. data/lib/phlexi/form/base.rb +18 -9
  6. data/lib/phlexi/form/builder.rb +297 -0
  7. data/lib/phlexi/form/components/base.rb +1 -1
  8. data/lib/phlexi/form/components/input.rb +16 -2
  9. data/lib/phlexi/form/components/select.rb +4 -0
  10. data/lib/phlexi/form/html.rb +18 -0
  11. data/lib/phlexi/form/{field_options → options}/autofocus.rb +1 -1
  12. data/lib/phlexi/form/{field_options → options}/collection.rb +6 -2
  13. data/lib/phlexi/form/{field_options → options}/disabled.rb +1 -1
  14. data/lib/phlexi/form/{field_options → options}/errors.rb +11 -11
  15. data/lib/phlexi/form/options/hints.rb +13 -0
  16. data/lib/phlexi/form/options/inferred_types.rb +32 -0
  17. data/lib/phlexi/form/{field_options → options}/length.rb +3 -3
  18. data/lib/phlexi/form/{field_options → options}/limit.rb +2 -2
  19. data/lib/phlexi/form/options/max.rb +55 -0
  20. data/lib/phlexi/form/options/min.rb +55 -0
  21. data/lib/phlexi/form/{field_options → options}/pattern.rb +2 -2
  22. data/lib/phlexi/form/{field_options → options}/readonly.rb +1 -1
  23. data/lib/phlexi/form/{field_options → options}/required.rb +2 -2
  24. data/lib/phlexi/form/options/step.rb +39 -0
  25. data/lib/phlexi/form/options/validators.rb +24 -0
  26. data/lib/phlexi/form/structure/field_collection.rb +9 -29
  27. data/lib/phlexi/form/structure/namespace.rb +2 -114
  28. data/lib/phlexi/form/structure/namespace_collection.rb +1 -32
  29. data/lib/phlexi/form/theme.rb +160 -0
  30. data/lib/phlexi/form/version.rb +1 -1
  31. data/lib/phlexi/form.rb +3 -6
  32. metadata +34 -23
  33. data/lib/phlexi/form/field_options/associations.rb +0 -21
  34. data/lib/phlexi/form/field_options/hints.rb +0 -26
  35. data/lib/phlexi/form/field_options/inferred_types.rb +0 -159
  36. data/lib/phlexi/form/field_options/labels.rb +0 -28
  37. data/lib/phlexi/form/field_options/min_max.rb +0 -92
  38. data/lib/phlexi/form/field_options/multiple.rb +0 -65
  39. data/lib/phlexi/form/field_options/placeholder.rb +0 -18
  40. data/lib/phlexi/form/field_options/themes.rb +0 -207
  41. data/lib/phlexi/form/field_options/validators.rb +0 -48
  42. data/lib/phlexi/form/structure/dom.rb +0 -62
  43. data/lib/phlexi/form/structure/field_builder.rb +0 -243
  44. data/lib/phlexi/form/structure/node.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d20cf0d6ccfcdf3cbb3a05a518b25a9b6176ef827e32f9cab47fddb43391f470
4
- data.tar.gz: '0788efe146100a6629f679d913a080ff7cc2039881477bc9ffda16dfe0f6dc9f'
3
+ metadata.gz: aa37fb3ecaded18bae5e7be4d2773c227acc970c588eceb276214f39597cf5cf
4
+ data.tar.gz: cc06f9e6d21d87e66efe7eb4a27fd1b2e9721b14f9dab5b1ab7df5c74ff58834
5
5
  SHA512:
6
- metadata.gz: 3e671e22b8daa6e5e243725f79302e26b9b1d5395d92a54013d0aed981bc9eb2dfd756d8214a69446e0328186b339dc21dd3e1853c5236270f048e974c4ae114
7
- data.tar.gz: d7441ae010f74f188baf43b6ac90e5cdd49b88563a22261e223b11d3c20de6135e5f610c0f278eaae28d49bc3627df3ec0b3012fe49d5e41626751d15200fd26
6
+ metadata.gz: d37efe7ef9526991cce1495180edeacab2322c499302dab5e1118731bf2410e708e1404a6dfc1c3bebaf7846366fa05c57acc94669cc6caf60a798159ff9bf8b
7
+ data.tar.gz: ec2a45b47b89e0215cf034584d7d1d7cd5f60aa3fc640447492ba184a01069150879ddb101e7616b71fbde770222701df50ac96f0361e7a848967bc17c405885
data/README.md CHANGED
@@ -159,7 +159,7 @@ Phlexi::Form supports theming through a flexible theming system:
159
159
 
160
160
  ```ruby
161
161
  class ThemedForm < Phlexi::Form::Base
162
- class FieldBuilder < FieldBuilder
162
+ class Builder < Builder
163
163
  private
164
164
 
165
165
  def default_theme
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- phlexi-form (0.3.0.rc1)
4
+ phlexi-form (0.3.1)
5
5
  activesupport
6
6
  phlex (~> 1.11)
7
7
  zeitwerk
@@ -9,32 +9,34 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- actionpack (7.1.3.4)
13
- actionview (= 7.1.3.4)
14
- activesupport (= 7.1.3.4)
12
+ actionpack (7.2.1)
13
+ actionview (= 7.2.1)
14
+ activesupport (= 7.2.1)
15
15
  nokogiri (>= 1.8.5)
16
16
  racc
17
- rack (>= 2.2.4)
17
+ rack (>= 2.2.4, < 3.2)
18
18
  rack-session (>= 1.0.1)
19
19
  rack-test (>= 0.6.3)
20
20
  rails-dom-testing (~> 2.2)
21
21
  rails-html-sanitizer (~> 1.6)
22
- actionview (7.1.3.4)
23
- activesupport (= 7.1.3.4)
22
+ useragent (~> 0.16)
23
+ actionview (7.2.1)
24
+ activesupport (= 7.2.1)
24
25
  builder (~> 3.1)
25
26
  erubi (~> 1.11)
26
27
  rails-dom-testing (~> 2.2)
27
28
  rails-html-sanitizer (~> 1.6)
28
- activesupport (7.1.3.4)
29
+ activesupport (7.2.1)
29
30
  base64
30
31
  bigdecimal
31
- concurrent-ruby (~> 1.0, >= 1.0.2)
32
+ concurrent-ruby (~> 1.0, >= 1.3.1)
32
33
  connection_pool (>= 2.2.5)
33
34
  drb
34
35
  i18n (>= 1.6, < 2)
36
+ logger (>= 1.4.2)
35
37
  minitest (>= 5.1)
36
- mutex_m
37
- tzinfo (~> 2.0)
38
+ securerandom (>= 0.3)
39
+ tzinfo (~> 2.0, >= 2.0.5)
38
40
  addressable (2.8.7)
39
41
  public_suffix (>= 2.0.2, < 7.0)
40
42
  ansi (1.5.0)
@@ -48,7 +50,7 @@ GEM
48
50
  builder (3.3.0)
49
51
  bundle-audit (0.1.0)
50
52
  bundler-audit
51
- bundler-audit (0.9.1)
53
+ bundler-audit (0.9.2)
52
54
  bundler (>= 1.2.0, < 3)
53
55
  thor (~> 1.0)
54
56
  capybara (3.40.0)
@@ -64,7 +66,7 @@ GEM
64
66
  activesupport (>= 3.0.0)
65
67
  railties (>= 3.0.0)
66
68
  thor (>= 0.14.6)
67
- concurrent-ruby (1.3.3)
69
+ concurrent-ruby (1.3.4)
68
70
  connection_pool (2.4.1)
69
71
  crass (1.0.6)
70
72
  drb (2.2.1)
@@ -78,22 +80,22 @@ GEM
78
80
  json (2.7.2)
79
81
  language_server-protocol (3.17.0.3)
80
82
  lint_roller (1.1.0)
83
+ logger (1.6.1)
81
84
  loofah (2.22.0)
82
85
  crass (~> 1.0.2)
83
86
  nokogiri (>= 1.12.0)
84
87
  matrix (0.4.2)
85
88
  mini_mime (1.1.5)
86
- minitest (5.24.1)
89
+ minitest (5.25.1)
87
90
  minitest-reporters (1.7.1)
88
91
  ansi
89
92
  builder
90
93
  minitest (>= 5.0)
91
94
  ruby-progressbar
92
- mutex_m (0.2.0)
93
95
  nokogiri (1.16.7-x86_64-darwin)
94
96
  racc (~> 1.4)
95
- parallel (1.25.1)
96
- parser (3.3.4.0)
97
+ parallel (1.26.3)
98
+ parser (3.3.5.0)
97
99
  ast (~> 2.4.1)
98
100
  racc
99
101
  phlex (1.11.0)
@@ -119,10 +121,10 @@ GEM
119
121
  rails-html-sanitizer (1.6.0)
120
122
  loofah (~> 2.21)
121
123
  nokogiri (~> 1.14)
122
- railties (7.1.3.4)
123
- actionpack (= 7.1.3.4)
124
- activesupport (= 7.1.3.4)
125
- irb
124
+ railties (7.2.1)
125
+ actionpack (= 7.2.1)
126
+ activesupport (= 7.2.1)
127
+ irb (~> 1.13)
126
128
  rackup (>= 1.0.0)
127
129
  rake (>= 12.2)
128
130
  thor (~> 1.0, >= 1.2.2)
@@ -132,31 +134,31 @@ GEM
132
134
  rdoc (6.7.0)
133
135
  psych (>= 4.0.0)
134
136
  regexp_parser (2.9.2)
135
- reline (0.5.9)
137
+ reline (0.5.10)
136
138
  io-console (~> 0.5)
137
- rexml (3.3.2)
138
- strscan
139
- rubocop (1.64.1)
139
+ rexml (3.3.7)
140
+ rubocop (1.65.1)
140
141
  json (~> 2.3)
141
142
  language_server-protocol (>= 3.17.0)
142
143
  parallel (~> 1.10)
143
144
  parser (>= 3.3.0.2)
144
145
  rainbow (>= 2.2.2, < 4.0)
145
- regexp_parser (>= 1.8, < 3.0)
146
+ regexp_parser (>= 2.4, < 3.0)
146
147
  rexml (>= 3.2.5, < 4.0)
147
148
  rubocop-ast (>= 1.31.1, < 2.0)
148
149
  ruby-progressbar (~> 1.7)
149
150
  unicode-display_width (>= 2.4.0, < 3.0)
150
- rubocop-ast (1.31.3)
151
+ rubocop-ast (1.32.3)
151
152
  parser (>= 3.3.1.0)
152
153
  rubocop-performance (1.21.1)
153
154
  rubocop (>= 1.48.1, < 2.0)
154
155
  rubocop-ast (>= 1.31.1, < 2.0)
155
156
  ruby-progressbar (1.13.0)
156
- standard (1.39.2)
157
+ securerandom (0.3.1)
158
+ standard (1.40.0)
157
159
  language_server-protocol (~> 3.17.0.2)
158
160
  lint_roller (~> 1.0)
159
- rubocop (~> 1.64.0)
161
+ rubocop (~> 1.65.0)
160
162
  standard-custom (~> 1.0.0)
161
163
  standard-performance (~> 1.4)
162
164
  standard-custom (1.0.2)
@@ -166,15 +168,15 @@ GEM
166
168
  lint_roller (~> 1.1)
167
169
  rubocop-performance (~> 1.21.0)
168
170
  stringio (3.1.1)
169
- strscan (3.1.0)
170
- thor (1.3.1)
171
+ thor (1.3.2)
171
172
  tzinfo (2.0.6)
172
173
  concurrent-ruby (~> 1.0)
173
174
  unicode-display_width (2.5.0)
175
+ useragent (0.16.10)
174
176
  webrick (1.8.1)
175
177
  xpath (3.2.0)
176
178
  nokogiri (~> 1.8)
177
- zeitwerk (2.6.17)
179
+ zeitwerk (2.6.18)
178
180
 
179
181
  PLATFORMS
180
182
  x86_64-darwin
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- phlexi-form (0.3.0.rc1)
4
+ phlexi-form (0.3.1)
5
5
  activesupport
6
6
  phlex (~> 1.11)
7
7
  zeitwerk
@@ -96,7 +96,7 @@ GEM
96
96
  builder (3.3.0)
97
97
  bundle-audit (0.1.0)
98
98
  bundler-audit
99
- bundler-audit (0.9.1)
99
+ bundler-audit (0.9.2)
100
100
  bundler (>= 1.2.0, < 3)
101
101
  thor (~> 1.0)
102
102
  capybara (3.40.0)
@@ -112,7 +112,7 @@ GEM
112
112
  activesupport (>= 3.0.0)
113
113
  railties (>= 3.0.0)
114
114
  thor (>= 0.14.6)
115
- concurrent-ruby (1.3.3)
115
+ concurrent-ruby (1.3.4)
116
116
  connection_pool (2.4.1)
117
117
  crass (1.0.6)
118
118
  date (3.3.4)
@@ -140,14 +140,14 @@ GEM
140
140
  marcel (1.0.4)
141
141
  matrix (0.4.2)
142
142
  mini_mime (1.1.5)
143
- minitest (5.24.1)
143
+ minitest (5.25.1)
144
144
  minitest-reporters (1.7.1)
145
145
  ansi
146
146
  builder
147
147
  minitest (>= 5.0)
148
148
  ruby-progressbar
149
149
  mutex_m (0.2.0)
150
- net-imap (0.4.14)
150
+ net-imap (0.4.16)
151
151
  date
152
152
  net-protocol
153
153
  net-pop (0.1.2)
@@ -159,8 +159,8 @@ GEM
159
159
  nio4r (2.7.3)
160
160
  nokogiri (1.16.7-x86_64-darwin)
161
161
  racc (~> 1.4)
162
- parallel (1.25.1)
163
- parser (3.3.4.0)
162
+ parallel (1.26.3)
163
+ parser (3.3.5.0)
164
164
  ast (~> 2.4.1)
165
165
  racc
166
166
  phlex (1.11.0)
@@ -213,32 +213,31 @@ GEM
213
213
  rdoc (6.7.0)
214
214
  psych (>= 4.0.0)
215
215
  regexp_parser (2.9.2)
216
- reline (0.5.9)
216
+ reline (0.5.10)
217
217
  io-console (~> 0.5)
218
- rexml (3.3.2)
219
- strscan
220
- rubocop (1.64.1)
218
+ rexml (3.3.7)
219
+ rubocop (1.65.1)
221
220
  json (~> 2.3)
222
221
  language_server-protocol (>= 3.17.0)
223
222
  parallel (~> 1.10)
224
223
  parser (>= 3.3.0.2)
225
224
  rainbow (>= 2.2.2, < 4.0)
226
- regexp_parser (>= 1.8, < 3.0)
225
+ regexp_parser (>= 2.4, < 3.0)
227
226
  rexml (>= 3.2.5, < 4.0)
228
227
  rubocop-ast (>= 1.31.1, < 2.0)
229
228
  ruby-progressbar (~> 1.7)
230
229
  unicode-display_width (>= 2.4.0, < 3.0)
231
- rubocop-ast (1.31.3)
230
+ rubocop-ast (1.32.3)
232
231
  parser (>= 3.3.1.0)
233
232
  rubocop-performance (1.21.1)
234
233
  rubocop (>= 1.48.1, < 2.0)
235
234
  rubocop-ast (>= 1.31.1, < 2.0)
236
235
  ruby-progressbar (1.13.0)
237
236
  sqlite3 (1.7.3-x86_64-darwin)
238
- standard (1.39.2)
237
+ standard (1.40.0)
239
238
  language_server-protocol (~> 3.17.0.2)
240
239
  lint_roller (~> 1.0)
241
- rubocop (~> 1.64.0)
240
+ rubocop (~> 1.65.0)
242
241
  standard-custom (~> 1.0.0)
243
242
  standard-performance (~> 1.4)
244
243
  standard-custom (1.0.2)
@@ -248,8 +247,7 @@ GEM
248
247
  lint_roller (~> 1.1)
249
248
  rubocop-performance (~> 1.21.0)
250
249
  stringio (3.1.1)
251
- strscan (3.1.0)
252
- thor (1.3.1)
250
+ thor (1.3.2)
253
251
  timeout (0.4.1)
254
252
  tzinfo (2.0.6)
255
253
  concurrent-ruby (~> 1.0)
@@ -260,7 +258,7 @@ GEM
260
258
  websocket-extensions (0.1.5)
261
259
  xpath (3.2.0)
262
260
  nokogiri (~> 1.8)
263
- zeitwerk (2.6.17)
261
+ zeitwerk (2.6.18)
264
262
 
265
263
  PLATFORMS
266
264
  x86_64-darwin
@@ -17,10 +17,18 @@ module Phlexi
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 < COMPONENT_BASE
21
- class Namespace < Structure::Namespace; end
20
+ class Base < Phlexi::Form::HTML
21
+ class Namespace < Phlexi::Form::Structure::Namespace; end
22
22
 
23
- class FieldBuilder < Structure::FieldBuilder; end
23
+ class Builder < Phlexi::Form::Builder; end
24
+
25
+ def self.inline(*, **, &block)
26
+ raise ArgumentError, "block is required" unless block
27
+
28
+ new(*, **) do |f|
29
+ f.instance_exec(&block)
30
+ end
31
+ end
24
32
 
25
33
  attr_reader :key, :object
26
34
 
@@ -39,7 +47,6 @@ module Phlexi
39
47
  def initialize(record, action: nil, method: nil, attributes: {}, **options)
40
48
  @form_action = action
41
49
  @form_method = method
42
- @form_class = options.delete(:class)
43
50
  @attributes = attributes
44
51
  @namespace_klass = options.delete(:namespace_klass) || default_namespace_klass
45
52
  @builder_klass = options.delete(:builder_klass) || default_builder_klass
@@ -53,8 +60,8 @@ module Phlexi
53
60
  # Renders the form template.
54
61
  #
55
62
  # @return [void]
56
- def view_template
57
- form_tag { form_template }
63
+ def view_template(&)
64
+ form_tag { form_template(&) }
58
65
  end
59
66
 
60
67
  # Executes the form's content block.
@@ -62,7 +69,7 @@ module Phlexi
62
69
  #
63
70
  # @return [void]
64
71
  def form_template
65
- instance_exec(&@_content_block) if @_content_block
72
+ yield if block_given?
66
73
  end
67
74
 
68
75
  def extract_input(params)
@@ -117,7 +124,9 @@ module Phlexi
117
124
  # Retrieves the form's CSS classes.
118
125
  #
119
126
  # @return [String] The form's CSS classes
120
- attr_reader :form_class
127
+ def form_class
128
+ themed(:base, nil)
129
+ end
121
130
 
122
131
  # Renders the form tag with its contents.
123
132
  #
@@ -236,7 +245,7 @@ module Phlexi
236
245
  end
237
246
 
238
247
  def default_builder_klass
239
- self.class::FieldBuilder
248
+ self.class::Builder
240
249
  end
241
250
  end
242
251
  end
@@ -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)