effective_datatables 4.3.4 → 4.3.5

Sign up to get free protection for your applications and to get access to all the features.
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