form_props 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +714 -0
- data/lib/form_props/action_view_extensions/form_helper.rb +120 -0
- data/lib/form_props/form_builder.rb +223 -0
- data/lib/form_props/form_options_helper.rb +158 -0
- data/lib/form_props/inputs/base.rb +164 -0
- data/lib/form_props/inputs/check_box.rb +69 -0
- data/lib/form_props/inputs/collection_check_boxes.rb +36 -0
- data/lib/form_props/inputs/collection_helpers.rb +53 -0
- data/lib/form_props/inputs/collection_radio_buttons.rb +35 -0
- data/lib/form_props/inputs/collection_select.rb +30 -0
- data/lib/form_props/inputs/color_field.rb +27 -0
- data/lib/form_props/inputs/date_field.rb +17 -0
- data/lib/form_props/inputs/datetime_field.rb +32 -0
- data/lib/form_props/inputs/datetime_local_field.rb +17 -0
- data/lib/form_props/inputs/email_field.rb +13 -0
- data/lib/form_props/inputs/file_field.rb +13 -0
- data/lib/form_props/inputs/grouped_collection_select.rb +31 -0
- data/lib/form_props/inputs/hidden_field.rb +16 -0
- data/lib/form_props/inputs/month_field.rb +17 -0
- data/lib/form_props/inputs/number_field.rb +21 -0
- data/lib/form_props/inputs/password_field.rb +18 -0
- data/lib/form_props/inputs/radio_button.rb +48 -0
- data/lib/form_props/inputs/range_field.rb +13 -0
- data/lib/form_props/inputs/search_field.rb +28 -0
- data/lib/form_props/inputs/select.rb +42 -0
- data/lib/form_props/inputs/submit.rb +26 -0
- data/lib/form_props/inputs/tel_field.rb +13 -0
- data/lib/form_props/inputs/text_area.rb +37 -0
- data/lib/form_props/inputs/text_field.rb +28 -0
- data/lib/form_props/inputs/time_field.rb +17 -0
- data/lib/form_props/inputs/time_zone_select.rb +22 -0
- data/lib/form_props/inputs/url_field.rb +13 -0
- data/lib/form_props/inputs/week_field.rb +17 -0
- data/lib/form_props/inputs/weekday_select.rb +28 -0
- data/lib/form_props/version.rb +5 -0
- data/lib/form_props.rb +45 -0
- metadata +120 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module ActionViewExtensions
|
5
|
+
module FormHelper
|
6
|
+
def form_props(model: nil, scope: nil, url: nil, format: nil, **options, &block)
|
7
|
+
json = @__json
|
8
|
+
|
9
|
+
options = {
|
10
|
+
allow_method_names_outside_object: true,
|
11
|
+
controlled: false
|
12
|
+
}.merge!(options)
|
13
|
+
|
14
|
+
options.delete(:remote)
|
15
|
+
|
16
|
+
options[:local] = true
|
17
|
+
options[:skip_default_ids] = false
|
18
|
+
|
19
|
+
if model
|
20
|
+
if url != false
|
21
|
+
url ||= if format.nil?
|
22
|
+
polymorphic_path(model, {})
|
23
|
+
else
|
24
|
+
polymorphic_path(model, format: format)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
model = convert_to_model(_object_for_form_builder(model))
|
29
|
+
scope ||= model_name_from_record_or_class(model).param_key
|
30
|
+
end
|
31
|
+
|
32
|
+
if block
|
33
|
+
options[:builder] = FormProps::FormBuilder
|
34
|
+
|
35
|
+
builder = instantiate_builder(scope, model, options)
|
36
|
+
json.inputs do
|
37
|
+
capture(builder, &block)
|
38
|
+
end
|
39
|
+
options[:multipart] ||= builder.multipart?
|
40
|
+
end
|
41
|
+
|
42
|
+
html_options = html_options_for_form_with(url, model, **options)
|
43
|
+
|
44
|
+
json.extras do
|
45
|
+
extra_props_for_form(json, html_options)
|
46
|
+
end
|
47
|
+
json.props(html_options)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def token_props(json, token = nil, form_options: {})
|
53
|
+
if token != false && defined?(protect_against_forgery?) && protect_against_forgery?
|
54
|
+
token =
|
55
|
+
if token == true || token.nil?
|
56
|
+
form_authenticity_token(form_options: form_options.merge(authenticity_token: token))
|
57
|
+
else
|
58
|
+
token
|
59
|
+
end
|
60
|
+
|
61
|
+
json.set!("csrf") do
|
62
|
+
json.name request_forgery_protection_token.to_s
|
63
|
+
json.type "hidden"
|
64
|
+
json.default_value token
|
65
|
+
json.autocomplete "off"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def method_props(json, method)
|
71
|
+
json.set!("method") do
|
72
|
+
json.name "_method"
|
73
|
+
json.type "hidden"
|
74
|
+
json.default_value method.to_s
|
75
|
+
json.autocomplete "off"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def utf8_enforcer_props(json)
|
80
|
+
json.set!("utf8") do
|
81
|
+
json.name "utf8"
|
82
|
+
json.type "hidden"
|
83
|
+
json.default_value "✓"
|
84
|
+
json.autocomplete "off"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def extra_props_for_form(json, html_options)
|
89
|
+
authenticity_token = html_options.delete("authenticity_token")
|
90
|
+
method = html_options.delete("method").to_s.downcase
|
91
|
+
|
92
|
+
case method
|
93
|
+
when "get"
|
94
|
+
html_options["method"] = "get"
|
95
|
+
when "post", ""
|
96
|
+
html_options["method"] = "post"
|
97
|
+
token_props(json, authenticity_token, form_options: {
|
98
|
+
action: html_options["action"],
|
99
|
+
method: "post"
|
100
|
+
})
|
101
|
+
else
|
102
|
+
html_options["method"] = "post"
|
103
|
+
method_props(json, method)
|
104
|
+
token_props(json, authenticity_token, form_options: {
|
105
|
+
action: html_options["action"],
|
106
|
+
method: method
|
107
|
+
})
|
108
|
+
end
|
109
|
+
|
110
|
+
if html_options.delete("enforce_utf8") { default_enforce_utf8 }
|
111
|
+
utf8_enforcer_props(json)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
ActiveSupport.on_load(:action_view) do
|
119
|
+
include FormProps::ActionViewExtensions::FormHelper
|
120
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
5
|
+
undef_method :button,
|
6
|
+
:label,
|
7
|
+
:datetime_select,
|
8
|
+
:time_select,
|
9
|
+
:date_select
|
10
|
+
# :rich_text_area
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
options = args.last || {}
|
15
|
+
@default_options[:controlled] = options[:controlled]
|
16
|
+
@default_html_options = @default_html_options.except(:controlled)
|
17
|
+
end
|
18
|
+
|
19
|
+
def file_field(method, options = {})
|
20
|
+
Inputs::FileField.new(
|
21
|
+
@object_name,
|
22
|
+
method,
|
23
|
+
@template,
|
24
|
+
@template.send(:convert_direct_upload_option_to_url, objectify_options(options).dup)
|
25
|
+
).render
|
26
|
+
end
|
27
|
+
|
28
|
+
def select(method, choices = [], options = {}, html_options = {})
|
29
|
+
Inputs::Select.new(@object_name, method, @template, choices, objectify_options(options), @default_html_options.merge(html_options)).render
|
30
|
+
end
|
31
|
+
|
32
|
+
def hidden_field(method, options = {})
|
33
|
+
Inputs::HiddenField.new(@object_name, method, @template, objectify_options(options)).render
|
34
|
+
end
|
35
|
+
|
36
|
+
def text_field(method, options = {})
|
37
|
+
Inputs::TextField.new(@object_name, method, @template, objectify_options(options)).render
|
38
|
+
end
|
39
|
+
|
40
|
+
def date_field(method, options = {})
|
41
|
+
Inputs::DateField.new(@object_name, method, @template, objectify_options(options)).render
|
42
|
+
end
|
43
|
+
|
44
|
+
def time_field(method, options = {})
|
45
|
+
Inputs::TimeField.new(@object_name, method, @template, objectify_options(options)).render
|
46
|
+
end
|
47
|
+
|
48
|
+
def week_field(method, options = {})
|
49
|
+
Inputs::WeekField.new(@object_name, method, @template, objectify_options(options)).render
|
50
|
+
end
|
51
|
+
|
52
|
+
def month_field(method, options = {})
|
53
|
+
Inputs::MonthField.new(@object_name, method, @template, objectify_options(options)).render
|
54
|
+
end
|
55
|
+
|
56
|
+
def datetime_field(method, options = {})
|
57
|
+
Inputs::DatetimeLocalField.new(@object_name, method, @template, objectify_options(options)).render
|
58
|
+
end
|
59
|
+
|
60
|
+
def datetime_local_field(method, options = {})
|
61
|
+
Inputs::DatetimeLocalField.new(@object_name, method, @template, objectify_options(options)).render
|
62
|
+
end
|
63
|
+
|
64
|
+
def url_field(method, options = {})
|
65
|
+
Inputs::UrlField.new(@object_name, method, @template, objectify_options(options)).render
|
66
|
+
end
|
67
|
+
|
68
|
+
def tel_field(method, options = {})
|
69
|
+
Inputs::TelField.new(@object_name, method, @template, objectify_options(options)).render
|
70
|
+
end
|
71
|
+
|
72
|
+
def color_field(method, options = {})
|
73
|
+
Inputs::ColorField.new(@object_name, method, @template, objectify_options(options)).render
|
74
|
+
end
|
75
|
+
|
76
|
+
def password_field(method, options = {})
|
77
|
+
Inputs::PasswordField.new(@object_name, method, @template, objectify_options(options)).render
|
78
|
+
end
|
79
|
+
|
80
|
+
def number_field(method, options = {})
|
81
|
+
Inputs::NumberField.new(@object_name, method, @template, objectify_options(options)).render
|
82
|
+
end
|
83
|
+
|
84
|
+
def range_field(method, options = {})
|
85
|
+
Inputs::RangeField.new(@object_name, method, @template, objectify_options(options)).render
|
86
|
+
end
|
87
|
+
|
88
|
+
def email_field(method, options = {})
|
89
|
+
Inputs::EmailField.new(@object_name, method, @template, objectify_options(options)).render
|
90
|
+
end
|
91
|
+
|
92
|
+
def search_field(method, options = {})
|
93
|
+
Inputs::SearchField.new(@object_name, method, @template, objectify_options(options)).render
|
94
|
+
end
|
95
|
+
|
96
|
+
def text_area(method, options = {})
|
97
|
+
Inputs::TextArea.new(@object_name, method, @template, objectify_options(options)).render
|
98
|
+
end
|
99
|
+
|
100
|
+
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
|
101
|
+
Inputs::CheckBox.new(@object_name, method, @template, checked_value, unchecked_value, objectify_options(options)).render
|
102
|
+
end
|
103
|
+
|
104
|
+
def radio_button(method, tag_value, options = {})
|
105
|
+
Inputs::RadioButton.new(@object_name, method, @template, tag_value, options).render
|
106
|
+
end
|
107
|
+
|
108
|
+
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
|
109
|
+
Inputs::CollectionSelect.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_html_options.merge(html_options)).render
|
110
|
+
end
|
111
|
+
|
112
|
+
def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
113
|
+
Inputs::CollectionCheckBoxes.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_html_options.merge(html_options), &block).render
|
114
|
+
end
|
115
|
+
|
116
|
+
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
117
|
+
Inputs::CollectionRadioButtons.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_html_options.merge(html_options), &block).render
|
118
|
+
end
|
119
|
+
|
120
|
+
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
121
|
+
Inputs::GroupedCollectionSelect.new(@object_name, method, @template, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_html_options.merge(html_options)).render
|
122
|
+
end
|
123
|
+
|
124
|
+
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
|
125
|
+
Inputs::TimeZoneSelect.new(@object_name, method, @template, priority_zones, objectify_options(options), @default_html_options.merge(html_options)).render
|
126
|
+
end
|
127
|
+
|
128
|
+
def weekday_select(method, options = {}, html_options = {})
|
129
|
+
Inputs::WeekdaySelect.new(@object_name, method, @template, objectify_options(options), @default_html_options.merge(html_options)).render
|
130
|
+
end
|
131
|
+
|
132
|
+
def submit(value = nil, options = {})
|
133
|
+
value, options = nil, value if value.is_a?(Hash)
|
134
|
+
value ||= submit_default_value
|
135
|
+
options = {name: "commit", value: value}.update(options)
|
136
|
+
|
137
|
+
Inputs::Submit.new(@template, options).render
|
138
|
+
end
|
139
|
+
|
140
|
+
def fields_for_with_nested_attributes(association_name, association, options, block)
|
141
|
+
name = "#{object_name}[#{association_name}_attributes]"
|
142
|
+
association = convert_to_model(association)
|
143
|
+
|
144
|
+
if association.respond_to?(:persisted?)
|
145
|
+
association = [association] if @object.public_send(association_name).respond_to?(:to_ary)
|
146
|
+
elsif !association.respond_to?(:to_ary)
|
147
|
+
association = @object.public_send(association_name)
|
148
|
+
end
|
149
|
+
|
150
|
+
if association.respond_to?(:to_ary)
|
151
|
+
explicit_child_index = options[:child_index]
|
152
|
+
|
153
|
+
@template.json.set!("#{association_name}_attributes") do
|
154
|
+
@template.json.array! association do |child|
|
155
|
+
if explicit_child_index
|
156
|
+
options[:child_index] = explicit_child_index.call if explicit_child_index.respond_to?(:call)
|
157
|
+
else
|
158
|
+
options[:child_index] = nested_child_index(name)
|
159
|
+
end
|
160
|
+
|
161
|
+
fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
elsif association
|
165
|
+
@template.json.set!("#{association_name}_attributes") do
|
166
|
+
fields_for_nested_model(name, association, options, block)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def fields_for_nested_model(name, object, fields_options, block)
|
172
|
+
object = convert_to_model(object)
|
173
|
+
emit_hidden_id = object.persisted? && fields_options.fetch(:include_id) {
|
174
|
+
options.fetch(:include_id, true)
|
175
|
+
}
|
176
|
+
|
177
|
+
@template.fields_for(name, object, fields_options) do |f|
|
178
|
+
block.call(f)
|
179
|
+
f.hidden_field(:id) if emit_hidden_id && !f.emitted_hidden_id?
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def fields_for(record_name, record_object = nil, fields_options = {}, &block)
|
184
|
+
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
|
185
|
+
fields_options[:builder] ||= self.class
|
186
|
+
fields_options[:namespace] = options[:namespace]
|
187
|
+
fields_options[:parent_builder] = self
|
188
|
+
|
189
|
+
case record_name
|
190
|
+
when String, Symbol
|
191
|
+
if nested_attributes_association?(record_name)
|
192
|
+
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
|
193
|
+
end
|
194
|
+
else
|
195
|
+
record_object = record_name.is_a?(Array) ? record_name.last : record_name
|
196
|
+
record_name = model_name_from_record_or_class(record_object).param_key
|
197
|
+
end
|
198
|
+
|
199
|
+
object_name = @object_name
|
200
|
+
index = if options.has_key?(:index)
|
201
|
+
options[:index]
|
202
|
+
elsif defined?(@auto_index)
|
203
|
+
object_name = object_name.to_s.delete_suffix("[]")
|
204
|
+
@auto_index
|
205
|
+
end
|
206
|
+
|
207
|
+
record_name = if index
|
208
|
+
"#{object_name}[#{index}][#{record_name}]"
|
209
|
+
elsif record_name.end_with?("[]")
|
210
|
+
"#{object_name}[#{record_name[0..-3]}][#{record_object.id}]"
|
211
|
+
else
|
212
|
+
"#{object_name}[#{record_name}]"
|
213
|
+
end
|
214
|
+
fields_options[:child_index] = index
|
215
|
+
|
216
|
+
@template.fields_for(record_name, record_object, fields_options, &block)
|
217
|
+
end
|
218
|
+
|
219
|
+
def default_form_builder_class
|
220
|
+
self.class
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module FormOptionsHelper
|
5
|
+
def selected_values
|
6
|
+
@selected_values ||= []
|
7
|
+
@selected_values
|
8
|
+
end
|
9
|
+
|
10
|
+
def grouped_options_for_select(grouped_options, selected_key = nil, options = {})
|
11
|
+
prompt = options[:prompt]
|
12
|
+
divider = options[:divider]
|
13
|
+
|
14
|
+
options = []
|
15
|
+
|
16
|
+
if prompt
|
17
|
+
options.push({
|
18
|
+
label: prompt_text(prompt),
|
19
|
+
value: ""
|
20
|
+
})
|
21
|
+
end
|
22
|
+
|
23
|
+
grouped_options.each do |container|
|
24
|
+
html_attributes = option_html_attributes(container)
|
25
|
+
|
26
|
+
if divider
|
27
|
+
label = divider
|
28
|
+
else
|
29
|
+
label, container = container
|
30
|
+
end
|
31
|
+
|
32
|
+
options.push({label: label, options: options_for_select(container, selected_key)}
|
33
|
+
.merge!(html_attributes))
|
34
|
+
end
|
35
|
+
|
36
|
+
options
|
37
|
+
end
|
38
|
+
|
39
|
+
def option_html_attributes(element)
|
40
|
+
if Array === element
|
41
|
+
element.select { |e| Hash === e }.reduce({}, :merge!)
|
42
|
+
else
|
43
|
+
{}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def options_for_select(container, selected = nil)
|
48
|
+
selected, disabled = extract_selected_and_disabled(selected).map do |r|
|
49
|
+
Array(r).map(&:to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
container.map do |element|
|
53
|
+
html_attributes = option_html_attributes(element)
|
54
|
+
text, value = option_text_and_value(element).map(&:to_s)
|
55
|
+
|
56
|
+
if !html_attributes[:selected] && option_value_selected?(value, selected)
|
57
|
+
selected_values.push(value)
|
58
|
+
end
|
59
|
+
|
60
|
+
if html_attributes[:selected]
|
61
|
+
selected_values.push(value)
|
62
|
+
end
|
63
|
+
|
64
|
+
if !html_attributes[:disabled] && (disabled && option_value_selected?(value, disabled))
|
65
|
+
html_attributes[:disabled] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
html_attributes[:value] = value
|
69
|
+
html_attributes[:label] = text
|
70
|
+
|
71
|
+
html_attributes
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
|
76
|
+
collection.map do |group|
|
77
|
+
option_tags = options_from_collection_for_select(
|
78
|
+
value_for_collection(group, group_method),
|
79
|
+
option_key_method,
|
80
|
+
option_value_method,
|
81
|
+
selected_key
|
82
|
+
)
|
83
|
+
|
84
|
+
{
|
85
|
+
options: option_tags,
|
86
|
+
label: value_for_collection(group, group_label_method)
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
|
92
|
+
options = collection.map do |element|
|
93
|
+
[value_for_collection(element, text_method), value_for_collection(element, value_method), option_html_attributes(element)]
|
94
|
+
end
|
95
|
+
selected, disabled = extract_selected_and_disabled(selected)
|
96
|
+
select_deselect = {
|
97
|
+
selected: extract_values_from_collection(collection, value_method, selected),
|
98
|
+
disabled: extract_values_from_collection(collection, value_method, disabled)
|
99
|
+
}
|
100
|
+
|
101
|
+
options_for_select(options, select_deselect)
|
102
|
+
end
|
103
|
+
|
104
|
+
def value_for_collection(item, value)
|
105
|
+
value.respond_to?(:call) ? value.call(item) : item.public_send(value)
|
106
|
+
end
|
107
|
+
|
108
|
+
def extract_selected_and_disabled(selected)
|
109
|
+
if selected.is_a?(Proc)
|
110
|
+
[selected, nil]
|
111
|
+
else
|
112
|
+
selected = Array.wrap(selected)
|
113
|
+
options = selected.extract_options!.symbolize_keys
|
114
|
+
selected_items = options.fetch(:selected, selected)
|
115
|
+
[selected_items, options[:disabled]]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def extract_values_from_collection(collection, value_method, selected)
|
120
|
+
if selected.is_a?(Proc)
|
121
|
+
collection.map do |element|
|
122
|
+
element.public_send(value_method) if selected.call(element)
|
123
|
+
end.compact
|
124
|
+
else
|
125
|
+
selected
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
|
130
|
+
zone_options = []
|
131
|
+
|
132
|
+
zones = model.all
|
133
|
+
convert_zones = lambda { |list| list.map { |z| [z.to_s, z.name] } }
|
134
|
+
|
135
|
+
if priority_zones
|
136
|
+
if priority_zones.is_a?(Regexp)
|
137
|
+
priority_zones = zones.select { |z| z.match?(priority_zones) }
|
138
|
+
end
|
139
|
+
|
140
|
+
zone_options.concat(options_for_select(convert_zones[priority_zones], selected))
|
141
|
+
zone_options.push({label: "-------------", value: "", disabled: true})
|
142
|
+
|
143
|
+
zones -= priority_zones
|
144
|
+
end
|
145
|
+
|
146
|
+
zone_options.concat(options_for_select(convert_zones[zones], selected))
|
147
|
+
zone_options
|
148
|
+
end
|
149
|
+
|
150
|
+
def weekday_options_for_select(selected = nil, index_as_value: false, day_format: :day_names, beginning_of_week: Date.beginning_of_week)
|
151
|
+
day_names = I18n.translate("date.#{day_format}")
|
152
|
+
day_names = day_names.map.with_index.to_a if index_as_value
|
153
|
+
day_names = day_names.rotate(Date::DAYS_INTO_WEEK.fetch(beginning_of_week))
|
154
|
+
|
155
|
+
options_for_select(day_names, selected)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class Base < ::ActionView::Helpers::Tags::Base
|
6
|
+
def json
|
7
|
+
@json ||= @template_object.instance_variable_get(:@__json)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(object_name, method_name, template_object, options = {})
|
11
|
+
options = options.with_indifferent_access
|
12
|
+
|
13
|
+
@controlled = options.delete(:controlled)
|
14
|
+
super(object_name, method_name, template_object, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def add_options(option_tags, options, value = nil)
|
20
|
+
if options[:include_blank]
|
21
|
+
content = (options[:include_blank] if options[:include_blank].is_a?(String))
|
22
|
+
label = (" " unless content)
|
23
|
+
option_tags = [{label: content || label, value: ""}] + option_tags
|
24
|
+
end
|
25
|
+
|
26
|
+
if value.blank? && options[:prompt]
|
27
|
+
tag_options = {value: ""}.tap do |prompt_opts|
|
28
|
+
prompt_opts[:disabled] = true if options[:disabled] == ""
|
29
|
+
if options[:selected] == ""
|
30
|
+
selected_values.push("")
|
31
|
+
end
|
32
|
+
prompt_opts[:label] = prompt_text(options[:prompt])
|
33
|
+
end
|
34
|
+
option_tags = [tag_options] + option_tags
|
35
|
+
end
|
36
|
+
|
37
|
+
option_tags
|
38
|
+
end
|
39
|
+
|
40
|
+
def input_props(options)
|
41
|
+
return if options.blank?
|
42
|
+
|
43
|
+
options.each_pair do |key, value|
|
44
|
+
type = TAG_TYPES[key]
|
45
|
+
|
46
|
+
if type == :data && value.is_a?(Hash)
|
47
|
+
value.each_pair do |k, v|
|
48
|
+
next if v.nil?
|
49
|
+
prefix_tag_props(key, k, v)
|
50
|
+
end
|
51
|
+
elsif type == :aria && value.is_a?(Hash)
|
52
|
+
value.each_pair do |k, v|
|
53
|
+
next if v.nil?
|
54
|
+
|
55
|
+
case v
|
56
|
+
when Array, Hash
|
57
|
+
tokens = build_values(v)
|
58
|
+
next if tokens.none?
|
59
|
+
|
60
|
+
v = safe_join(tokens, " ")
|
61
|
+
else
|
62
|
+
v = v.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
prefix_tag_props(key, k, v)
|
66
|
+
end
|
67
|
+
elsif key == "class" || key == :class
|
68
|
+
value = build_values(value).join(" ")
|
69
|
+
key = "class_name"
|
70
|
+
tag_option(key, value)
|
71
|
+
elsif !value.nil?
|
72
|
+
if key.to_s == "value"
|
73
|
+
key = key.to_s
|
74
|
+
value = value.is_a?(Array) ? value : value.to_s
|
75
|
+
end
|
76
|
+
tag_option(key, value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def tag_option(key, value)
|
82
|
+
if value.is_a? Regexp
|
83
|
+
value = value.source
|
84
|
+
end
|
85
|
+
|
86
|
+
@controlled ||= nil
|
87
|
+
|
88
|
+
if !@controlled
|
89
|
+
if key.to_sym == :value
|
90
|
+
key = "default_value"
|
91
|
+
end
|
92
|
+
|
93
|
+
if key.to_sym == :checked
|
94
|
+
key = "default_checked"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
json.set!(key, value)
|
99
|
+
end
|
100
|
+
|
101
|
+
def prefix_tag_props(prefix, key, value)
|
102
|
+
key = "#{prefix}-#{key.to_s.dasherize}"
|
103
|
+
unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
|
104
|
+
value = value.to_json
|
105
|
+
end
|
106
|
+
tag_option(key, value)
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_values(*args)
|
110
|
+
tag_values = []
|
111
|
+
|
112
|
+
args.each do |tag_value|
|
113
|
+
case tag_value
|
114
|
+
when Hash
|
115
|
+
tag_value.each do |key, val|
|
116
|
+
tag_values << key.to_s if val && key.present?
|
117
|
+
end
|
118
|
+
when Array
|
119
|
+
tag_values.concat build_values(*tag_value)
|
120
|
+
else
|
121
|
+
tag_values << tag_value.to_s if tag_value.present?
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
tag_values
|
126
|
+
end
|
127
|
+
|
128
|
+
def select_content_props(option_tags, options, html_options)
|
129
|
+
html_options = html_options.stringify_keys
|
130
|
+
add_default_name_and_id(html_options)
|
131
|
+
|
132
|
+
if placeholder_required?(html_options)
|
133
|
+
raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
|
134
|
+
options[:include_blank] ||= true unless options[:prompt]
|
135
|
+
end
|
136
|
+
|
137
|
+
html_options["type"] = "select"
|
138
|
+
value_for_blank = options.fetch(:selected) { value }
|
139
|
+
option_tags = add_options(option_tags, options, value_for_blank)
|
140
|
+
|
141
|
+
if options[:multiple]
|
142
|
+
html_options["multiple"] = options[:multiple]
|
143
|
+
end
|
144
|
+
|
145
|
+
if selected_values.any?
|
146
|
+
html_options["value"] ||= if html_options["multiple"]
|
147
|
+
Array(selected_values)
|
148
|
+
else
|
149
|
+
selected_values.first
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
json.set!(sanitized_method_name) do
|
154
|
+
input_props(html_options)
|
155
|
+
|
156
|
+
if options.key?(:include_hidden)
|
157
|
+
json.include_hidden options[:include_hidden]
|
158
|
+
end
|
159
|
+
json.options(option_tags)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|