effective_datatables 4.3.4 → 4.3.5

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -1
  3. data/app/assets/javascripts/dataTables/buttons/buttons.bootstrap4.js +1 -0
  4. data/app/assets/javascripts/dataTables/buttons/buttons.colVis.js +12 -2
  5. data/app/assets/javascripts/dataTables/buttons/buttons.html5.js +58 -6
  6. data/app/assets/javascripts/dataTables/buttons/buttons.print.js +16 -5
  7. data/app/assets/javascripts/dataTables/buttons/dataTables.buttons.js +26 -10
  8. data/app/assets/javascripts/dataTables/rowReorder/dataTables.rowReorder.js +818 -0
  9. data/app/assets/javascripts/dataTables/rowReorder/rowReorder.bootstrap4.js +38 -0
  10. data/app/assets/javascripts/effective_datatables.js +3 -10
  11. data/app/assets/javascripts/effective_datatables/flash.js.coffee +20 -0
  12. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +12 -1
  13. data/app/assets/javascripts/effective_datatables/overrides.js.coffee +0 -24
  14. data/app/assets/javascripts/effective_datatables/reorder.js.coffee +37 -0
  15. data/app/assets/stylesheets/dataTables/buttons/buttons.bootstrap4.css +6 -0
  16. data/app/assets/stylesheets/dataTables/rowReorder/rowReorder.bootstrap4.css +22 -0
  17. data/app/assets/stylesheets/effective_datatables.scss +1 -0
  18. data/app/assets/stylesheets/effective_datatables/_overrides.scss +15 -2
  19. data/app/controllers/effective/datatables_controller.rb +25 -0
  20. data/app/helpers/effective_datatables_helper.rb +3 -1
  21. data/app/helpers/effective_datatables_private_helper.rb +28 -4
  22. data/app/models/effective/datatable.rb +23 -3
  23. data/app/models/effective/effective_datatable/dsl.rb +1 -1
  24. data/app/models/effective/effective_datatable/dsl/datatable.rb +65 -26
  25. data/app/models/effective/effective_datatable/format.rb +7 -4
  26. data/app/models/effective/effective_datatable/resource.rb +0 -2
  27. data/app/models/effective/effective_datatable/state.rb +15 -8
  28. data/app/views/effective/datatables/_reorder_column.html.haml +5 -0
  29. data/config/routes.rb +1 -0
  30. data/lib/effective_datatables/version.rb +1 -1
  31. metadata +8 -2
@@ -0,0 +1,38 @@
1
+ /*! Bootstrap 4 styling wrapper for RowReorder
2
+ * ©2018 SpryMedia Ltd - datatables.net/license
3
+ */
4
+
5
+ (function( factory ){
6
+ if ( typeof define === 'function' && define.amd ) {
7
+ // AMD
8
+ define( ['jquery', 'datatables.net-bs4', 'datatables.net-rowreorder'], function ( $ ) {
9
+ return factory( $, window, document );
10
+ } );
11
+ }
12
+ else if ( typeof exports === 'object' ) {
13
+ // CommonJS
14
+ module.exports = function (root, $) {
15
+ if ( ! root ) {
16
+ root = window;
17
+ }
18
+
19
+ if ( ! $ || ! $.fn.dataTable ) {
20
+ $ = require('datatables.net-bs4')(root, $).$;
21
+ }
22
+
23
+ if ( ! $.fn.dataTable.RowReorder ) {
24
+ require('datatables.net-rowreorder')(root, $);
25
+ }
26
+
27
+ return factory( $, root, root.document );
28
+ };
29
+ }
30
+ else {
31
+ // Browser
32
+ factory( jQuery, window, document );
33
+ }
34
+ }(function( $, window, document, undefined ) {
35
+
36
+ return $.fn.dataTable;
37
+
38
+ }));
@@ -11,14 +11,7 @@
11
11
  //= require dataTables/buttons/buttons.print
12
12
  //= require dataTables/responsive/dataTables.responsive
13
13
  //= require dataTables/responsive/responsive.bootstrap4
14
+ //= require dataTables/rowReorder/dataTables.rowReorder
15
+ //= require dataTables/rowReorder/rowReorder.bootstrap4
14
16
 
15
- //= require effective_datatables/bulk_actions
16
- //= require effective_datatables/events
17
- //= require effective_datatables/filters
18
- //= require effective_datatables/inline_crud
19
- //= require effective_datatables/reset
20
- //= require effective_datatables/responsive
21
- //= require effective_datatables/overrides
22
-
23
- //= require effective_datatables/charts
24
- //= require effective_datatables/initialize
17
+ //= require_tree ./effective_datatables
@@ -0,0 +1,20 @@
1
+ flash = (message) ->
2
+ @context[0].oFeatures.bProcessing = false
3
+
4
+ message ||= 'Processing...'
5
+
6
+ $processing = $(@table().node()).siblings('.dataTables_processing')
7
+ $processing.html(message).show()
8
+
9
+ timeout = $processing.data('timeout')
10
+ clearTimeout(timeout) if timeout
11
+
12
+ $processing.html(message).data('timeout', setTimeout( =>
13
+ $processing.html('Processing...').hide()
14
+ @context[0].oFeatures.bProcessing = true
15
+ , 1500)
16
+ )
17
+
18
+ return @
19
+
20
+ $.fn.DataTable.Api.register('flash()', flash);
@@ -4,6 +4,7 @@ initializeDataTables = ->
4
4
  options = datatable.data('options') || {}
5
5
  buttons_export_columns = options['buttons_export_columns'] || ':not(.col-actions)'
6
6
  simple = ('' + datatable.data('simple') == 'true')
7
+ reorder = datatable.data('reorder')
7
8
 
8
9
  if options['buttons'] == false
9
10
  options['buttons'] = []
@@ -16,7 +17,8 @@ initializeDataTables = ->
16
17
  extend: 'colvis',
17
18
  text: 'Show / Hide',
18
19
  postfixButtons: [
19
- { extend: 'colvisGroup', text: 'Show all', show: ':hidden'},
20
+ { extend: 'colvisGroup', text: 'Show all', show: ':hidden', className: 'buttons-colvisGroup-first'},
21
+ { extend: 'colvisGroup', text: 'Show none', hide: ':visible'}
20
22
  { extend: 'colvisRestore', text: 'Show default'}
21
23
  ]
22
24
  },
@@ -102,6 +104,9 @@ initializeDataTables = ->
102
104
  if $table.data('reset')
103
105
  $buttons.prepend($table.data('reset'))
104
106
 
107
+ if $table.data('reorder')
108
+ $buttons.prepend($table.data('reorder'))
109
+
105
110
  if $table.data('bulk-actions')
106
111
  $buttons.prepend($table.data('bulk-actions'))
107
112
 
@@ -160,6 +165,9 @@ initializeDataTables = ->
160
165
  init_options['dom'] = "<'row'<'col-sm-12'tr>>" # Just show the table
161
166
  datatable.addClass('simple')
162
167
 
168
+ if reorder
169
+ init_options['rowReorder'] = { selector: 'td.col-_reorder', snapX: true, dataSrc: datatable.data('reorder-index') }
170
+
163
171
  # Let's actually initialize the table now
164
172
  table = datatable.dataTable(jQuery.extend(init_options, options))
165
173
 
@@ -169,6 +177,9 @@ initializeDataTables = ->
169
177
  # Apply EffectiveFormInputs to the Show x per page dropdown
170
178
  try table.closest('.dataTables_wrapper').find('.dataTables_length select').removeAttr('name').select2(minimumResultsForSearch: 100)
171
179
 
180
+ if reorder
181
+ table.DataTable().on('row-reorder', (event, diff, edit) -> $(event.target).DataTable().reorder(event, diff, edit))
182
+
172
183
  table.addClass('initialized')
173
184
 
174
185
  destroyDataTables = ->
@@ -5,27 +5,3 @@ $.extend(true, $.fn.dataTable.Buttons.defaults, {
5
5
  }
6
6
  }
7
7
  });
8
-
9
- # DataTable is the API
10
- # dataTable is the object fnStuff
11
-
12
- flash = (message) ->
13
- @context[0].oFeatures.bProcessing = false
14
-
15
- message ||= 'Processing...'
16
-
17
- $processing = $(@table().node()).siblings('.dataTables_processing')
18
- $processing.html(message).show()
19
-
20
- timeout = $processing.data('timeout')
21
- clearTimeout(timeout) if timeout
22
-
23
- $processing.html(message).data('timeout', setTimeout( =>
24
- $processing.html('Processing...').hide()
25
- @context[0].oFeatures.bProcessing = true
26
- , 1500)
27
- )
28
-
29
- return @
30
-
31
- $.fn.DataTable.Api.register('flash()', flash);
@@ -0,0 +1,37 @@
1
+ reorder = (event, diff, edit) ->
2
+ change = diff.find (obj) -> obj.node == edit.triggerRow.node()
3
+ return unless change?
4
+
5
+ oldNode = $("<div>#{change.oldData}</div>").find('input[data-reorder-resource]')
6
+ newNode = $("<div>#{change.newData}</div>").find('input[data-reorder-resource]')
7
+ return unless oldNode? && newNode?
8
+
9
+ url = @context[0].ajax.url.replace('.json', '/reorder.json')
10
+ data = {'reorder[id]': oldNode.data('reorder-resource'), 'reorder[old]': oldNode.val(), 'reorder[new]': newNode.val()}
11
+
12
+ @context[0].rowreorder.c.enable = false
13
+
14
+ $.ajax(
15
+ method: 'post',
16
+ url: url,
17
+ data: data,
18
+ async: false
19
+ ).fail((response, text, status) =>
20
+ $(event.target).closest('table').DataTable().flash(status)
21
+ ).always((response) =>
22
+ @context[0].rowreorder.c.enable = true
23
+ )
24
+
25
+ $.fn.DataTable.Api.register('reorder()', reorder);
26
+
27
+ $(document).on 'click', '.dataTables_wrapper a.buttons-reorder', (event) ->
28
+ event.preventDefault() # prevent the click
29
+
30
+ $link = $(event.currentTarget)
31
+ $table = $link.closest('.dataTables_wrapper').find('table.dataTable').first()
32
+
33
+ column = $table.DataTable().column('.col-_reorder')
34
+ return unless column.length > 0
35
+
36
+ column.visible(!column.visible())
37
+
@@ -52,6 +52,12 @@ div.dt-button-info > div {
52
52
  padding: 1em;
53
53
  }
54
54
 
55
+ div.dt-button-collection-title {
56
+ text-align: center;
57
+ padding: 0.3em 0 0.5em;
58
+ font-size: 0.9em;
59
+ }
60
+
55
61
  ul.dt-button-collection.dropdown-menu {
56
62
  display: block;
57
63
  z-index: 2002;
@@ -0,0 +1,22 @@
1
+ table.dt-rowReorder-float {
2
+ position: absolute !important;
3
+ opacity: 0.8;
4
+ table-layout: fixed;
5
+ outline: 2px solid #0275d8;
6
+ outline-offset: -2px;
7
+ z-index: 2001;
8
+ }
9
+
10
+ tr.dt-rowReorder-moving {
11
+ outline: 2px solid #888;
12
+ outline-offset: -2px;
13
+ }
14
+
15
+ body.dt-rowReorder-noOverflow {
16
+ overflow-x: hidden;
17
+ }
18
+
19
+ table.dataTable td.reorder {
20
+ text-align: center;
21
+ cursor: move;
22
+ }
@@ -2,5 +2,6 @@
2
2
 
3
3
  @import 'dataTables/buttons/buttons.bootstrap4';
4
4
  @import 'dataTables/responsive/responsive.bootstrap4';
5
+ @import 'dataTables/rowReorder/rowReorder.bootstrap4';
5
6
 
6
7
  @import 'effective_datatables/overrides';
@@ -52,7 +52,6 @@ table.dataTable > thead {
52
52
  }
53
53
 
54
54
  table.dataTable > thead th {
55
- vertical-align: top;
56
55
  background-repeat: no-repeat;
57
56
 
58
57
  > span { padding-right: 1rem; }
@@ -95,6 +94,13 @@ table.dataTable > tbody > tr.child {
95
94
  .btn { font-size: 0.75rem; }
96
95
  }
97
96
 
97
+ div.dt-button-collection-title { padding: 0px; }
98
+
99
+ .dt-button.buttons-colvisGroup-first {
100
+ padding-top: 0.5rem;
101
+ border-top: 1px solid #dee2e6;
102
+ }
103
+
98
104
  // Search bar
99
105
  table.dataTable > thead {
100
106
  tr th {
@@ -113,10 +119,13 @@ table.dataTable > thead {
113
119
  cursor: pointer;
114
120
  }
115
121
  }
116
-
117
122
  }
118
123
  }
119
124
 
125
+ table.dataTable.collapsed {
126
+ th.col-bulk_actions:first-child { div { margin-left: 18px; } }
127
+ }
128
+
120
129
  // Processing div
121
130
  div.dataTables_wrapper div.dataTables_processing {
122
131
  position: absolute;
@@ -198,6 +207,10 @@ table.dataTable {
198
207
  text-align: right;
199
208
  }
200
209
 
210
+ td.col-reorder {
211
+ cursor: move;
212
+ }
213
+
201
214
  td.col-resource_item { word-break: keep-all; }
202
215
 
203
216
  td {
@@ -18,7 +18,32 @@ module Effective
18
18
  ExceptionNotifier.notify_exception(e) if defined?(ExceptionNotifier)
19
19
  raise e if Rails.env.development?
20
20
  end
21
+ end
22
+
23
+ def reorder
24
+ @datatable = EffectiveDatatables.find(params[:id])
25
+ @datatable.view = view_context
26
+
27
+ @resource = @datatable.collection.find(params[:reorder][:id])
28
+ EffectiveDatatables.authorize!(self, :update, @resource)
29
+
30
+ attribute = @datatable.columns[:_reorder][:reorder]
31
+ old_index = params[:reorder][:old].to_i
32
+ new_index = params[:reorder][:new].to_i
33
+
34
+ @resource.class.transaction do
35
+ if new_index > old_index
36
+ @datatable.collection.where("#{attribute} > ? AND #{attribute} <= ?", old_index, new_index).update_all("#{attribute} = #{attribute} - 1")
37
+ @resource.update_column(attribute, new_index)
38
+ end
39
+
40
+ if old_index > new_index
41
+ @datatable.collection.where("#{attribute} >= ? AND #{attribute} < ?", new_index, old_index).update_all("#{attribute} = #{attribute} + 1")
42
+ @resource.update_column(attribute, new_index)
43
+ end
44
+ end
21
45
 
46
+ render status: :ok, body: :ok
22
47
  end
23
48
 
24
49
  private
@@ -26,12 +26,14 @@ module EffectiveDatatablesHelper
26
26
  'columns' => datatable_columns(datatable),
27
27
  'cookie' => datatable.cookie_key,
28
28
  'display-length' => datatable.display_length,
29
- 'display-order' => [datatable.order_index, datatable.order_direction].to_json().html_safe,
29
+ 'display-order' => datatable_display_order(datatable),
30
30
  'display-records' => datatable.to_json[:recordsFiltered],
31
31
  'display-start' => datatable.display_start,
32
32
  'inline' => datatable.inline?.to_s,
33
33
  'options' => (input_js || {}).to_json.html_safe,
34
34
  'reset' => datatable_reset(datatable),
35
+ 'reorder' => datatable_reorder(datatable),
36
+ 'reorder-index' => (datatable.columns[:_reorder][:index] if datatable.reorder?).to_s,
35
37
  'simple' => datatable.simple?.to_s,
36
38
  'spinner' => icon('spinner'), # effective_bootstrap
37
39
  'source' => effective_datatables.datatable_path(datatable, {format: 'json'}),
@@ -3,16 +3,18 @@ module EffectiveDatatablesPrivateHelper
3
3
 
4
4
  # https://datatables.net/reference/option/columns
5
5
  def datatable_columns(datatable)
6
+ sortable = datatable.sortable?
7
+
6
8
  datatable.columns.map do |name, opts|
7
9
  {
8
- name: name,
9
- title: content_tag(:span, opts[:label].presence),
10
10
  className: opts[:col_class],
11
+ name: name,
11
12
  responsivePriority: opts[:responsive],
12
13
  search: datatable.state[:search][name],
13
14
  searchHtml: datatable_search_tag(datatable, name, opts),
14
- sortable: (opts[:sort] && !datatable.simple?),
15
- visible: datatable.state[:visible][name],
15
+ sortable: (opts[:sort] && sortable),
16
+ title: datatable_label_tag(datatable, name, opts),
17
+ visible: datatable.state[:visible][name]
16
18
  }
17
19
  end.to_json.html_safe
18
20
  end
@@ -23,10 +25,19 @@ module EffectiveDatatablesPrivateHelper
23
25
  end
24
26
  end
25
27
 
28
+ def datatable_display_order(datatable)
29
+ (datatable.sortable? ? [datatable.order_index, datatable.order_direction] : false).to_json.html_safe
30
+ end
31
+
26
32
  def datatable_reset(datatable)
27
33
  link_to(content_tag(:span, 'Reset'), '#', class: 'btn btn-link btn-sm buttons-reset-search')
28
34
  end
29
35
 
36
+ def datatable_reorder(datatable)
37
+ return false unless datatable.reorder?
38
+ link_to(content_tag(:span, 'Reorder'), '#', class: 'btn btn-link btn-sm buttons-reorder')
39
+ end
40
+
30
41
  def datatable_new_resource_button(datatable, name, column)
31
42
  if column[:inline] && column[:actions][:new] != false
32
43
  actions = {'New' => { action: :new, class: 'btn btn-outline-primary', 'data-remote': true } }
@@ -34,6 +45,19 @@ module EffectiveDatatablesPrivateHelper
34
45
  end
35
46
  end
36
47
 
48
+ def datatable_label_tag(datatable, name, opts)
49
+ case opts[:as]
50
+ when :actions
51
+ content_tag(:span, 'Actions', style: 'display: none;')
52
+ when :bulk_actions
53
+ content_tag(:span, 'Bulk Actions', style: 'display: none;')
54
+ when :reorder
55
+ content_tag(:span, 'Reorder', style: 'display: none;')
56
+ else
57
+ content_tag(:span, opts[:label].presence)
58
+ end
59
+ end
60
+
37
61
  def datatable_search_tag(datatable, name, opts)
38
62
  return datatable_new_resource_button(datatable, name, opts) if name == :_actions
39
63
 
@@ -63,12 +63,14 @@ module Effective
63
63
  load_filters!
64
64
  load_state!
65
65
 
66
+ # Bulk actions called first so it can add the bulk_actions_col first
67
+ initialize_bulk_actions if respond_to?(:initialize_bulk_actions)
68
+
66
69
  # Now we initialize all the columns. columns knows about attributes and filters and scope
67
70
  initialize_datatable if respond_to?(:initialize_datatable)
68
71
  load_columns!
69
72
 
70
73
  # Execute any additional DSL methods
71
- initialize_bulk_actions if respond_to?(:initialize_bulk_actions)
72
74
  initialize_charts if respond_to?(:initialize_charts)
73
75
 
74
76
  # Load the collection. This is the first time def collection is called on the Datatable itself
@@ -77,10 +79,13 @@ module Effective
77
79
 
78
80
  # Figure out the class, and if it's activerecord, do all the resource discovery on it
79
81
  load_resource!
80
-
81
- # If attributes match a belongs_to column, scope the collection and remove the column
82
+ load_resource_search!
82
83
  apply_belongs_to_attributes!
83
84
 
85
+ # Check everything is okay
86
+ validate_datatable!
87
+
88
+ # Save for next time
84
89
  save_cookie!
85
90
  end
86
91
 
@@ -129,6 +134,14 @@ module Effective
129
134
  attributes[:inline] == true
130
135
  end
131
136
 
137
+ def reorder?
138
+ columns.key?(:_reorder)
139
+ end
140
+
141
+ def sortable?
142
+ !simple? && !reorder?
143
+ end
144
+
132
145
  # Whether the filters must be rendered as a <form> or we can keep the normal <div> behaviour
133
146
  def _filters_form_required?
134
147
  _form[:verb].present?
@@ -164,5 +177,12 @@ module Effective
164
177
  @value_tool ||= DatatableValueTool.new(self)
165
178
  end
166
179
 
180
+ def validate_datatable!
181
+ if reorder?
182
+ raise 'cannot use reorder with an Array collection' if array_collection?
183
+ raise 'cannot use reorder with a non-Integer column' if resource.sql_type(columns[:_reorder][:reorder]) != :integer
184
+ end
185
+ end
186
+
167
187
  end
168
188
  end