effective_datatables 3.6.3 → 3.7.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 (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