diff --git a/.gitignore b/.gitignore
index 9af112d6..061912ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ Gemfile.lock
log/*.log
pkg/
tmp/
+.rubocop-*
diff --git a/README.md b/README.md
index ed4da007..162ab7aa 100644
--- a/README.md
+++ b/README.md
@@ -43,6 +43,7 @@ Please check out [Effective Datatables 3.x](https://github.com/code-and-effect/e
* [bulk_actions_col](#bulk_actions_col)
* [actions_col](#actions_col)
* [length](#length)
+ * [length_menu](#length_menu)
* [order](#order)
* [reorder](#reorder)
* [aggregate](#aggregate)
@@ -50,6 +51,7 @@ Please check out [Effective Datatables 3.x](https://github.com/code-and-effect/e
* [filters](#filters)
* [scope](#scope)
* [filter](#filter)
+ * [filter_date_range](#filter_date_range)
* [bulk_actions](#bulk_actions)
* [bulk_action](#bulk_action)
* [bulk_action](#bulk_action_divider)
@@ -227,7 +229,7 @@ class PostsDatatable < Effective::Datatable
# The user's selected filters, search, sort, length, column visibility and pagination settings are saved between visits
# on a per-table basis and can be Reset with a button
datatable do
- length 25 # 5, 10, 25, 50, 100, 500, :all
+ length 25 # 5, 10, 25, 50, 100, 500
order :updated_at, :desc
# Renders a column of checkboxes to select items for any bulk_actions
@@ -613,7 +615,7 @@ MyApp::UsersTable.new(namespace: :my_app)
## length
-Sets the default number of rows per page. Valid lengths are `5`, `10`, `25`, `50`, `100`, `250`, `500`, `:all`
+Sets the default number of rows per page.
When not specified, effective_datatables uses the default as per the `config/initializers/effective_datatables.rb` or 25.
@@ -621,6 +623,18 @@ When not specified, effective_datatables uses the default as per the `config/ini
length 100
```
+## length_menu
+
+You can specify the length menu values in `config/initializers/effective_datatables.rb` or per datatable.
+
+To override the default, add the method to your datatable
+
+```
+def length_menu
+ [5, 10, 25, 50]
+end
+```
+
## order
Sets the default order of table rows. The first argument is the column, the second the direction.
@@ -791,6 +805,24 @@ required: true|false # Passed to form
Any other option given will be yielded to EffectiveBootstrap as options.
+## filter_date_range
+
+There is also a special date range filter built in. To use:
+
+```ruby
+filters do
+ filter_date_range
+end
+
+collection do
+ Thing.where(updated_at: date_range)
+end
+```
+
+This method creates 3 filters, `filters[:date_range]`, `filters[:start_date]` and `filters[:end_date]` and presents a rough Prev/Next month and year navigation. Do not have any columns named the same as these.
+
+You can pass a default into `filter_date_range`, one of `:current_month`, `:current_year`, `:month`, `:year` and `:custom`.
+
## bulk_actions
Creates a single dropdown menu with a link to each action, download or content.
@@ -1112,6 +1144,27 @@ Above we have `resources :things` for the 7 crud actions. And we add two more me
Your datatable should now have New, Show, Edit, Approve and Reject buttons. Click them for inline functionality.
+## Adding an Ajax member action
+
+To render a member action with an inline datatable:
+
+- Create a "cool_things.html" template and a "_cool_things.html" partial file. Need both.
+
+- The links must be inside an `actions_col` or a `col(:thing, col_class: 'col-actions')` for the javascript to work.
+
+- The action itself just needs to be `data-remote=true`. Try `link_to('Show Cool Things', thing_cool_things_path(thing), 'data-remote': true)`
+
+Make sure the route and permissions are working:
+
+```
+resources :things do
+ get :cool_things, on: :member
+```
+
+and `can?(:cool_things, Thing)`
+
+Good luck.
+
## Troubleshooting Inline
If things aren't working, try the following:
@@ -1292,7 +1345,7 @@ class TimeEntriesPerClientReport < Effective::Datatable
end
datatable do
- length :all
+ length 50
col :client
diff --git a/app/assets/javascripts/dataTables/UPGRADE.md b/app/assets/javascripts/dataTables/UPGRADE.md
new file mode 100644
index 00000000..ba33d80b
--- /dev/null
+++ b/app/assets/javascripts/dataTables/UPGRADE.md
@@ -0,0 +1,17 @@
+# Upgrade
+
+To upgrade the datatables.net source code:
+
+Visit https://datatables.net/download/
+
+Step 1: Choose Bootstrap4
+Step 2:
+ Packages: Just DataTables
+ Extensions: Buttons, Column visibility, HTML5 export, Print view, Responsive, RowReorder
+Step 3: Download do not minify or concatenate
+Step 4: Replace existing javascript and css with downloaded
+
+After replacing the existing JS/CSS:
+
+- Consider the buttons.colVis.js and add back the custom sorted functionality
+- Consider the stylesheets/effective_datatables/overrides.bootstrap4.scss
diff --git a/app/assets/javascripts/dataTables/buttons/buttons.bootstrap4.js b/app/assets/javascripts/dataTables/buttons/buttons.bootstrap4.js
old mode 100755
new mode 100644
index f2dedd9b..11f43be0
--- a/app/assets/javascripts/dataTables/buttons/buttons.bootstrap4.js
+++ b/app/assets/javascripts/dataTables/buttons/buttons.bootstrap4.js
@@ -1,5 +1,5 @@
/*! Bootstrap integration for DataTables' Buttons
- * ©2016 SpryMedia Ltd - datatables.net/license
+ * © SpryMedia Ltd - datatables.net/license
*/
(function( factory ){
@@ -11,21 +11,37 @@
}
else if ( typeof exports === 'object' ) {
// CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
-
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
+ var jq = require('jquery');
+ var cjsRequires = function (root, $) {
+ if ( ! $.fn.dataTable ) {
+ require('datatables.net-bs4')(root, $);
}
if ( ! $.fn.dataTable.Buttons ) {
require('datatables.net-buttons')(root, $);
}
-
- return factory( $, root, root.document );
};
+
+ if (typeof window === 'undefined') {
+ module.exports = function (root, $) {
+ if ( ! root ) {
+ // CommonJS environments without a window global must pass a
+ // root. This will give an error otherwise
+ root = window;
+ }
+
+ if ( ! $ ) {
+ $ = jq( root );
+ }
+
+ cjsRequires( root, $ );
+ return factory( $, root, root.document );
+ };
+ }
+ else {
+ cjsRequires( window, jq );
+ module.exports = factory( jq, window, window.document );
+ }
}
else {
// Browser
@@ -35,29 +51,67 @@
'use strict';
var DataTable = $.fn.dataTable;
-$.extend( true, DataTable.Buttons.defaults, {
+
+
+$.extend(true, DataTable.Buttons.defaults, {
dom: {
container: {
- className: 'dt-buttons btn-group'
+ className: 'dt-buttons btn-group flex-wrap'
},
button: {
- className: 'btn btn-secondary'
+ className: 'btn btn-secondary',
+ active: 'active'
},
collection: {
- tag: 'div',
- className: 'dt-button-collection dropdown-menu',
+ action: {
+ dropHtml: ''
+ },
+ container: {
+ tag: 'div',
+ className: 'dropdown-menu dropdown-menu-right dt-button-collection'
+ },
+ closeButton: false,
button: {
tag: 'a',
className: 'dt-button dropdown-item',
- active: 'active',
- disabled: 'disabled'
+ active: 'dt-button-active',
+ disabled: 'disabled',
+ spacer: {
+ className: 'dropdown-divider',
+ tag: 'hr'
+ }
+ }
+ },
+ split: {
+ action: {
+ tag: 'a',
+ className: 'btn btn-secondary dt-button-split-drop-button',
+ closeButton: false
+ },
+ dropdown: {
+ tag: 'button',
+ dropHtml: '',
+ className:
+ 'btn btn-secondary dt-button-split-drop dropdown-toggle dropdown-toggle-split',
+ closeButton: false,
+ align: 'split-left',
+ splitAlignClass: 'dt-button-split-left'
+ },
+ wrapper: {
+ tag: 'div',
+ className: 'dt-button-split btn-group',
+ closeButton: false
}
}
+ },
+ buttonCreated: function (config, button) {
+ return config.buttons ? $('
').append(button) : button;
}
-} );
+});
DataTable.ext.buttons.collection.className += ' dropdown-toggle';
DataTable.ext.buttons.collection.rightAlignClassName = 'dropdown-menu-right';
-return DataTable.Buttons;
+
+return DataTable;
}));
diff --git a/app/assets/javascripts/dataTables/buttons/buttons.colVis.js b/app/assets/javascripts/dataTables/buttons/buttons.colVis.js
old mode 100755
new mode 100644
index d5ec8de8..19db9dd9
--- a/app/assets/javascripts/dataTables/buttons/buttons.colVis.js
+++ b/app/assets/javascripts/dataTables/buttons/buttons.colVis.js
@@ -1,6 +1,6 @@
/*!
* Column visibility buttons for Buttons and DataTables.
- * 2016 SpryMedia Ltd - datatables.net/license
+ * © SpryMedia Ltd - datatables.net/license
*/
(function( factory ){
@@ -12,21 +12,37 @@
}
else if ( typeof exports === 'object' ) {
// CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
-
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
+ var jq = require('jquery');
+ var cjsRequires = function (root, $) {
+ if ( ! $.fn.dataTable ) {
+ require('datatables.net')(root, $);
}
if ( ! $.fn.dataTable.Buttons ) {
require('datatables.net-buttons')(root, $);
}
-
- return factory( $, root, root.document );
};
+
+ if (typeof window === 'undefined') {
+ module.exports = function (root, $) {
+ if ( ! root ) {
+ // CommonJS environments without a window global must pass a
+ // root. This will give an error otherwise
+ root = window;
+ }
+
+ if ( ! $ ) {
+ $ = jq( root );
+ }
+
+ cjsRequires( root, $ );
+ return factory( $, root, root.document );
+ };
+ }
+ else {
+ cjsRequires( window, jq );
+ module.exports = factory( jq, window, window.document );
+ }
}
else {
// Browser
@@ -37,38 +53,68 @@
var DataTable = $.fn.dataTable;
-$.extend( DataTable.ext.buttons, {
+
+$.extend(DataTable.ext.buttons, {
// A collection of column visibility buttons
- colvis: function ( dt, conf ) {
- return {
+ colvis: function (dt, conf) {
+ var node = null;
+ var buttonConf = {
extend: 'collection',
- text: function ( dt ) {
- return dt.i18n( 'buttons.colvis', 'Column visibility' );
+ init: function (dt, n) {
+ node = n;
+ },
+ text: function (dt) {
+ return dt.i18n('buttons.colvis', 'Column visibility');
},
className: 'buttons-colvis',
- buttons: [ {
- extend: 'columnsToggle',
- columns: conf.columns,
- columnText: conf.columnText
- } ]
+ closeButton: false,
+ buttons: [
+ {
+ extend: 'columnsToggle',
+ columns: conf.columns,
+ columnText: conf.columnText
+ }
+ ]
};
+
+ // Rebuild the collection with the new column structure if columns are reordered
+ dt.on('column-reorder.dt' + conf.namespace, function (e, settings, details) {
+ dt.button(null, dt.button(null, node).node()).collectionRebuild([
+ {
+ extend: 'columnsToggle',
+ columns: conf.columns,
+ columnText: conf.columnText
+ }
+ ]);
+ });
+
+ return buttonConf;
},
// Selected columns with individual buttons - toggle column visibility
- columnsToggle: function ( dt, conf ) {
- var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) {
- return {
- extend: 'columnToggle',
- columns: idx,
- columnText: conf.columnText
- };
- } ).toArray();
-
- return columns;
+ columnsToggle: function (dt, conf) {
+ var columns = dt
+ .columns(conf.columns)
+ .indexes()
+ .map(function (idx) {
+ return {
+ extend: 'columnToggle',
+ columns: idx,
+ columnText: conf.columnText
+ };
+ })
+
+ var sorted = columns.sort(function (a, b) {
+ var a = dt.settings()[0].aoColumns[a.columns].sTitle;
+ var b = dt.settings()[0].aoColumns[b.columns].sTitle;
+ return a.localeCompare(b)
+ }).toArray();
+
+ return sorted;
},
// Single button to toggle column visibility
- columnToggle: function ( dt, conf ) {
+ columnToggle: function (dt, conf) {
return {
extend: 'columnVisibility',
columns: conf.columns,
@@ -77,15 +123,19 @@ $.extend( DataTable.ext.buttons, {
},
// Selected columns with individual buttons - set column visibility
- columnsVisibility: function ( dt, conf ) {
- var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) {
- return {
- extend: 'columnVisibility',
- columns: idx,
- visibility: conf.visibility,
- columnText: conf.columnText
- };
- } ).toArray();
+ columnsVisibility: function (dt, conf) {
+ var columns = dt
+ .columns(conf.columns)
+ .indexes()
+ .map(function (idx) {
+ return {
+ extend: 'columnVisibility',
+ columns: idx,
+ visibility: conf.visibility,
+ columnText: conf.columnText
+ };
+ })
+ .toArray();
return columns;
},
@@ -93,111 +143,113 @@ $.extend( DataTable.ext.buttons, {
// Single button to set column visibility
columnVisibility: {
columns: undefined, // column selector
- text: function ( dt, button, conf ) {
- return conf._columnText( dt, conf );
+ text: function (dt, button, conf) {
+ return conf._columnText(dt, conf);
},
className: 'buttons-columnVisibility',
- action: function ( e, dt, button, conf ) {
- var col = dt.columns( conf.columns );
+ action: function (e, dt, button, conf) {
+ var col = dt.columns(conf.columns);
var curr = col.visible();
- col.visible( conf.visibility !== undefined ?
- conf.visibility :
- ! (curr.length ? curr[0] : false )
+ col.visible(
+ conf.visibility !== undefined ? conf.visibility : !(curr.length ? curr[0] : false)
);
},
- init: function ( dt, button, conf ) {
+ init: function (dt, button, conf) {
var that = this;
- button.attr( 'data-cv-idx', conf.columns );
-
- dt
- .on( 'column-visibility.dt'+conf.namespace, function (e, settings) {
- if ( ! settings.bDestroying && settings.nTable == dt.settings()[0].nTable ) {
- that.active( dt.column( conf.columns ).visible() );
- }
- } )
- .on( 'column-reorder.dt'+conf.namespace, function (e, settings, details) {
- // Don't rename buttons based on column name if the button
- // controls more than one column!
- if ( dt.columns( conf.columns ).count() !== 1 ) {
- return;
- }
-
- conf.columns = $.inArray( conf.columns, details.mapping );
- button.attr( 'data-cv-idx', conf.columns );
-
- // Reorder buttons for new table order
- button
- .parent()
- .children('[data-cv-idx]')
- .sort( function (a, b) {
- return (a.getAttribute('data-cv-idx')*1) - (b.getAttribute('data-cv-idx')*1);
- } )
- .appendTo(button.parent());
- } );
-
- this.active( dt.column( conf.columns ).visible() );
+ button.attr('data-cv-idx', conf.columns);
+
+ dt.on('column-visibility.dt' + conf.namespace, function (e, settings) {
+ if (!settings.bDestroying && settings.nTable == dt.settings()[0].nTable) {
+ that.active(dt.column(conf.columns).visible());
+ }
+ }).on('column-reorder.dt' + conf.namespace, function (e, settings, details) {
+ // Button has been removed from the DOM
+ if (conf.destroying) {
+ return;
+ }
+
+ if (dt.columns(conf.columns).count() !== 1) {
+ return;
+ }
+
+ // This button controls the same column index but the text for the column has
+ // changed
+ that.text(conf._columnText(dt, conf));
+
+ // Since its a different column, we need to check its visibility
+ that.active(dt.column(conf.columns).visible());
+ });
+
+ this.active(dt.column(conf.columns).visible());
},
- destroy: function ( dt, button, conf ) {
- dt
- .off( 'column-visibility.dt'+conf.namespace )
- .off( 'column-reorder.dt'+conf.namespace );
+ destroy: function (dt, button, conf) {
+ dt.off('column-visibility.dt' + conf.namespace).off(
+ 'column-reorder.dt' + conf.namespace
+ );
},
- _columnText: function ( dt, conf ) {
+ _columnText: function (dt, conf) {
// Use DataTables' internal data structure until this is presented
// is a public API. The other option is to use
// `$( column(col).node() ).text()` but the node might not have been
// populated when Buttons is constructed.
- var idx = dt.column( conf.columns ).index();
- var title = dt.settings()[0].aoColumns[ idx ].sTitle
- .replace(/\n/g," ") // remove new lines
- .replace(/
/gi, " ") // replace line breaks with spaces
- .replace(/