active_scaffold 4.2.3 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.rdoc +16 -1
- data/README.md +108 -7
- data/app/assets/javascripts/{jquery → active_scaffold}/active_scaffold.js +759 -762
- data/app/assets/javascripts/{jquery/date_picker_bridge.js.erb → active_scaffold/date_picker_bridge.js} +0 -3
- data/app/assets/javascripts/active_scaffold/load.js +102 -0
- data/app/assets/javascripts/active_scaffold.js.erb +3 -27
- data/app/assets/stylesheets/active_scaffold/_colours.scss +330 -0
- data/app/assets/stylesheets/active_scaffold/_images.scss +65 -0
- data/app/assets/stylesheets/{active_scaffold_layout.scss → active_scaffold/_layout.scss} +14 -0
- data/app/assets/stylesheets/active_scaffold/_variables.scss +194 -0
- data/app/assets/stylesheets/active_scaffold/core.scss +15 -0
- data/app/assets/stylesheets/active_scaffold.scss.erb +16 -0
- data/app/views/active_scaffold_overrides/_field_search_columns.html.erb +8 -0
- data/app/views/active_scaffold_overrides/_form.html.erb +8 -0
- data/app/views/active_scaffold_overrides/_form_association.html.erb +3 -1
- data/app/views/active_scaffold_overrides/_form_association_record.html.erb +4 -2
- data/app/views/active_scaffold_overrides/_show_association.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_show_association_horizontal.html.erb +2 -2
- data/app/views/active_scaffold_overrides/_show_association_vertical.html.erb +1 -1
- data/app/views/active_scaffold_overrides/edit_associated.js.erb +10 -8
- data/lib/active_scaffold/actions/core.rb +34 -3
- data/lib/active_scaffold/assets/css_deps_generator.rb +42 -0
- data/lib/active_scaffold/assets/jquery_ui_manifest.rb +77 -0
- data/lib/active_scaffold/assets/jquery_ui_theme_generator.rb +102 -0
- data/lib/active_scaffold/assets.rb +109 -0
- data/lib/active_scaffold/attribute_params.rb +11 -2
- data/lib/active_scaffold/bridges/active_storage/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/chosen.rb +1 -1
- data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/file_column/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/paperclip/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/record_select/helpers.rb +1 -1
- data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +1 -0
- data/lib/active_scaffold/bridges/tiny_mce.rb +6 -1
- data/lib/active_scaffold/bridges.rb +7 -0
- data/lib/active_scaffold/config/core.rb +7 -3
- data/lib/active_scaffold/constraints.rb +1 -1
- data/lib/active_scaffold/data_structures/action_columns.rb +66 -0
- data/lib/active_scaffold/data_structures/bridge.rb +2 -0
- data/lib/active_scaffold/data_structures/column.rb +3 -0
- data/lib/active_scaffold/engine.rb +40 -0
- data/lib/active_scaffold/finder.rb +1 -1
- data/lib/active_scaffold/helpers/assets_helpers.rb +39 -0
- data/lib/active_scaffold/helpers/controller_helpers.rb +1 -1
- data/lib/active_scaffold/helpers/form_column_helpers.rb +57 -532
- data/lib/active_scaffold/helpers/form_ui_helpers.rb +530 -0
- data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -0
- data/lib/active_scaffold/helpers/list_column_helpers.rb +31 -11
- data/lib/active_scaffold/helpers/search_column_helpers.rb +5 -12
- data/lib/active_scaffold/helpers/show_column_helpers.rb +4 -2
- data/lib/active_scaffold/helpers/view_helpers.rb +12 -0
- data/lib/active_scaffold/railties/tasks.rake +10 -0
- data/lib/active_scaffold/testing/assert_embedded_load.rb +33 -0
- data/lib/active_scaffold/version.rb +2 -2
- data/lib/active_scaffold.rb +7 -2
- data/lib/tasks/active_scaffold/assets.rake +42 -0
- data/lib/tasks/bundle.rake +25 -0
- data/vendor/assets/stylesheets/{jquery-ui-theme.css.erb → jquery-ui-theme.css} +17 -17
- metadata +26 -28
- data/app/assets/stylesheets/active_scaffold.scss +0 -424
- data/app/assets/stylesheets/active_scaffold_extensions.css.erb +0 -2
- data/app/assets/stylesheets/active_scaffold_images.scss +0 -65
- data/app/assets/stylesheets/active_scaffold_jquery_ui.css.erb +0 -13
- /data/app/assets/javascripts/{jquery → active_scaffold}/active_scaffold_chosen.js +0 -0
- /data/app/assets/javascripts/{jquery → active_scaffold}/draggable_lists.js +0 -0
- /data/app/assets/javascripts/{jquery → active_scaffold}/jquery.editinplace.js +0 -0
- /data/app/assets/javascripts/{jquery → active_scaffold}/tiny_mce_bridge.js +0 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveScaffold
|
|
4
|
+
module Helpers
|
|
5
|
+
# Helpers that assist with the rendering of a Form Column
|
|
6
|
+
module FormUiHelpers
|
|
7
|
+
def active_scaffold_grouped_options(column, select_options, optgroup)
|
|
8
|
+
group_column = active_scaffold_config_for(column.association.klass).columns[optgroup]
|
|
9
|
+
group_label = group_column.options[:label_method] if group_column
|
|
10
|
+
group_label ||= group_column&.association ? :to_label : :to_s
|
|
11
|
+
select_options.group_by(&optgroup.to_sym).collect do |group, options|
|
|
12
|
+
[group.send(group_label), options.collect { |r| [r.send(column.options[:label_method] || :to_label), r.id] }]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def active_scaffold_translate_select_options(options)
|
|
17
|
+
options[:include_blank] = as_(options[:include_blank].to_s) if options[:include_blank].is_a? Symbol
|
|
18
|
+
options[:prompt] = as_(options[:prompt].to_s) if options[:prompt].is_a? Symbol
|
|
19
|
+
options
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def active_scaffold_select_name_with_multiple(options)
|
|
23
|
+
return if !options[:multiple] || options[:name].to_s.ends_with?('[]')
|
|
24
|
+
|
|
25
|
+
options[:name] = "#{options[:name]}[]"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def active_scaffold_input_singular_association(column, html_options, options = {}, ui_options: column.options)
|
|
29
|
+
record = html_options.delete(:object)
|
|
30
|
+
associated = html_options.include?(:associated) ? html_options.delete(:associated) : record.send(column.association.name)
|
|
31
|
+
|
|
32
|
+
helper_method = association_helper_method(column.association, :sorted_association_options_find)
|
|
33
|
+
select_options = send(helper_method, column.association, nil, record)
|
|
34
|
+
select_options.unshift(associated) if associated&.persisted? && select_options.exclude?(associated)
|
|
35
|
+
|
|
36
|
+
method = column.name
|
|
37
|
+
options.merge! selected: associated&.id, include_blank: as_(:_select_), object: record
|
|
38
|
+
|
|
39
|
+
html_options.merge!(ui_options[:html_options] || {})
|
|
40
|
+
options.merge!(ui_options)
|
|
41
|
+
html_options.delete(:multiple) # no point using multiple in a form for singular assoc, but may be set for field search
|
|
42
|
+
active_scaffold_translate_select_options(options)
|
|
43
|
+
|
|
44
|
+
html =
|
|
45
|
+
if (optgroup = options.delete(:optgroup))
|
|
46
|
+
select(:record, method, active_scaffold_grouped_options(column, select_options, optgroup), options, html_options)
|
|
47
|
+
else
|
|
48
|
+
collection_select(:record, method, select_options, :id, ui_options[:label_method] || :to_label, options, html_options)
|
|
49
|
+
end
|
|
50
|
+
html << active_scaffold_refresh_link(column, html_options, record, ui_options) if ui_options[:refresh_link]
|
|
51
|
+
html
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def active_scaffold_new_record_klass(column, record, **options)
|
|
55
|
+
if column.association.polymorphic? && column.association.belongs_to?
|
|
56
|
+
type = record.send(column.association.foreign_type)
|
|
57
|
+
type_options = options[:types]
|
|
58
|
+
column.association.klass(record) if type.present? && (type_options.nil? || type_options.include?(type))
|
|
59
|
+
else
|
|
60
|
+
column.association.klass
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def active_scaffold_add_new(column, record, html_options, ui_options: column.options, skip_link: false)
|
|
65
|
+
options = ui_options[:add_new] == true ? {} : ui_options[:add_new]
|
|
66
|
+
options[:mode] = :popup if column.association&.collection?
|
|
67
|
+
case options[:mode]
|
|
68
|
+
when nil, :subform
|
|
69
|
+
active_scaffold_new_record_subform(column, record, html_options, options: options, skip_link: skip_link)
|
|
70
|
+
when :popup
|
|
71
|
+
active_scaffold_new_record_popup(column, record, html_options, options: options) unless skip_link
|
|
72
|
+
else
|
|
73
|
+
raise ArgumentError, "unsupported mode for add_new: #{options[:mode].inspect}"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def active_scaffold_new_record_url_options(column, record)
|
|
78
|
+
if column.association.reverse
|
|
79
|
+
constraint = [record.id]
|
|
80
|
+
constraint.unshift record.class.name if column.association.reverse_association.polymorphic?
|
|
81
|
+
{embedded: {constraints: {column.association.reverse => constraint}}}
|
|
82
|
+
else
|
|
83
|
+
raise "can't add constraint to create new record with :popup, no reverse association for " \
|
|
84
|
+
"\"#{column.name}\" in #{column.association.klass}, add the reverse association " \
|
|
85
|
+
'or override active_scaffold_new_record_url_options helper.'
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def active_scaffold_new_record_popup(column, record, html_options, options: {})
|
|
90
|
+
klass = send(override_helper_per_model(:active_scaffold_new_record_klass, record.class), column, record, **options)
|
|
91
|
+
klass = nil if options[:security_method] && !controller.send(options[:security_method])
|
|
92
|
+
klass = nil if klass && options[:security_method].nil? && !klass.authorized_for?(crud_type: :create)
|
|
93
|
+
return h('') unless klass
|
|
94
|
+
|
|
95
|
+
link_text = active_scaffold_add_new_text(options, :add_new_text, :add)
|
|
96
|
+
url_options_helper = override_helper_per_model(:active_scaffold_new_record_url_options, record.class)
|
|
97
|
+
url_options = send(url_options_helper, column, record)
|
|
98
|
+
url_options[:controller] ||= active_scaffold_controller_for(klass).controller_path
|
|
99
|
+
url_options[:action] ||= :new
|
|
100
|
+
url_options[:from_field] ||= html_options[:id]
|
|
101
|
+
url_options[:parent_model] ||= record.class.name
|
|
102
|
+
url_options[:parent_column] ||= column.name
|
|
103
|
+
url_options.reverse_merge! options[:url_options] if options[:url_options]
|
|
104
|
+
link_to(link_text, url_options, remote: true, data: {position: :popup}, class: 'as_action')
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def active_scaffold_new_record_subform(column, record, html_options, options: {}, new_record_attributes: nil, locals: {}, skip_link: false)
|
|
108
|
+
klass = send(override_helper_per_model(:active_scaffold_new_record_klass, record.class), column, record, **options)
|
|
109
|
+
return content_tag(:div, '') unless klass
|
|
110
|
+
|
|
111
|
+
subform_attrs = active_scaffold_subform_attributes(column, nil, klass, ui_options: options)
|
|
112
|
+
if record.send(column.name)&.new_record?
|
|
113
|
+
new_record = record.send(column.name)
|
|
114
|
+
else
|
|
115
|
+
subform_attrs[:style] = 'display: none'
|
|
116
|
+
end
|
|
117
|
+
subform_attrs[:class] << ' optional'
|
|
118
|
+
scope = html_options[:name].scan(/record(.*)\[#{column.name}\]/).dig(0, 0)
|
|
119
|
+
new_record ||= klass.new(new_record_attributes)
|
|
120
|
+
locals = locals.reverse_merge(column: column, parent_record: record, associated: [], show_blank_record: new_record, scope: scope)
|
|
121
|
+
subform = render(partial: subform_partial_for_column(column, klass, ui_options: options), locals: locals)
|
|
122
|
+
if options[:hide_subgroups]
|
|
123
|
+
toggable_id = "#{sub_form_id(association: column.name, id: record.id || generated_id(record) || 99_999_999_999)}-div"
|
|
124
|
+
subform << link_to_visibility_toggle(toggable_id, default_visible: false)
|
|
125
|
+
end
|
|
126
|
+
html = content_tag(:div, subform, subform_attrs)
|
|
127
|
+
return html if skip_link
|
|
128
|
+
|
|
129
|
+
html << active_scaffold_show_new_subform_link(column, record, html_options[:id], subform_attrs[:id], options: options)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def active_scaffold_add_new_text(options, key, default)
|
|
133
|
+
text = options[key] unless options == true
|
|
134
|
+
return text if text.is_a? String
|
|
135
|
+
|
|
136
|
+
as_(text || default)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def active_scaffold_show_new_subform_link(column, record, select_id, subform_id, options: {})
|
|
140
|
+
add_existing = active_scaffold_add_new_text(options, :add_existing_text, :add_existing)
|
|
141
|
+
create_new = active_scaffold_add_new_text(options, :add_new_text, :create_new)
|
|
142
|
+
data = {select_id: select_id, subform_id: subform_id, subform_text: add_existing, select_text: create_new}
|
|
143
|
+
label = data[record.send(column.name)&.new_record? ? :subform_text : :select_text]
|
|
144
|
+
link_to(label, '#', data: data, class: 'show-new-subform')
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def active_scaffold_file_with_content(column, options, content, remove_file_prefix, controls_class, ui_options: column.options)
|
|
148
|
+
options = active_scaffold_input_text_options(options.merge(ui_options))
|
|
149
|
+
options[:style] = 'display: none' if content
|
|
150
|
+
field = file_field(:record, column.name, options)
|
|
151
|
+
|
|
152
|
+
if content
|
|
153
|
+
content = [content, ' | ']
|
|
154
|
+
content << yield if block_given?
|
|
155
|
+
object_name, method = options[:name].split(/\[(#{column.name})\]/)
|
|
156
|
+
method.sub!(/#{column.name}/, "#{remove_file_prefix}\\0")
|
|
157
|
+
content << hidden_field(object_name, method, value: 'false', class: 'remove_file')
|
|
158
|
+
active_scaffold_file_with_remove_link(safe_join(content), options, controls_class) { field }
|
|
159
|
+
else
|
|
160
|
+
field
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def active_scaffold_file_with_remove_link(content, options, controls_class, link_key = nil)
|
|
165
|
+
required = options.delete(:required)
|
|
166
|
+
link_key ||= options[:multiple] ? :remove_files : :remove_file
|
|
167
|
+
content_tag(:div, class: "#{controls_class} file-input-controls", data: {required: required}) do
|
|
168
|
+
content_line = content_tag(:div) do
|
|
169
|
+
safe_join [content, content_tag(:a, as_(link_key), href: '#', class: 'remove-file-btn')]
|
|
170
|
+
end
|
|
171
|
+
content_line << yield if block_given?
|
|
172
|
+
content_line
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def active_scaffold_refresh_link(column, html_options, record, ui_options = {})
|
|
177
|
+
link_options = {object: record, data: {field_selector: ui_options[:field_selector] || "##{html_options[:id]}"}}
|
|
178
|
+
if html_options['data-update_url']
|
|
179
|
+
link_options['data-update_send_form'] = html_options['data-update_send_form']
|
|
180
|
+
link_options['data-update_send_form_selector'] = html_options['data-update_send_form_selector']
|
|
181
|
+
else
|
|
182
|
+
scope = html_options[:name].scan(/^record((\[[^\]]*\])*)\[#{column.name}\]/).dig(0, 0) if html_options[:name]
|
|
183
|
+
link_options = update_columns_options(column, scope.presence, link_options, true)
|
|
184
|
+
end
|
|
185
|
+
link_options[:class] = 'refresh-link'
|
|
186
|
+
if ui_options[:refresh_link].is_a?(Hash)
|
|
187
|
+
text = ui_options.dig(:refresh_link, :text)
|
|
188
|
+
text = as_(text) if text.is_a?(Symbol)
|
|
189
|
+
link_options.merge! ui_options[:refresh_link].except(:text)
|
|
190
|
+
end
|
|
191
|
+
link_to(text || as_(:refresh), link_options.delete('data-update_url') || html_options['data-update_url'], link_options.except(:object))
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def active_scaffold_plural_association_options(column, record = nil)
|
|
195
|
+
associated_options = record.send(column.association.name)
|
|
196
|
+
helper_method = association_helper_method(column.association, :sorted_association_options_find)
|
|
197
|
+
[associated_options, associated_options | send(helper_method, column.association, nil, record)]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def active_scaffold_input_plural_association(column, options, ui_options: column.options)
|
|
201
|
+
record = options.delete(:object)
|
|
202
|
+
associated_options, select_options = active_scaffold_plural_association_options(column, record)
|
|
203
|
+
|
|
204
|
+
html =
|
|
205
|
+
if options[:multiple] || ui_options.dig(:html_options, :multiple)
|
|
206
|
+
html_options = options.merge(ui_options[:html_options] || {})
|
|
207
|
+
active_scaffold_select_name_with_multiple html_options
|
|
208
|
+
collection_select(:record, column.name, select_options, :id, ui_options[:label_method] || :to_label, ui_options.merge(object: record), html_options)
|
|
209
|
+
elsif select_options.empty?
|
|
210
|
+
content_tag(:span, as_(:no_options), class: "#{options[:class]} no-options", id: options[:id]) <<
|
|
211
|
+
hidden_field_tag("#{options[:name]}[]", '', id: nil)
|
|
212
|
+
else
|
|
213
|
+
active_scaffold_checkbox_list(column, select_options, associated_options.collect(&:id), options, ui_options: ui_options)
|
|
214
|
+
end
|
|
215
|
+
html << active_scaffold_refresh_link(column, options, record, ui_options) if ui_options[:refresh_link]
|
|
216
|
+
html
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def active_scaffold_input_draggable(column, options, ui_options: column.options)
|
|
220
|
+
active_scaffold_input_checkboxes(column, options.merge(draggable_lists: true), ui_options: ui_options)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def active_scaffold_input_checkboxes(column, options, ui_options: column.options)
|
|
224
|
+
if column.association&.singular?
|
|
225
|
+
raise ArgumentError, "association #{column.association.name} is singular, but checkboxes form_ui expects a collection"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
if column.association # collection
|
|
229
|
+
active_scaffold_input_plural_association(column, options, ui_options: ui_options)
|
|
230
|
+
else
|
|
231
|
+
record = options.delete(:object)
|
|
232
|
+
associated_options = record.send(column.name) || []
|
|
233
|
+
raise ArgumentError, 'checkboxes form_ui expect getter to return an Array' unless associated_options.is_a?(Array)
|
|
234
|
+
|
|
235
|
+
select_options = active_scaffold_translated_enum_options(column, options[:object], ui_options: ui_options)
|
|
236
|
+
active_scaffold_checkbox_list(column, select_options, associated_options, options, ui_options: ui_options)
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def active_scaffold_checkbox_option(option, label_method, associated_ids, checkbox_options, li_options = {})
|
|
241
|
+
content_tag(:li, li_options) do
|
|
242
|
+
option_id = option.is_a?(Array) ? option[1] : option.id
|
|
243
|
+
label = option.is_a?(Array) ? option[0] : option.send(label_method)
|
|
244
|
+
check_box_tag(checkbox_options[:name], option_id, associated_ids.include?(option_id), checkbox_options) <<
|
|
245
|
+
content_tag(:label, label, for: checkbox_options[:id])
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def active_scaffold_check_all_buttons(column, options, ui_options: column.options)
|
|
250
|
+
content_tag(:div, class: 'check-buttons') do
|
|
251
|
+
link_to(as_(:check_all), '#', class: 'check-all') <<
|
|
252
|
+
link_to(as_(:uncheck_all), '#', class: 'uncheck-all')
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def active_scaffold_checkbox_list(column, select_options, associated_ids, options, ui_options: column.options)
|
|
257
|
+
label_method = ui_options[:label_method] || :to_label
|
|
258
|
+
html = active_scaffold_check_all_buttons(column, options, ui_options: ui_options)
|
|
259
|
+
html << hidden_field_tag("#{options[:name]}[]", '', id: nil)
|
|
260
|
+
draggable = options.delete(:draggable_lists) || ui_options[:draggable_lists]
|
|
261
|
+
html << content_tag(:ul, options.merge(class: "#{options[:class]} checkbox-list#{' draggable-lists' if draggable}")) do
|
|
262
|
+
content = []
|
|
263
|
+
select_options.each_with_index do |option, i|
|
|
264
|
+
content << active_scaffold_checkbox_option(option, label_method, associated_ids, name: "#{options[:name]}[]", id: "#{options[:id]}_#{i}_id")
|
|
265
|
+
end
|
|
266
|
+
safe_join content
|
|
267
|
+
end
|
|
268
|
+
html
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def active_scaffold_translated_option(column, text, value = nil)
|
|
272
|
+
value = text if value.nil?
|
|
273
|
+
if text.is_a?(Symbol)
|
|
274
|
+
klass = column.active_record_class
|
|
275
|
+
text = I18n.t "#{klass.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{column.name}.#{text}",
|
|
276
|
+
default: klass.human_attribute_name(text)
|
|
277
|
+
end
|
|
278
|
+
[text, value]
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def active_scaffold_enum_options(column, record = nil, ui_options: column.options)
|
|
282
|
+
ui_options[:options]
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def active_scaffold_translated_enum_options(column, record, ui_options: column.options)
|
|
286
|
+
enum_options_method = override_helper_per_model(:active_scaffold_enum_options, record.class)
|
|
287
|
+
# e.g. setting form_ui = :select, set the options in column.options, and set search_ui = :select, {null_comparators: false}
|
|
288
|
+
ui_options = column.options if ui_options[:options].nil?
|
|
289
|
+
options = send(enum_options_method, column, record, ui_options: ui_options)&.collect do |text, value|
|
|
290
|
+
active_scaffold_translated_option(column, text, value)
|
|
291
|
+
end
|
|
292
|
+
options || []
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def active_scaffold_input_enum(column, html_options, options = {}, ui_options: column.options)
|
|
296
|
+
record = html_options.delete(:object)
|
|
297
|
+
options[:selected] = record.send(column.name)
|
|
298
|
+
options[:object] = record
|
|
299
|
+
options_for_select = active_scaffold_translated_enum_options(column, record, ui_options: ui_options)
|
|
300
|
+
html_options.merge!(ui_options[:html_options] || {})
|
|
301
|
+
options.merge!(ui_options)
|
|
302
|
+
active_scaffold_select_name_with_multiple html_options
|
|
303
|
+
active_scaffold_translate_select_options(options)
|
|
304
|
+
html = select(:record, column.name, options_for_select, options, html_options)
|
|
305
|
+
html << active_scaffold_refresh_link(column, html_options, record, ui_options) if ui_options[:refresh_link]
|
|
306
|
+
html
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def active_scaffold_input_select(column, html_options, ui_options: column.options)
|
|
310
|
+
record = html_options[:object]
|
|
311
|
+
html =
|
|
312
|
+
if column.association&.singular?
|
|
313
|
+
active_scaffold_input_singular_association(column, html_options, ui_options: ui_options)
|
|
314
|
+
elsif column.association&.collection?
|
|
315
|
+
active_scaffold_input_plural_association(column, html_options, ui_options: ui_options)
|
|
316
|
+
else
|
|
317
|
+
active_scaffold_input_enum(column, html_options, ui_options: ui_options)
|
|
318
|
+
end
|
|
319
|
+
if ui_options[:add_new]
|
|
320
|
+
html = content_tag(:div, html, class: 'select-field') <<
|
|
321
|
+
active_scaffold_add_new(column, record, html_options, ui_options: ui_options)
|
|
322
|
+
end
|
|
323
|
+
html
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def active_scaffold_input_select_multiple(column, options, ui_options: column.options)
|
|
327
|
+
active_scaffold_input_select(column, options.merge(multiple: true), ui_options: ui_options)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def active_scaffold_radio_option(option, selected, column, radio_options, ui_options: column.options)
|
|
331
|
+
if column.association
|
|
332
|
+
label_method = ui_options[:label_method] || :to_label
|
|
333
|
+
text = option.send(label_method)
|
|
334
|
+
value = option.id
|
|
335
|
+
checked = {checked: selected == value}
|
|
336
|
+
else
|
|
337
|
+
text, value = option
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
id_key = radio_options[:'data-id'] ? :'data-id' : :id
|
|
341
|
+
radio_options = radio_options.merge(id_key => "#{radio_options[id_key]}-#{value.to_s.parameterize}")
|
|
342
|
+
radio_options.merge!(checked) if checked
|
|
343
|
+
content_tag(:label, radio_button(:record, column.name, value, radio_options) + text)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def active_scaffold_input_radio_content(column, record, options, html_options, ui_options)
|
|
347
|
+
if ui_options[:add_new]
|
|
348
|
+
add_new_subform = ui_options[:add_new] == true || ui_options[:add_new][:mode].in?([nil, :subform])
|
|
349
|
+
if add_new_subform
|
|
350
|
+
html_options[:data] ||= {}
|
|
351
|
+
html_options[:data][:subform_id] = active_scaffold_subform_attributes(column, ui_options: ui_options)[:id]
|
|
352
|
+
end
|
|
353
|
+
radio_html_options = html_options.merge(class: "#{html_options[:class]} hide-new-subform")
|
|
354
|
+
else
|
|
355
|
+
radio_html_options = html_options
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
selected = record.send(column.association.name) if column.association
|
|
359
|
+
radios = options.map do |option|
|
|
360
|
+
active_scaffold_radio_option(option, selected&.id, column, radio_html_options, ui_options: ui_options)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
if ui_options[:include_blank]
|
|
364
|
+
label = ui_options[:include_blank]
|
|
365
|
+
label = as_(ui_options[:include_blank]) if ui_options[:include_blank].is_a?(Symbol)
|
|
366
|
+
radio_id = "#{html_options[:id]}-"
|
|
367
|
+
radios.prepend content_tag(:label, radio_button(:record, column.name, '', html_options.merge(id: radio_id)) + label)
|
|
368
|
+
end
|
|
369
|
+
if ui_options[:add_new]
|
|
370
|
+
if add_new_subform
|
|
371
|
+
create_new = content_tag(:label) do
|
|
372
|
+
radio_button_tag(html_options[:name], '', selected&.new_record?, html_options.merge(
|
|
373
|
+
id: "#{html_options[:id]}-create_new", class: "#{html_options[:class]} show-new-subform"
|
|
374
|
+
).except(:object)) <<
|
|
375
|
+
active_scaffold_add_new_text(ui_options[:add_new], :add_new_text, :create_new)
|
|
376
|
+
end
|
|
377
|
+
radios << create_new
|
|
378
|
+
skip_link = true
|
|
379
|
+
else
|
|
380
|
+
ui_options = ui_options.merge(add_new: ui_options[:add_new].merge(
|
|
381
|
+
url_options: {
|
|
382
|
+
parent_scope: html_options[:name].gsub(/^record|\[[^\]]*\]$/, '').presence,
|
|
383
|
+
radio_data: html_options.slice(*html_options.keys.grep(/^data-update_/))
|
|
384
|
+
}
|
|
385
|
+
))
|
|
386
|
+
radios << content_tag(:span, '', class: 'new-radio-container', id: html_options[:id])
|
|
387
|
+
end
|
|
388
|
+
radios << active_scaffold_add_new(column, record, html_options, ui_options: ui_options, skip_link: skip_link)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
safe_join radios
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def active_scaffold_input_radio(column, html_options, ui_options: column.options)
|
|
395
|
+
record = html_options[:object]
|
|
396
|
+
html_options.merge!(ui_options[:html_options] || {})
|
|
397
|
+
options =
|
|
398
|
+
if column.association
|
|
399
|
+
helper_method = association_helper_method(column.association, :sorted_association_options_find)
|
|
400
|
+
send(helper_method, column.association, nil, record)
|
|
401
|
+
else
|
|
402
|
+
active_scaffold_translated_enum_options(column, record, ui_options: ui_options)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
if options.present?
|
|
406
|
+
html = active_scaffold_input_radio_content(column, record, options, html_options, ui_options)
|
|
407
|
+
else
|
|
408
|
+
html = content_tag(:span, as_(:no_options), class: "#{html_options[:class]} no-options")
|
|
409
|
+
html << hidden_field_tag(html_options[:name], '', id: html_options[:id])
|
|
410
|
+
if ui_options[:add_new]
|
|
411
|
+
html = content_tag(:div, html, class: 'select-field') <<
|
|
412
|
+
active_scaffold_add_new(column, record, html_options, ui_options: ui_options)
|
|
413
|
+
end
|
|
414
|
+
html
|
|
415
|
+
end
|
|
416
|
+
html << active_scaffold_refresh_link(column, html_options, record, ui_options.merge(field_selector: "[name=\"#{html_options[:name]}\"]")) if ui_options[:refresh_link]
|
|
417
|
+
html
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def active_scaffold_input_checkbox(column, options, ui_options: column.options)
|
|
421
|
+
check_box(:record, column.name, options.merge(ui_options))
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def active_scaffold_input_password(column, options, ui_options: column.options)
|
|
425
|
+
active_scaffold_text_input :password_field, column, options.reverse_merge(autocomplete: 'new-password'), ui_options: ui_options
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def active_scaffold_input_textarea(column, options, ui_options: column.options)
|
|
429
|
+
text_area(:record, column.name, options.merge(cols: ui_options[:cols], rows: ui_options[:rows], size: ui_options[:size]))
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def active_scaffold_input_virtual(column, options)
|
|
433
|
+
active_scaffold_text_input :text_field, column, options
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Some fields from HTML5 (primarily for using in-browser validation)
|
|
437
|
+
# Sadly, many of them lacks browser support
|
|
438
|
+
|
|
439
|
+
# A text box, that accepts only valid email address (in-browser validation)
|
|
440
|
+
def active_scaffold_input_email(column, options, ui_options: column.options)
|
|
441
|
+
active_scaffold_text_input :email_field, column, options, ui_options: ui_options
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# A text box, that accepts only valid URI (in-browser validation)
|
|
445
|
+
def active_scaffold_input_url(column, options, ui_options: column.options)
|
|
446
|
+
active_scaffold_text_input :url_field, column, options, ui_options: ui_options
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# A text box, that accepts only valid phone-number (in-browser validation)
|
|
450
|
+
def active_scaffold_input_telephone(column, options, ui_options: column.options)
|
|
451
|
+
active_scaffold_text_input :telephone_field, column, options, :format, ui_options: ui_options
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
# A spinbox control for number values (in-browser validation)
|
|
455
|
+
def active_scaffold_input_number(column, options, ui_options: column.options)
|
|
456
|
+
active_scaffold_number_input :number_field, column, options, :format, ui_options: ui_options
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# A slider control for number values (in-browser validation)
|
|
460
|
+
def active_scaffold_input_range(column, options, ui_options: column.options)
|
|
461
|
+
active_scaffold_number_input :range_field, column, options, :format, ui_options: ui_options
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# A slider control for number values (in-browser validation)
|
|
465
|
+
def active_scaffold_number_input(method, column, options, remove_options = nil, ui_options: column.options)
|
|
466
|
+
options = numerical_constraints_for_column(column, options)
|
|
467
|
+
active_scaffold_text_input method, column, options, remove_options, ui_options: ui_options
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def active_scaffold_text_input(method, column, options, remove_options = nil, ui_options: column.options)
|
|
471
|
+
options = active_scaffold_input_text_options(options)
|
|
472
|
+
options = options.merge(ui_options)
|
|
473
|
+
options = options.except(*remove_options) if remove_options.present?
|
|
474
|
+
send method, :record, column.name, options
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
# A color picker
|
|
478
|
+
def active_scaffold_input_color(column, options, ui_options: column.options)
|
|
479
|
+
html = []
|
|
480
|
+
options = active_scaffold_input_text_options(options)
|
|
481
|
+
if column.null?
|
|
482
|
+
no_color = options[:object].send(column.name).nil?
|
|
483
|
+
method = no_color ? :hidden_field : :color_field
|
|
484
|
+
html << content_tag(:label, check_box_tag('disable', '1', no_color, id: nil, name: nil, class: 'no-color') << " #{as_ ui_options[:no_color] || :no_color}")
|
|
485
|
+
else
|
|
486
|
+
method = :color_field
|
|
487
|
+
end
|
|
488
|
+
html << send(method, :record, column.name, options.merge(ui_options).except(:format, :no_color))
|
|
489
|
+
safe_join html
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
#
|
|
493
|
+
# Column.type-based inputs
|
|
494
|
+
#
|
|
495
|
+
|
|
496
|
+
def active_scaffold_input_boolean(column, html_options, ui_options: column.options)
|
|
497
|
+
record = html_options.delete(:object)
|
|
498
|
+
html_options.merge!(ui_options[:html_options] || {})
|
|
499
|
+
|
|
500
|
+
options = {selected: record.send(column.name), object: record}
|
|
501
|
+
options[:include_blank] = :_select_ if column.null?
|
|
502
|
+
options.merge!(ui_options)
|
|
503
|
+
active_scaffold_translate_select_options(options)
|
|
504
|
+
|
|
505
|
+
options_for_select = [[as_(:true), true], [as_(:false), false]] # rubocop:disable Lint/BooleanSymbol
|
|
506
|
+
select(:record, column.name, options_for_select, options, html_options)
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def active_scaffold_input_date(column, options, ui_options: column.options)
|
|
510
|
+
active_scaffold_text_input :date_field, column, options, ui_options: ui_options
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
def active_scaffold_input_time(column, options, ui_options: column.options)
|
|
514
|
+
active_scaffold_text_input :time_field, column, options, ui_options: ui_options
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
def active_scaffold_input_datetime(column, options, ui_options: column.options)
|
|
518
|
+
active_scaffold_text_input :datetime_local_field, column, options, ui_options: ui_options
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def active_scaffold_input_month(column, options, ui_options: column.options)
|
|
522
|
+
active_scaffold_text_input :month_field, column, options, ui_options: ui_options
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def active_scaffold_input_week(column, options, ui_options: column.options)
|
|
526
|
+
active_scaffold_text_input :week_field, column, options, ui_options: ui_options
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
end
|
|
@@ -130,6 +130,7 @@ module ActiveScaffold
|
|
|
130
130
|
end
|
|
131
131
|
as_(:association, scope: :human_conditions, column: attribute, value: associated.join(', '))
|
|
132
132
|
end
|
|
133
|
+
alias active_scaffold_human_condition_checkboxes active_scaffold_human_condition_select
|
|
133
134
|
alias active_scaffold_human_condition_multi_select active_scaffold_human_condition_select
|
|
134
135
|
alias active_scaffold_human_condition_select_multiple active_scaffold_human_condition_select
|
|
135
136
|
alias active_scaffold_human_condition_record_select active_scaffold_human_condition_select
|
|
@@ -24,9 +24,7 @@ module ActiveScaffold
|
|
|
24
24
|
value = ' '.html_safe if value.nil? || value.blank? # fix for IE 6
|
|
25
25
|
value
|
|
26
26
|
rescue StandardError => e
|
|
27
|
-
|
|
28
|
-
ActiveScaffold.log_exception(e, message)
|
|
29
|
-
raise e.class, "#{e.message} -- #{message}", e.backtrace
|
|
27
|
+
handle_exception_on_column(e, column, record)
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
def get_column_method(record, column)
|
|
@@ -65,9 +63,7 @@ module ActiveScaffold
|
|
|
65
63
|
text
|
|
66
64
|
end
|
|
67
65
|
rescue StandardError => e
|
|
68
|
-
|
|
69
|
-
ActiveScaffold.log_exception(e, message)
|
|
70
|
-
raise e.class, "#{e.message} -- #{message}", e.backtrace
|
|
66
|
+
handle_exception_on_column(e, column, record)
|
|
71
67
|
end
|
|
72
68
|
|
|
73
69
|
def column_wrap_tag
|
|
@@ -110,6 +106,27 @@ module ActiveScaffold
|
|
|
110
106
|
check_box(:record, column.name, options)
|
|
111
107
|
end
|
|
112
108
|
|
|
109
|
+
def active_scaffold_column_checkboxes(record, column, ui_options: column.options)
|
|
110
|
+
return format_column_value(record, column) if column.association
|
|
111
|
+
|
|
112
|
+
values = record.send(column.name)
|
|
113
|
+
return if values.empty?
|
|
114
|
+
|
|
115
|
+
content_tag :ul, safe_join(values.map { |v| content_tag(:li, convert_value_to_label(column, v)) })
|
|
116
|
+
end
|
|
117
|
+
alias active_scaffold_column_select_multiple active_scaffold_column_checkboxes
|
|
118
|
+
alias active_scaffold_column_draggable active_scaffold_column_checkboxes
|
|
119
|
+
|
|
120
|
+
def active_scaffold_column_select(record, column, ui_options: column.options)
|
|
121
|
+
unless column.association
|
|
122
|
+
return active_scaffold_column_select_multiple(record, column, ui_options: ui_options) if ui_options[:multiple]
|
|
123
|
+
|
|
124
|
+
value = convert_value_to_label(column, record.send(column.name))
|
|
125
|
+
end
|
|
126
|
+
format_column_value(record, column, value)
|
|
127
|
+
end
|
|
128
|
+
alias active_scaffold_column_radio active_scaffold_column_select
|
|
129
|
+
|
|
113
130
|
def active_scaffold_column_boolean(record, column, ui_options: column.options)
|
|
114
131
|
value = record.send(column.name)
|
|
115
132
|
if value.nil? && ui_options[:include_blank]
|
|
@@ -181,17 +198,20 @@ module ActiveScaffold
|
|
|
181
198
|
value || record.send(column.name)
|
|
182
199
|
end
|
|
183
200
|
|
|
201
|
+
def convert_value_to_label(column, value, options = nil)
|
|
202
|
+
options ||= (column.form_ui_options || column.options)&.dig(:options)
|
|
203
|
+
return value if options.blank?
|
|
204
|
+
|
|
205
|
+
text, val = options.find { |t, v| (v.nil? ? t : v).to_s == value.to_s }
|
|
206
|
+
text ? active_scaffold_translated_option(column, text, val).first : value
|
|
207
|
+
end
|
|
208
|
+
|
|
184
209
|
FORM_UI_WITH_OPTIONS = %i[select radio].freeze
|
|
185
210
|
def format_column_value(record, column, value = nil)
|
|
186
211
|
value ||= read_value_from_record(record, column) unless record.nil?
|
|
187
212
|
if grouped_search? && column == search_group_column && (search_group_function || search_group_column.group_by)
|
|
188
213
|
format_grouped_search_column(value, column.options)
|
|
189
214
|
elsif column.association.nil?
|
|
190
|
-
form_ui_options = column.form_ui_options || column.options if FORM_UI_WITH_OPTIONS.include?(column.form_ui)
|
|
191
|
-
if form_ui_options&.dig(:options)
|
|
192
|
-
text, val = form_ui_options[:options].find { |t, v| (v.nil? ? t : v).to_s == value.to_s }
|
|
193
|
-
value = active_scaffold_translated_option(column, text, val).first if text
|
|
194
|
-
end
|
|
195
215
|
if value.is_a? Numeric
|
|
196
216
|
format_number_value(value, column.options)
|
|
197
217
|
else
|
|
@@ -45,9 +45,7 @@ module ActiveScaffold
|
|
|
45
45
|
text_field(:record, column.name, options.merge(column.options))
|
|
46
46
|
end
|
|
47
47
|
rescue StandardError => e
|
|
48
|
-
|
|
49
|
-
ActiveScaffold.log_exception(e, message)
|
|
50
|
-
raise e.class, "#{e.message} -- #{message}", e.backtrace
|
|
48
|
+
handle_exception_on_column(e, column)
|
|
51
49
|
end
|
|
52
50
|
|
|
53
51
|
# the standard active scaffold options used for class, name and scope
|
|
@@ -84,15 +82,13 @@ module ActiveScaffold
|
|
|
84
82
|
[r.send(method), r.id]
|
|
85
83
|
end
|
|
86
84
|
else
|
|
87
|
-
|
|
88
|
-
select_options = send(enum_options_method, column, record, ui_options: ui_options).collect do |text, value|
|
|
89
|
-
active_scaffold_translated_option(column, text, value)
|
|
90
|
-
end
|
|
85
|
+
select_options = active_scaffold_translated_enum_options(column, record, ui_options: ui_options)
|
|
91
86
|
end
|
|
92
87
|
return as_(:no_options) if select_options.empty?
|
|
93
88
|
|
|
94
89
|
active_scaffold_checkbox_list(column, select_options, associated, options, ui_options: ui_options)
|
|
95
90
|
end
|
|
91
|
+
alias active_scaffold_search_checkboxes active_scaffold_search_multi_select
|
|
96
92
|
|
|
97
93
|
def active_scaffold_search_select(column, html_options, options = {}, ui_options: column.options)
|
|
98
94
|
record = html_options.delete(:object)
|
|
@@ -111,10 +107,7 @@ module ActiveScaffold
|
|
|
111
107
|
select_options = send(helper_method, column.association, false, record)
|
|
112
108
|
else
|
|
113
109
|
method = column.name
|
|
114
|
-
|
|
115
|
-
select_options = send(enum_options_method, column, record, ui_options: ui_options).collect do |text, value|
|
|
116
|
-
active_scaffold_translated_option(column, text, value)
|
|
117
|
-
end
|
|
110
|
+
select_options = active_scaffold_translated_enum_options(column, record, ui_options: ui_options)
|
|
118
111
|
end
|
|
119
112
|
|
|
120
113
|
options = options.merge(selected: associated).merge ui_options
|
|
@@ -143,7 +136,7 @@ module ActiveScaffold
|
|
|
143
136
|
end
|
|
144
137
|
|
|
145
138
|
def active_scaffold_search_draggable(column, options, ui_options: column.options)
|
|
146
|
-
|
|
139
|
+
active_scaffold_search_checkboxes(column, options.merge(draggable_lists: true), ui_options: ui_options)
|
|
147
140
|
end
|
|
148
141
|
|
|
149
142
|
def active_scaffold_search_text(column, options, ui_options: column.options)
|