phlexi-form 0.3.0.rc1 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +10 -8
- data/gemfiles/default.gemfile.lock +34 -32
- data/gemfiles/rails_7.gemfile.lock +16 -18
- data/lib/phlexi/form/base.rb +42 -41
- data/lib/phlexi/form/builder.rb +297 -0
- data/lib/phlexi/form/components/base.rb +3 -3
- data/lib/phlexi/form/components/collection_checkboxes.rb +1 -1
- data/lib/phlexi/form/components/collection_radio_buttons.rb +1 -1
- data/lib/phlexi/form/components/concerns/extracts_input.rb +53 -0
- data/lib/phlexi/form/components/concerns/handles_input.rb +4 -34
- data/lib/phlexi/form/components/concerns/submits_form.rb +8 -0
- data/lib/phlexi/form/components/error.rb +1 -1
- data/lib/phlexi/form/components/file_input.rb +1 -0
- data/lib/phlexi/form/components/hint.rb +1 -1
- data/lib/phlexi/form/components/input.rb +16 -6
- data/lib/phlexi/form/components/input_array.rb +1 -1
- data/lib/phlexi/form/components/select.rb +4 -3
- data/lib/phlexi/form/components/textarea.rb +2 -3
- data/lib/phlexi/form/html.rb +18 -0
- data/lib/phlexi/form/{field_options → options}/autofocus.rb +1 -1
- data/lib/phlexi/form/{field_options → options}/collection.rb +14 -10
- data/lib/phlexi/form/{field_options → options}/disabled.rb +1 -1
- data/lib/phlexi/form/{field_options → options}/errors.rb +18 -14
- data/lib/phlexi/form/options/hints.rb +13 -0
- data/lib/phlexi/form/options/inferred_types.rb +32 -0
- data/lib/phlexi/form/{field_options → options}/length.rb +3 -3
- data/lib/phlexi/form/{field_options → options}/limit.rb +2 -2
- data/lib/phlexi/form/options/max.rb +55 -0
- data/lib/phlexi/form/options/min.rb +55 -0
- data/lib/phlexi/form/{field_options → options}/pattern.rb +2 -2
- data/lib/phlexi/form/{field_options → options}/readonly.rb +1 -1
- data/lib/phlexi/form/{field_options → options}/required.rb +3 -3
- data/lib/phlexi/form/options/step.rb +39 -0
- data/lib/phlexi/form/options/validators.rb +24 -0
- data/lib/phlexi/form/structure/field_collection.rb +12 -27
- data/lib/phlexi/form/structure/namespace.rb +4 -111
- data/lib/phlexi/form/structure/namespace_collection.rb +1 -32
- data/lib/phlexi/form/theme.rb +160 -0
- data/lib/phlexi/form/version.rb +1 -1
- data/lib/phlexi/form.rb +3 -6
- metadata +36 -24
- data/lib/phlexi/form/field_options/associations.rb +0 -21
- data/lib/phlexi/form/field_options/hints.rb +0 -22
- data/lib/phlexi/form/field_options/inferred_types.rb +0 -155
- data/lib/phlexi/form/field_options/labels.rb +0 -28
- data/lib/phlexi/form/field_options/min_max.rb +0 -92
- data/lib/phlexi/form/field_options/multiple.rb +0 -65
- data/lib/phlexi/form/field_options/placeholder.rb +0 -18
- data/lib/phlexi/form/field_options/themes.rb +0 -207
- data/lib/phlexi/form/field_options/validators.rb +0 -48
- data/lib/phlexi/form/structure/dom.rb +0 -62
- data/lib/phlexi/form/structure/field_builder.rb +0 -236
- data/lib/phlexi/form/structure/node.rb +0 -18
@@ -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 <
|
6
|
+
class Base < Phlexi::Form::HTML
|
7
7
|
attr_reader :field, :attributes
|
8
8
|
|
9
9
|
def initialize(field, **attributes)
|
@@ -23,15 +23,15 @@ module Phlexi
|
|
23
23
|
def append_attribute_classes
|
24
24
|
return if attributes[:class] == false
|
25
25
|
|
26
|
-
|
26
|
+
default_classes = tokens(
|
27
27
|
component_name,
|
28
|
-
attributes[:class],
|
29
28
|
-> { attributes[:required] } => "required",
|
30
29
|
-> { !attributes[:required] } => "optional",
|
31
30
|
-> { field.has_errors? } => "invalid",
|
32
31
|
-> { attributes[:readonly] } => "readonly",
|
33
32
|
-> { attributes[:disabled] } => "disabled"
|
34
33
|
)
|
34
|
+
attributes[:class] = tokens(default_classes, attributes[:class])
|
35
35
|
end
|
36
36
|
|
37
37
|
def component_name
|
@@ -10,7 +10,7 @@ module Phlexi
|
|
10
10
|
|
11
11
|
def view_template
|
12
12
|
div(**attributes.slice(:id, :class)) do
|
13
|
-
field.
|
13
|
+
field.repeated(option_mapper.values) do |builder|
|
14
14
|
render builder.hidden_field_tag if builder.index == 0
|
15
15
|
|
16
16
|
field = builder.field(
|
@@ -9,7 +9,7 @@ module Phlexi
|
|
9
9
|
|
10
10
|
def view_template
|
11
11
|
div(**attributes.slice(:id, :class)) do
|
12
|
-
field.
|
12
|
+
field.repeated(option_mapper.values) do |builder|
|
13
13
|
render builder.hidden_field_tag if builder.index == 0
|
14
14
|
|
15
15
|
field = builder.field(
|
@@ -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
|
@@ -5,46 +5,16 @@ module Phlexi
|
|
5
5
|
module Components
|
6
6
|
module Concerns
|
7
7
|
module HandlesInput
|
8
|
-
|
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
|
8
|
+
include Phlexi::Form::Components::Concerns::ExtractsInput
|
30
9
|
|
31
10
|
protected
|
32
11
|
|
33
12
|
def build_attributes
|
34
13
|
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
14
|
|
46
|
-
|
47
|
-
|
15
|
+
# only overwrite id if it was set in Base
|
16
|
+
attributes[:id] = field.dom.id if attributes[:id] == "#{field.dom.id}_#{component_name}"
|
17
|
+
attributes[:name] = field.dom.name
|
48
18
|
end
|
49
19
|
end
|
50
20
|
end
|
@@ -5,6 +5,14 @@ module Phlexi
|
|
5
5
|
module Components
|
6
6
|
module Concerns
|
7
7
|
module SubmitsForm
|
8
|
+
include Phlexi::Form::Components::Concerns::ExtractsInput
|
9
|
+
|
10
|
+
def extract_input(params)
|
11
|
+
{}
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
8
16
|
def submit_type_value
|
9
17
|
if field.object.respond_to?(:persisted?)
|
10
18
|
field.object.persisted? ? :update : :create
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "time"
|
4
|
+
|
3
5
|
module Phlexi
|
4
6
|
module Form
|
5
7
|
module Components
|
@@ -15,17 +17,13 @@ module Phlexi
|
|
15
17
|
def build_attributes
|
16
18
|
super
|
17
19
|
|
18
|
-
# only overwrite id if it was set in Base
|
19
|
-
attributes[:id] = field.dom.id if attributes[:id] == "#{field.dom.id}_#{component_name}"
|
20
|
-
|
21
|
-
attributes[:name] = field.dom.name
|
22
20
|
attributes[:value] = field.dom.value
|
23
21
|
|
24
22
|
build_input_attributes
|
25
23
|
end
|
26
24
|
|
27
25
|
def build_input_attributes
|
28
|
-
attributes.fetch(:type) { attributes[:type] = field.
|
26
|
+
attributes.fetch(:type) { attributes[:type] = field.inferred_string_field_type }
|
29
27
|
attributes.fetch(:disabled) { attributes[:disabled] = field.disabled? }
|
30
28
|
|
31
29
|
case attributes[:type]
|
@@ -52,12 +50,24 @@ module Phlexi
|
|
52
50
|
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
53
51
|
attributes.fetch(:required) { attributes[:required] = field.required? }
|
54
52
|
attributes.fetch(:multiple) { attributes[:multiple] = field.multiple? }
|
55
|
-
when :date, :time, :
|
53
|
+
when :date, :time, :"datetime-local"
|
56
54
|
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
57
55
|
attributes.fetch(:readonly) { attributes[:readonly] = field.readonly? }
|
58
56
|
attributes.fetch(:required) { attributes[:required] = field.required? }
|
59
57
|
attributes.fetch(:min) { attributes[:min] = field.min }
|
60
58
|
attributes.fetch(:max) { attributes[:max] = field.max }
|
59
|
+
|
60
|
+
# TODO: Investigate if this is Timezone complaint
|
61
|
+
if field.value.respond_to?(:strftime)
|
62
|
+
attributes[:value] = case attributes[:type]
|
63
|
+
when :date
|
64
|
+
field.value.strftime("%Y-%m-%d")
|
65
|
+
when :time
|
66
|
+
field.value.strftime("%H:%M:%S")
|
67
|
+
when :"datetime-local"
|
68
|
+
field.value.strftime("%Y-%m-%dT%H:%M:%S")
|
69
|
+
end
|
70
|
+
end
|
61
71
|
when :color
|
62
72
|
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
63
73
|
when :range
|
@@ -31,9 +31,6 @@ module Phlexi
|
|
31
31
|
def build_attributes
|
32
32
|
super
|
33
33
|
|
34
|
-
attributes[:id] = field.dom.id
|
35
|
-
attributes[:name] = field.dom.name
|
36
|
-
|
37
34
|
build_select_attributes
|
38
35
|
end
|
39
36
|
|
@@ -46,6 +43,10 @@ module Phlexi
|
|
46
43
|
attributes[:disabled] = attributes.fetch(:disabled, field.disabled?)
|
47
44
|
attributes[:multiple] = attributes.fetch(:multiple, field.multiple?)
|
48
45
|
attributes[:size] = attributes.fetch(:size, field.limit)
|
46
|
+
|
47
|
+
if attributes[:multiple]
|
48
|
+
attributes[:name] = "#{attributes[:name].sub(/\[\]$/, "")}[]"
|
49
|
+
end
|
49
50
|
end
|
50
51
|
|
51
52
|
def blank_option_text
|
@@ -4,6 +4,8 @@ module Phlexi
|
|
4
4
|
module Form
|
5
5
|
module Components
|
6
6
|
class Textarea < Base
|
7
|
+
include Concerns::HandlesInput
|
8
|
+
|
7
9
|
def view_template
|
8
10
|
textarea(**attributes) { field.dom.value }
|
9
11
|
end
|
@@ -13,9 +15,6 @@ module Phlexi
|
|
13
15
|
def build_attributes
|
14
16
|
super
|
15
17
|
|
16
|
-
attributes[:id] = field.dom.id
|
17
|
-
attributes[:name] = field.dom.name
|
18
|
-
|
19
18
|
build_textarea_attributes
|
20
19
|
end
|
21
20
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Phlexi
|
2
|
+
module Form
|
3
|
+
class HTML < (defined?(::ApplicationComponent) ? ::ApplicationComponent : Phlex::HTML)
|
4
|
+
module Behaviour
|
5
|
+
protected
|
6
|
+
|
7
|
+
def themed(component, field)
|
8
|
+
base_theme = Phlexi::Form::Theme.instance.resolve_theme(component)
|
9
|
+
validity_theme = Phlexi::Form::Theme.instance.resolve_validity_theme(component, field)
|
10
|
+
|
11
|
+
tokens(base_theme, validity_theme)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
include Behaviour
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
module Phlexi
|
4
4
|
module Form
|
5
|
-
module
|
5
|
+
module Options
|
6
6
|
module Collection
|
7
7
|
def collection(collection = nil)
|
8
8
|
if collection.nil?
|
9
|
-
options
|
9
|
+
options.fetch(:collection) { options[:collection] = infer_collection }
|
10
10
|
else
|
11
11
|
options[:collection] = collection
|
12
12
|
self
|
@@ -16,23 +16,27 @@ module Phlexi
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def infer_collection
|
19
|
+
if object.class.respond_to?(:defined_enums)
|
20
|
+
return object.class.defined_enums.fetch(key.to_s).keys if object.class.defined_enums.key?(key.to_s)
|
21
|
+
end
|
22
|
+
|
19
23
|
collection_value_from_association || collection_value_from_validator
|
20
24
|
end
|
21
25
|
|
22
26
|
def collection_value_from_association
|
23
|
-
return unless
|
27
|
+
return unless association_reflection
|
24
28
|
|
25
|
-
relation =
|
29
|
+
relation = association_reflection.klass.all
|
26
30
|
|
27
|
-
if
|
28
|
-
relation = if
|
29
|
-
|
31
|
+
if association_reflection.respond_to?(:scope) && association_reflection.scope
|
32
|
+
relation = if association_reflection.scope.parameters.any?
|
33
|
+
association_reflection.klass.instance_exec(object, &association_reflection.scope)
|
30
34
|
else
|
31
|
-
|
35
|
+
association_reflection.klass.instance_exec(&association_reflection.scope)
|
32
36
|
end
|
33
37
|
else
|
34
|
-
order =
|
35
|
-
conditions =
|
38
|
+
order = association_reflection.options[:order]
|
39
|
+
conditions = association_reflection.options[:conditions]
|
36
40
|
conditions = object.instance_exec(&conditions) if conditions.respond_to?(:call)
|
37
41
|
|
38
42
|
relation = relation.where(conditions) if relation.respond_to?(:where) && conditions.present?
|