effective_datatables 3.6.3 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/app/assets/javascripts/dataTables/locales/en.lang +33 -0
  4. data/app/assets/javascripts/dataTables/locales/es.lang +36 -0
  5. data/app/assets/javascripts/dataTables/locales/nl.lang +30 -0
  6. data/app/assets/javascripts/effective_datatables/filters.js.coffee +1 -0
  7. data/app/assets/javascripts/effective_datatables/flash.js.coffee +31 -0
  8. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +41 -53
  9. data/app/assets/javascripts/effective_datatables/inline_crud.js.coffee +217 -0
  10. data/app/assets/javascripts/effective_datatables/overrides.js.coffee +7 -0
  11. data/app/assets/javascripts/effective_datatables/reorder.js.coffee +43 -0
  12. data/app/assets/javascripts/effective_datatables/reset.js.coffee +1 -1
  13. data/app/assets/stylesheets/effective_datatables/_overrides.scss +28 -0
  14. data/app/controllers/effective/datatables_controller.rb +39 -6
  15. data/app/datatables/effective_style_guide_datatable.rb +47 -0
  16. data/app/helpers/effective_datatables_helper.rb +49 -56
  17. data/app/helpers/effective_datatables_private_helper.rb +137 -11
  18. data/app/models/effective/datatable.rb +36 -16
  19. data/app/models/effective/datatable_column.rb +1 -0
  20. data/app/models/effective/datatable_value_tool.rb +20 -20
  21. data/app/models/effective/effective_datatable/attributes.rb +5 -13
  22. data/app/models/effective/effective_datatable/collection.rb +18 -3
  23. data/app/models/effective/effective_datatable/compute.rb +15 -6
  24. data/app/models/effective/effective_datatable/cookie.rb +19 -18
  25. data/app/models/effective/effective_datatable/dsl.rb +8 -3
  26. data/app/models/effective/effective_datatable/dsl/bulk_actions.rb +16 -23
  27. data/app/models/effective/effective_datatable/dsl/datatable.rb +70 -28
  28. data/app/models/effective/effective_datatable/dsl/filters.rb +12 -4
  29. data/app/models/effective/effective_datatable/format.rb +1 -4
  30. data/app/models/effective/effective_datatable/params.rb +9 -4
  31. data/app/models/effective/effective_datatable/resource.rb +129 -74
  32. data/app/models/effective/effective_datatable/state.rb +30 -15
  33. data/app/views/effective/datatables/_bulk_actions_dropdown.html.haml +3 -5
  34. data/app/views/effective/datatables/_datatable.html.haml +3 -5
  35. data/app/views/effective/datatables/_filters.html.haml +4 -24
  36. data/app/views/effective/datatables/_reorder_column.html.haml +5 -0
  37. data/app/views/effective/style_guide/_effective_datatables.html.haml +1 -0
  38. data/config/effective_datatables.rb +8 -21
  39. data/config/locales/en.yml +12 -0
  40. data/config/locales/es.yml +12 -0
  41. data/config/locales/nl.yml +12 -0
  42. data/config/routes.rb +5 -4
  43. data/lib/effective_datatables.rb +49 -2
  44. data/lib/effective_datatables/engine.rb +4 -2
  45. data/lib/effective_datatables/version.rb +1 -1
  46. metadata +17 -5
  47. data/app/views/effective/datatables/_reset.html.haml +0 -2
@@ -0,0 +1,7 @@
1
+ $.extend(true, $.fn.dataTable.Buttons.defaults, {
2
+ dom: {
3
+ button: {
4
+ className: 'btn btn-link btn-sm'
5
+ }
6
+ }
7
+ });
@@ -0,0 +1,43 @@
1
+ reorder = (event, diff, edit) ->
2
+ change = diff.find (obj) -> obj.node == edit.triggerRow.node()
3
+ return unless change?
4
+
5
+ $table = $(event.currentTarget)
6
+ oldNode = $("<div>#{change.oldData}</div>").find('input[data-reorder-resource]')
7
+ newNode = $("<div>#{change.newData}</div>").find('input[data-reorder-resource]')
8
+ return unless oldNode? && newNode?
9
+
10
+ url = @context[0].ajax.url.replace('.json', '/reorder.json')
11
+ data = {'reorder[id]': oldNode.data('reorder-resource'), 'reorder[old]': oldNode.val(), 'reorder[new]': newNode.val(), attributes: $table.data('attributes') }
12
+
13
+ @context[0].rowreorder.c.enable = false
14
+
15
+ $.ajax(
16
+ method: 'post',
17
+ url: url,
18
+ data: data,
19
+ async: false
20
+ ).fail((response, text, status) =>
21
+ $(event.target).closest('table').DataTable().flash(status, 'danger')
22
+ ).always((response) =>
23
+ @context[0].rowreorder.c.enable = true
24
+ )
25
+
26
+ $.fn.DataTable.Api.register('reorder()', reorder);
27
+
28
+ $(document).on 'click', '.dataTables_wrapper a.buttons-reorder', (event) ->
29
+ event.preventDefault() # prevent the click
30
+
31
+ $link = $(event.currentTarget)
32
+ $table = $link.closest('.dataTables_wrapper').find('table.dataTable').first()
33
+
34
+ column = $table.DataTable().column('.col-_reorder')
35
+ return unless column.length > 0
36
+
37
+ if column.visible()
38
+ $table.removeClass('reordering')
39
+ else
40
+ $table.addClass('reordering')
41
+
42
+ column.visible(!column.visible())
43
+
@@ -1,4 +1,4 @@
1
- $(document).on 'click', 'a.buttons-reset-search', (event) ->
1
+ $(document).on 'click', '.dataTables_wrapper a.buttons-reset-search', (event) ->
2
2
  event.preventDefault() # prevent the click
3
3
 
4
4
  $table = $(event.currentTarget).closest('.dataTables_wrapper').find('table.dataTable').first()
@@ -35,6 +35,8 @@ table.dataTable thead {
35
35
  border-bottom: none;
36
36
  white-space: nowrap;
37
37
  padding: 6px;
38
+
39
+ span { display: block; }
38
40
  }
39
41
 
40
42
  .form-control, .form-group {
@@ -194,3 +196,29 @@ table.dataTable {
194
196
  p { margin-bottom: 0px; }
195
197
  }
196
198
  }
199
+
200
+ // Simple styles
201
+ table.dataTable.hide-sort > thead {
202
+ .sorting { background-image: none; cursor: default; }
203
+ .sorting_asc { background-image: none; cursor: default; }
204
+ .sorting_desc { background-image: none; cursor: default; }
205
+ }
206
+
207
+ table.dataTable.hide-search > thead {
208
+ .form-group { display: none; }
209
+ }
210
+
211
+ table.dataTable.hide-buttons {
212
+ .col-bulk_actions { display: none; }
213
+ }
214
+
215
+ // Datatables entries
216
+ @media screen and (max-width: 767px) {
217
+ div.dataTables_wrapper div.dataTables_entries { text-align: center; }
218
+ }
219
+
220
+ .dataTables_buttons {
221
+ .buttons-bulk-actions {
222
+ button { font-size: 12px; }
223
+ }
224
+ }
@@ -5,7 +5,7 @@ module Effective
5
5
  # This will respond to both a GET and a POST
6
6
  def show
7
7
  begin
8
- @datatable = find_datatable(params[:id]).try(:new) || raise('unable to find datatable')
8
+ @datatable = EffectiveDatatables.find(params[:id], params[:attributes])
9
9
  @datatable.view = view_context
10
10
 
11
11
  EffectiveDatatables.authorize!(self, :index, @datatable.collection_class)
@@ -13,18 +13,51 @@ module Effective
13
13
  render json: @datatable.to_json
14
14
  rescue => e
15
15
  EffectiveDatatables.authorized?(self, :index, @datatable.try(:collection_class))
16
-
17
16
  render json: error_json(e)
17
+
18
+ ExceptionNotifier.notify_exception(e) if defined?(ExceptionNotifier)
19
+ raise e if Rails.env.development?
18
20
  end
19
21
  end
20
22
 
21
- private
23
+ def reorder
24
+ begin
25
+ @datatable = EffectiveDatatables.find(params[:id], params[:attributes])
26
+ @datatable.view = view_context
27
+
28
+ EffectiveDatatables.authorize!(self, :index, @datatable.collection_class)
29
+
30
+ @resource = @datatable.collection.find(params[:reorder][:id])
31
+
32
+ EffectiveDatatables.authorize!(self, :update, @resource)
33
+
34
+ attribute = @datatable.columns[:_reorder][:reorder]
35
+ old_index = params[:reorder][:old].to_i
36
+ new_index = params[:reorder][:new].to_i
37
+
38
+ @resource.class.transaction do
39
+ if new_index > old_index
40
+ @datatable.collection.where("#{attribute} > ? AND #{attribute} <= ?", old_index, new_index).update_all("#{attribute} = #{attribute} - 1")
41
+ @resource.update_column(attribute, new_index)
42
+ end
43
+
44
+ if old_index > new_index
45
+ @datatable.collection.where("#{attribute} >= ? AND #{attribute} < ?", new_index, old_index).update_all("#{attribute} = #{attribute} + 1")
46
+ @resource.update_column(attribute, new_index)
47
+ end
48
+ end
49
+
50
+ render status: :ok, body: :ok
51
+ rescue Effective::AccessDenied => e
52
+ render(status: :unauthorized, body: 'Access Denied')
53
+ rescue => e
54
+ render(status: :error, body: 'Unexpected Error')
55
+ end
22
56
 
23
- def find_datatable(id)
24
- id = id.to_s.gsub(/-\d+\z/, '').gsub('-', '/')
25
- id.classify.safe_constantize || id.classify.pluralize.safe_constantize
26
57
  end
27
58
 
59
+ private
60
+
28
61
  def error_json(e)
29
62
  {
30
63
  data: [],
@@ -0,0 +1,47 @@
1
+ class EffectiveStyleGuideDatatable < Effective::Datatable
2
+ datatable do
3
+ length 10
4
+
5
+ col :id
6
+ col :material, search: { collection: ['Stainless Steel', 'Copper', 'Cast Iron', 'Composite'] }
7
+ col :bowl, search: { collection: ['Single Bowl', 'Double Bowl', 'Triple Bowl'] }
8
+ col :name
9
+ col :date, as: :date
10
+
11
+ actions_col
12
+ end
13
+
14
+ # Set the permission check to the same as Effective::StyleGuide
15
+ def collection_class
16
+ defined?(Effective::StyleGuide) ? Effective::StyleGuide : super
17
+ end
18
+
19
+ collection do
20
+ now = Time.zone.now
21
+ [
22
+ [1, 'Stainless Steel', 'Single Bowl', 'KOHLER Staccato', (now + 1.day)],
23
+ [2, 'Stainless Steel', 'Double Bowl', 'KOHLER Vault Undercounter', (now + 1.day)],
24
+ [3, 'Stainless Steel', 'Triple Bowl', 'KRAUS All-In-One', (now + 1.day)],
25
+ [4, 'Stainless Steel', 'Single Bowl', 'KOHLER Vault Dual Mount', (now + 1.day)],
26
+ [5, 'Stainless Steel', 'Single Bowl', 'KRAUS All-In-One Undermount', (now + 2.days)],
27
+ [6, 'Stainless Steel', 'Double Bowl', 'Glacier Bay All-in-One', (now + 2.days)],
28
+ [7, 'Stainless Steel', 'Single Bowl', 'Elkay Neptune', (now + 2.days)],
29
+ [8, 'Copper', 'Single Bowl', 'ECOSINKS Apron Front Dual Mount', (now + 2.days)],
30
+ [9, 'Copper', 'Double Bowl', 'ECOSINKS Dual Mount Front Hammered', (now + 2.days)],
31
+ [10, 'Copper', 'Triple Bowl', 'Glarier Bay Undermount', (now + 3.days)],
32
+ [11, 'Copper', 'Single Bowl', 'Whitehaus Undermount', (now + 3.days)],
33
+ [12, 'Copper', 'Double Bowl', 'Belle Foret Apron Front', (now + 3.days)],
34
+ [13, 'Copper', 'Double Bowl', 'Pegasus Dual Mount', (now + 3.days)],
35
+ [14, 'Cast Iron', 'Double Bowl', 'KOHLER Whitehaven', (now + 3.days)],
36
+ [15, 'Cast Iron', 'Triple Bowl', 'KOHLER Hartland', (now + 3.days)],
37
+ [16, 'Cast Iron', 'Single Bowl', 'KOHLER Cape Dory Undercounter', (now + 4.days)],
38
+ [17, 'Cast Iron', 'Double Bowl', 'KOLER Bakersfield', (now + 4.days)],
39
+ [18, 'Cast Iron', 'Double Bowl', 'American Standard Offset', (now + 4.days)],
40
+ [19, 'Cast Iron', 'Single Bowl', 'Brookfield Top', (now + 4.days)],
41
+ [20, 'Composite', 'Single Bowl', 'Blanco Diamond Undermount', (now + 5.days)],
42
+ [21, 'Composite', 'Double Bowl', 'Mont Blanc Waterbrook', (now + 5.days)],
43
+ [22, 'Composite', 'Triple Bowl', 'Pegasus Triple Mount', (now + 5.days)],
44
+ [23, 'Composite', 'Single Bowl', 'Swanstone Dual Mount', (now + 5.days)]
45
+ ]
46
+ end
47
+ end
@@ -1,8 +1,15 @@
1
1
  # These are expected to be called by a developer. They are part of the datatables DSL.
2
2
  module EffectiveDatatablesHelper
3
-
4
- def render_datatable(datatable, input_js: {}, buttons: true, charts: true, filters: true, simple: false)
3
+ def render_datatable(datatable, input_js: {}, buttons: true, charts: true, entries: true, filters: true, inline: false, pagination: true, search: true, simple: false, sort: true)
5
4
  raise 'expected datatable to be present' unless datatable
5
+ raise 'expected input_js to be a Hash' unless input_js.kind_of?(Hash)
6
+
7
+ if simple
8
+ buttons = charts = entries = filters = pagination = search = sort = false
9
+ end
10
+
11
+ datatable.attributes[:inline] = true if inline
12
+ datatable.attributes[:sortable] = false unless sort
6
13
 
7
14
  datatable.view ||= self
8
15
 
@@ -13,31 +20,48 @@ module EffectiveDatatablesHelper
13
20
  charts = charts && datatable._charts.present?
14
21
  filters = filters && (datatable._scopes.present? || datatable._filters.present?)
15
22
 
16
- datatable.attributes[:simple] = true if simple
17
- input_js[:buttons] = false if simple || !buttons
23
+ html_class = ['effective-datatable', datatable.html_class, ('hide-sort' unless sort), ('hide-search' unless search), ('hide-buttons' unless buttons)].compact.join(' ')
24
+
25
+ if datatable.reorder? && !buttons
26
+ buttons = true; input_js[:buttons] = false
27
+ end
28
+
29
+ # Build the datatables DOM option
30
+ input_js[:dom] ||= [
31
+ ("<'row'<'col-sm-12 dataTables_buttons'B>>" if buttons),
32
+ "<'row'<'col-sm-12'tr>>",
33
+ ("<'row'" if entries || pagination),
34
+ ("<'col-sm-6 dataTables_entries'il>" if entries),
35
+ ("<'col-sm-6'p>" if pagination),
36
+ (">" if entries || pagination)
37
+ ].compact.join
18
38
 
19
39
  effective_datatable_params = {
20
40
  id: datatable.to_param,
21
- class: ('effective-datatable ' + Array(datatable.table_html_class).join(' ')),
41
+ class: html_class,
22
42
  data: {
43
+ 'attributes' => EffectiveDatatables.encrypt(datatable.attributes),
23
44
  'authenticity-token' => form_authenticity_token,
24
- 'effective-form-inputs' => defined?(EffectiveFormInputs),
25
45
  'bulk-actions' => datatable_bulk_actions(datatable),
26
46
  'columns' => datatable_columns(datatable),
27
- 'cookie' => datatable.cookie_key,
28
47
  'display-length' => datatable.display_length,
29
- 'display-order' => [datatable.order_index, datatable.order_direction].to_json().html_safe,
48
+ 'display-order' => datatable_display_order(datatable),
30
49
  'display-records' => datatable.to_json[:recordsFiltered],
31
50
  'display-start' => datatable.display_start,
32
- 'input-js-options' => (input_js || {}).to_json.html_safe,
33
- 'reset' => datatable_reset(datatable),
34
- 'simple' => datatable.simple?.to_s,
51
+ 'inline' => inline.to_s,
52
+ 'language' => EffectiveDatatables.language(I18n.locale),
53
+ 'options' => input_js.to_json,
54
+ 'reset' => (datatable_reset(datatable) if search),
55
+ 'reorder' => datatable_reorder(datatable),
56
+ 'reorder-index' => (datatable.columns[:_reorder][:index] if datatable.reorder?).to_s,
57
+ 'simple' => simple.to_s,
58
+ 'spinner' => '', # effective_bootstrap
35
59
  'source' => effective_datatables.datatable_path(datatable, {format: 'json'}),
36
60
  'total-records' => datatable.to_json[:recordsTotal]
37
61
  }
38
62
  }
39
63
 
40
- if (charts || filters) && !simple
64
+ if (charts || filters)
41
65
  output = ''.html_safe
42
66
 
43
67
  if charts
@@ -60,59 +84,28 @@ module EffectiveDatatablesHelper
60
84
  end
61
85
  end
62
86
 
63
- def render_simple_datatable(datatable)
64
- raise 'expected datatable to be present' unless datatable
65
-
66
- datatable.view ||= self
67
-
68
- unless EffectiveDatatables.authorized?(controller, :index, datatable.collection_class)
69
- return content_tag(:p, "You are not authorized to view this datatable. (cannot :index, #{datatable.collection_class})")
70
- end
71
-
72
- effective_datatable_params = {
73
- id: datatable.to_param,
74
- class: Array(datatable.table_html_class).join(' '),
75
- data: {}
76
- }
77
-
78
- render(partial: 'effective/datatables/datatable',
79
- locals: { datatable: datatable, effective_datatable_params: effective_datatable_params }
80
- )
87
+ def render_inline_datatable(datatable)
88
+ render_datatable(datatable, inline: true)
81
89
  end
82
90
 
83
- def render_datatable_filters(datatable)
84
- raise 'expected datatable to be present' unless datatable
85
-
86
- datatable.view ||= self
87
- return unless datatable._scopes.present? || datatable._filters.present?
88
-
89
- if datatable._filters_form_required?
90
- render partial: 'effective/datatables/filters', locals: { datatable: datatable }
91
- else
92
- render(partial: 'effective/datatables/filters', locals: { datatable: datatable }).gsub('<form', '<div').gsub('/form>', '/div>').html_safe
93
- end
94
-
91
+ def render_simple_datatable(datatable)
92
+ render_datatable(datatable, simple: true)
95
93
  end
96
94
 
97
- def render_datatable_charts(datatable)
98
- raise 'expected datatable to be present' unless datatable
99
-
100
- datatable.view ||= self
101
- return unless datatable._charts.present?
102
-
103
- datatable._charts.map { |name, _| render_datatable_chart(datatable, name) }.join.html_safe
95
+ def inline_datatable?
96
+ params[:_datatable_id].present? && params[:_datatable_attributes].present?
104
97
  end
105
98
 
106
- def render_datatable_chart(datatable, name)
107
- raise 'expected datatable to be present' unless datatable
99
+ def inline_datatable
100
+ return nil unless inline_datatable?
101
+ return @_inline_datatable if @_inline_datatable
108
102
 
109
- datatable.view ||= self
110
- return unless datatable._charts[name].present?
103
+ datatable = EffectiveDatatables.find(params[:_datatable_id], params[:_datatable_attributes])
104
+ datatable.view = self
111
105
 
112
- chart = datatable._charts[name]
113
- chart_data = datatable.to_json[:charts][name][:data]
106
+ EffectiveDatatables.authorize!(self, :index, datatable.collection_class)
114
107
 
115
- render partial: chart[:partial], locals: { datatable: datatable, chart: chart, chart_data: chart_data }
108
+ @_inline_datatable ||= datatable
116
109
  end
117
110
 
118
111
  end
@@ -1,21 +1,21 @@
1
1
  # These aren't expected to be called by a developer. They are internal methods.
2
+ # These aren't expected to be called by a developer. They are internal methods.
2
3
  module EffectiveDatatablesPrivateHelper
3
4
 
4
5
  # https://datatables.net/reference/option/columns
5
6
  def datatable_columns(datatable)
6
- form = nil
7
- simple_form_for(:datatable_search, url: '#', html: {id: "#{datatable.to_param}-form"}) { |f| form = f }
7
+ sortable = datatable.sortable?
8
8
 
9
9
  datatable.columns.map do |name, opts|
10
10
  {
11
- name: name,
12
- title: content_tag(:span, (opts[:label] == false ? '' : opts[:label]), class: 'search-label'),
13
11
  className: opts[:col_class],
14
- searchHtml: (datatable_search_html(form, name, datatable.state[:search][name], opts) unless datatable.simple?),
12
+ name: name,
15
13
  responsivePriority: opts[:responsive],
16
14
  search: datatable.state[:search][name],
17
- sortable: (opts[:sort] && !datatable.simple?),
18
- visible: datatable.state[:visible][name],
15
+ searchHtml: datatable_search_tag(datatable, name, opts),
16
+ sortable: (opts[:sort] && sortable),
17
+ title: datatable_label_tag(datatable, name, opts),
18
+ visible: datatable.state[:visible][name]
19
19
  }
20
20
  end.to_json.html_safe
21
21
  end
@@ -26,17 +26,71 @@ module EffectiveDatatablesPrivateHelper
26
26
  end
27
27
  end
28
28
 
29
+ def datatable_display_order(datatable)
30
+ (datatable.sortable? ? [datatable.order_index, datatable.order_direction] : false).to_json.html_safe
31
+ end
32
+
29
33
  def datatable_reset(datatable)
30
- render(partial: '/effective/datatables/reset', locals: { datatable: datatable }).gsub("'", '"').html_safe
34
+ link_to(content_tag(:span, t('effective_datatables.reset')), '#', class: 'btn btn-link btn-sm buttons-reset-search')
35
+ end
36
+
37
+ def datatable_reorder(datatable)
38
+ return unless datatable.reorder? && EffectiveDatatables.authorized?(self, :update, datatable.collection_class)
39
+ link_to(content_tag(:span, t('effective_datatables.reorder')), '#', class: 'btn btn-link btn-sm buttons-reorder', disabled: true)
31
40
  end
32
41
 
33
- def datatable_search_html(form, name, value, opts)
42
+ def datatable_new_resource_button(datatable, name, column)
43
+ return unless column[:inline] && (column[:actions][:new] != false)
44
+
45
+ action = { action: :new, class: ['btn', column[:btn_class].presence].compact.join(' '), 'data-remote': true }
46
+
47
+ if column[:actions][:new].kind_of?(Hash) # This might be active_record_array_collection?
48
+ action = action.merge(column[:actions][:new])
49
+
50
+ effective_resource = (datatable.effective_resource || datatable.fallback_effective_resource)
51
+ klass = (column[:actions][:new][:klass] || effective_resource&.klass || datatable.collection_class)
52
+ elsif Array(datatable.effective_resource&.actions).include?(:new)
53
+ effective_resource = datatable.effective_resource
54
+ klass = effective_resource.klass
55
+ else
56
+ return
57
+ end
58
+
59
+ # Will only work if permitted
60
+ render_resource_actions(klass, actions: { t('effective_datatables.new') => action }, effective_resource: effective_resource)
61
+ end
62
+
63
+ def datatable_label_tag(datatable, name, opts)
64
+ case opts[:as]
65
+ when :actions
66
+ content_tag(:span, t('effective_datatables.actions'), style: 'display: none;')
67
+ when :bulk_actions
68
+ content_tag(:span, t('effective_datatables.bulk_actions'), style: 'display: none;')
69
+ when :reorder
70
+ content_tag(:span, t('effective_datatables.reorder'), style: 'display: none;')
71
+ else
72
+ content_tag(:span, opts[:label].presence)
73
+ end
74
+ end
75
+
76
+ def datatable_search_tag(datatable, name, opts)
77
+ return datatable_new_resource_button(datatable, name, opts) if name == :_actions
78
+
79
+ return if opts[:search] == false
80
+
81
+ # Build the search
82
+ @_effective_datatables_form_builder || simple_form_for(:datatable_search, url: '#', html: {id: "#{datatable.to_param}-form"}) { |f| @_effective_datatables_form_builder = f }
83
+ form = @_effective_datatables_form_builder
84
+
34
85
  include_blank = opts[:search].key?(:include_blank) ? opts[:search][:include_blank] : opts[:label]
35
86
  pattern = opts[:search][:pattern]
36
87
  placeholder = opts[:search][:placeholder] || ''
37
88
  title = opts[:search][:title] || opts[:label]
38
89
  wrapper_html = { class: 'datatable_search' }
39
90
 
91
+ collection = opts[:search].delete(:collection)
92
+ value = datatable.state[:search][name]
93
+
40
94
  input_html = {
41
95
  name: nil,
42
96
  value: value,
@@ -83,7 +137,7 @@ module EffectiveDatatablesPrivateHelper
83
137
  when :select, :boolean
84
138
  form.input name, label: false, required: false, value: value,
85
139
  as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_select) ? :effective_select : :select),
86
- collection: opts[:search][:collection],
140
+ collection: collection,
87
141
  selected: opts[:search][:value],
88
142
  multiple: opts[:search][:multiple],
89
143
  grouped: opts[:search][:grouped],
@@ -102,4 +156,76 @@ module EffectiveDatatablesPrivateHelper
102
156
  end
103
157
  end
104
158
 
105
- end
159
+ def render_datatable_filters(datatable)
160
+ raise 'expected datatable to be present' unless datatable
161
+
162
+ datatable.view ||= self
163
+ return unless datatable._scopes.present? || datatable._filters.present?
164
+
165
+ if datatable._filters_form_required?
166
+ render partial: 'effective/datatables/filters', locals: { datatable: datatable }
167
+ else
168
+ render(partial: 'effective/datatables/filters', locals: { datatable: datatable }).gsub('<form', '<div').gsub('/form>', '/div>').html_safe
169
+ end
170
+
171
+ end
172
+
173
+ def datatable_filter_tag(form, datatable, name, opts)
174
+ as = opts[:as].to_s.chomp('_field').to_sym
175
+ value = datatable.state[:filter][name]
176
+ collection = opts[:collection]
177
+ input_html = opts[:input_html] || {}
178
+
179
+ binding.pry
180
+
181
+ form.input name,
182
+ value: value,
183
+ selected: value,
184
+ as: as,
185
+ collection: collection,
186
+ label: opts[:label],
187
+ required: input_html.delete(:required),
188
+ multiple: input_html.delete(:multiple),
189
+ include_blank: input_html.delete(:include_blank),
190
+ group_method: input_html.delete(:group_method),
191
+ group_label_method: input_html.delete(:group_label_method),
192
+ value_method: input_html.delete(:value_method),
193
+ label_method: input_html.delete(:label_method),
194
+ input_html: (({name: ''} unless datatable._filters_form_required?) || {}).merge(input_html),
195
+ input_js: ({ placeholder: ''} if as == :effective_select),
196
+ wrapper_html: {class: 'form-group-sm'}
197
+ end
198
+
199
+ def datatable_scope_tag(form, datatable, opts = {})
200
+ collection = datatable._scopes.map { |name, opts| [opts[:label], name] }
201
+ value = datatable.state[:scope]
202
+
203
+ form.input :scope, label: false, required: false, checked: value,
204
+ as: (defined?(EffectiveFormInputs) ? :effective_radio_buttons : :radio_buttons),
205
+ collection: collection,
206
+ buttons: true,
207
+ wrapper_html: {class: 'btn-group-sm'}
208
+ end
209
+
210
+ def render_datatable_charts(datatable)
211
+ raise 'expected datatable to be present' unless datatable
212
+
213
+ datatable.view ||= self
214
+ return unless datatable._charts.present?
215
+
216
+ datatable._charts.map { |name, _| render_datatable_chart(datatable, name) }.join.html_safe
217
+ end
218
+
219
+ def render_datatable_chart(datatable, name)
220
+ raise 'expected datatable to be present' unless datatable
221
+
222
+ datatable.view ||= self
223
+ return unless datatable._charts[name].present?
224
+
225
+ chart = datatable._charts[name]
226
+ chart_data = datatable.to_json[:charts][name][:data]
227
+
228
+ render partial: chart[:partial], locals: { datatable: datatable, chart: chart, chart_data: chart_data }
229
+ end
230
+
231
+ end