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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +16 -1
  3. data/README.md +108 -7
  4. data/app/assets/javascripts/{jquery → active_scaffold}/active_scaffold.js +759 -762
  5. data/app/assets/javascripts/{jquery/date_picker_bridge.js.erb → active_scaffold/date_picker_bridge.js} +0 -3
  6. data/app/assets/javascripts/active_scaffold/load.js +102 -0
  7. data/app/assets/javascripts/active_scaffold.js.erb +3 -27
  8. data/app/assets/stylesheets/active_scaffold/_colours.scss +330 -0
  9. data/app/assets/stylesheets/active_scaffold/_images.scss +65 -0
  10. data/app/assets/stylesheets/{active_scaffold_layout.scss → active_scaffold/_layout.scss} +14 -0
  11. data/app/assets/stylesheets/active_scaffold/_variables.scss +194 -0
  12. data/app/assets/stylesheets/active_scaffold/core.scss +15 -0
  13. data/app/assets/stylesheets/active_scaffold.scss.erb +16 -0
  14. data/app/views/active_scaffold_overrides/_field_search_columns.html.erb +8 -0
  15. data/app/views/active_scaffold_overrides/_form.html.erb +8 -0
  16. data/app/views/active_scaffold_overrides/_form_association.html.erb +3 -1
  17. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +4 -2
  18. data/app/views/active_scaffold_overrides/_show_association.html.erb +2 -1
  19. data/app/views/active_scaffold_overrides/_show_association_horizontal.html.erb +2 -2
  20. data/app/views/active_scaffold_overrides/_show_association_vertical.html.erb +1 -1
  21. data/app/views/active_scaffold_overrides/edit_associated.js.erb +10 -8
  22. data/lib/active_scaffold/actions/core.rb +34 -3
  23. data/lib/active_scaffold/assets/css_deps_generator.rb +42 -0
  24. data/lib/active_scaffold/assets/jquery_ui_manifest.rb +77 -0
  25. data/lib/active_scaffold/assets/jquery_ui_theme_generator.rb +102 -0
  26. data/lib/active_scaffold/assets.rb +109 -0
  27. data/lib/active_scaffold/attribute_params.rb +11 -2
  28. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +1 -1
  29. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +1 -1
  30. data/lib/active_scaffold/bridges/chosen.rb +1 -1
  31. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +1 -1
  32. data/lib/active_scaffold/bridges/file_column/form_ui.rb +1 -1
  33. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +1 -1
  34. data/lib/active_scaffold/bridges/record_select/helpers.rb +1 -1
  35. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +1 -0
  36. data/lib/active_scaffold/bridges/tiny_mce.rb +6 -1
  37. data/lib/active_scaffold/bridges.rb +7 -0
  38. data/lib/active_scaffold/config/core.rb +7 -3
  39. data/lib/active_scaffold/constraints.rb +1 -1
  40. data/lib/active_scaffold/data_structures/action_columns.rb +66 -0
  41. data/lib/active_scaffold/data_structures/bridge.rb +2 -0
  42. data/lib/active_scaffold/data_structures/column.rb +3 -0
  43. data/lib/active_scaffold/engine.rb +40 -0
  44. data/lib/active_scaffold/finder.rb +1 -1
  45. data/lib/active_scaffold/helpers/assets_helpers.rb +39 -0
  46. data/lib/active_scaffold/helpers/controller_helpers.rb +1 -1
  47. data/lib/active_scaffold/helpers/form_column_helpers.rb +57 -532
  48. data/lib/active_scaffold/helpers/form_ui_helpers.rb +530 -0
  49. data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -0
  50. data/lib/active_scaffold/helpers/list_column_helpers.rb +31 -11
  51. data/lib/active_scaffold/helpers/search_column_helpers.rb +5 -12
  52. data/lib/active_scaffold/helpers/show_column_helpers.rb +4 -2
  53. data/lib/active_scaffold/helpers/view_helpers.rb +12 -0
  54. data/lib/active_scaffold/railties/tasks.rake +10 -0
  55. data/lib/active_scaffold/testing/assert_embedded_load.rb +33 -0
  56. data/lib/active_scaffold/version.rb +2 -2
  57. data/lib/active_scaffold.rb +7 -2
  58. data/lib/tasks/active_scaffold/assets.rake +42 -0
  59. data/lib/tasks/bundle.rake +25 -0
  60. data/vendor/assets/stylesheets/{jquery-ui-theme.css.erb → jquery-ui-theme.css} +17 -17
  61. metadata +26 -28
  62. data/app/assets/stylesheets/active_scaffold.scss +0 -424
  63. data/app/assets/stylesheets/active_scaffold_extensions.css.erb +0 -2
  64. data/app/assets/stylesheets/active_scaffold_images.scss +0 -65
  65. data/app/assets/stylesheets/active_scaffold_jquery_ui.css.erb +0 -13
  66. /data/app/assets/javascripts/{jquery → active_scaffold}/active_scaffold_chosen.js +0 -0
  67. /data/app/assets/javascripts/{jquery → active_scaffold}/draggable_lists.js +0 -0
  68. /data/app/assets/javascripts/{jquery → active_scaffold}/jquery.editinplace.js +0 -0
  69. /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 = '&nbsp;'.html_safe if value.nil? || value.blank? # fix for IE 6
25
25
  value
26
26
  rescue StandardError => e
27
- message = "on the ActiveScaffold column = :#{column.name} in #{controller.class}, record: #{record.inspect}"
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
- message = "on the ActiveScaffold column = :#{column.name} in #{controller.class}"
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
- message = "on the ActiveScaffold column = :#{column.name} in #{controller.class}"
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
- enum_options_method = override_helper_per_model(:active_scaffold_enum_options, record.class)
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
- enum_options_method = override_helper_per_model(:active_scaffold_enum_options, record.class)
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
- active_scaffold_search_multi_select(column, options.merge(draggable_lists: true), ui_options: ui_options)
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)