├── .github └── FUNDING.yml ├── Issue_template.txt ├── License.txt ├── Readme.md ├── bower.json ├── css ├── colReorder.bootstrap.scss ├── colReorder.bootstrap4.scss ├── colReorder.bootstrap5.scss ├── colReorder.bulma.scss ├── colReorder.dataTables.scss ├── colReorder.foundation.scss ├── colReorder.jqueryui.scss └── colReorder.semanticui.scss ├── docs ├── api │ ├── colReorder.disable().xml │ ├── colReorder.enable().xml │ ├── colReorder.move().xml │ ├── colReorder.order().xml │ ├── colReorder.reset().xml │ └── colReorder.transpose().xml ├── event │ ├── column-reorder.xml │ └── columns-reordered.xml └── option │ ├── colReorder.columns.xml │ ├── colReorder.enable.xml │ ├── colReorder.headerRows.xml │ ├── colReorder.order.xml │ └── colReorder.xml ├── examples ├── index.xml ├── initialisation │ ├── col_filter.xml │ ├── column-selector.xml │ ├── complex-header-row.xml │ ├── complex-header.xml │ ├── enableDisable.xml │ ├── footer_callback.xml │ ├── index.xml │ ├── new_init.xml │ ├── orthogonal-data-attrs.xml │ ├── predefined.xml │ ├── reset.xml │ ├── scrolling.xml │ └── simple.xml ├── integration │ ├── colvis.xml │ ├── fixedcolumns.xml │ ├── fixedheader.xml │ ├── index.xml │ ├── responsive.xml │ ├── server_side.xml │ └── state_save.xml └── styling │ ├── bootstrap.xml │ ├── bootstrap4.xml │ ├── bootstrap5.xml │ ├── bulma.xml │ ├── foundation.xml │ ├── index.xml │ ├── jqueryui.xml │ └── semanticui.xml ├── js ├── ColReorder.ts ├── colReorder.bootstrap.js ├── colReorder.bootstrap4.js ├── colReorder.bootstrap5.js ├── colReorder.bulma.js ├── colReorder.dataTables.js ├── colReorder.foundation.js ├── colReorder.jqueryui.js ├── colReorder.semanticui.js ├── dataTables.colReorder.ts └── functions.ts ├── make.sh ├── rollup.config.mjs ├── test ├── api │ ├── move().js │ ├── order().js │ ├── reset().js │ └── transpose().js ├── events │ ├── column-reorder.js │ └── columns-reordered.js └── options │ ├── enable.js │ └── order.js ├── tsconfig.json └── types ├── colReorder.bootstrap.d.ts ├── colReorder.bootstrap4.d.ts ├── colReorder.bootstrap5.d.ts ├── colReorder.bulma.d.ts ├── colReorder.dataTables.d.ts ├── colReorder.foundation.d.ts ├── colReorder.jqueryui.d.ts ├── colReorder.semanticui.d.ts └── types.d.ts /.github/FUNDING.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataTables/ColReorder/6a9a178d713c0d7d3379651ce622900527dc4e35/.github/FUNDING.yml -------------------------------------------------------------------------------- /Issue_template.txt: -------------------------------------------------------------------------------- 1 | Please post support requests and questions in the DataTables forums at https://datatables.net/forums. Support requests posted here will be closed. The intention of this request is to allow all questions to be located in a single, searchable, location. 2 | 3 | When you post your question in the DataTables forums, please ensure that you include a link to the page showing the issue so it can be debugged. 4 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT license 2 | 3 | Copyright (c) 2010-2015 SpryMedia Limited 4 | http://datatables.net 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # ColReorder 2 | 3 | ColReorder adds the ability for the end user to click and drag column headers to reorder a table as they see fit, to DataTables. See the [documentation](http://datatables.net/extensions/colreorder/) for full details. 4 | 5 | 6 | # Installation 7 | 8 | To use ColReorder the primary way to obtain the software is to use the [DataTables downloader](//datatables.net/download). You can also include the individual files from the [DataTables CDN](//cdn.datatables.net). See the [documentation](http://datatables.net/extensions/colreorder/) for full details. 9 | 10 | ## NPM and Bower 11 | 12 | If you prefer to use a package manager such as NPM or Bower, distribution repositories are available with software built from this repository under the name `datatables.net-colreorder`. Styling packages for Bootstrap, Foundation and other styling libraries are also available by adding a suffix to the package name. 13 | 14 | Please see the DataTables [NPM](//datatables.net/download/npm) and [Bower](//datatables.net/download/bower) installation pages for further information. The [DataTables installation manual](//datatables.net/manual/installation) also has details on how to use package managers with DataTables. 15 | 16 | 17 | # Basic usage 18 | 19 | ColReorder is initialised using the `colReorder` option in the DataTables constructor - a simple boolean `true` will enable the feature. Further options can be specified using this option as an object - see the documentation for details. 20 | 21 | Example: 22 | 23 | ```js 24 | $(document).ready( function () { 25 | $('#myTable').DataTable( { 26 | colReorder: true 27 | } ); 28 | } ); 29 | ``` 30 | 31 | 32 | # Documentation / support 33 | 34 | * [Documentation](https://datatables.net/extensions/colreorder/) 35 | * [DataTables support forums](http://datatables.net/forums) 36 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "datatables-colreorder", 3 | "main": [ 4 | "js/dataTables.colReorder.js", 5 | "css/colReorder.dataTables.scss" 6 | ], 7 | "dependencies": { 8 | "jquery": ">=1.7.0", 9 | "datatables": ">=1.10.8" 10 | }, 11 | "license": "MIT" 12 | } 13 | -------------------------------------------------------------------------------- /css/colReorder.bootstrap.scss: -------------------------------------------------------------------------------- 1 | 2 | $colreorder-insert-color: #337ab7 !default; 3 | 4 | @import 'colReorder.dataTables.scss'; 5 | -------------------------------------------------------------------------------- /css/colReorder.bootstrap4.scss: -------------------------------------------------------------------------------- 1 | 2 | $colreorder-insert-color: #0275d8 !default; 3 | 4 | @import 'colReorder.dataTables.scss'; 5 | -------------------------------------------------------------------------------- /css/colReorder.bootstrap5.scss: -------------------------------------------------------------------------------- 1 | 2 | $colreorder-insert-color: #0d6efd !default; 3 | $colreorder-insert-color-dark: rgb(13, 110, 253) !default; 4 | 5 | @import 'colReorder.dataTables.scss'; 6 | -------------------------------------------------------------------------------- /css/colReorder.bulma.scss: -------------------------------------------------------------------------------- 1 | 2 | $colreorder-insert-color: #00D1B2 !default; 3 | 4 | @import 'colReorder.dataTables.scss'; 5 | -------------------------------------------------------------------------------- /css/colReorder.dataTables.scss: -------------------------------------------------------------------------------- 1 | 2 | $colreorder-insert-color: #0259C4 !default; 3 | $colreorder-insert-color-dark: rgb(110, 168, 254) !default; 4 | 5 | body.dtcr-dragging { 6 | overflow-x: hidden; 7 | } 8 | 9 | // Floating table, dragged with the mouse 10 | table.dtcr-cloned.dataTable { 11 | position: absolute !important; 12 | background-color: rgba(255, 255, 255, 0.7); 13 | z-index: 202; 14 | border-radius: 4px; 15 | } 16 | 17 | table.dataTable tbody { 18 | tr { 19 | td.dtcr-moving { 20 | background-color: rgba(127, 127, 127, 0.15); 21 | } 22 | 23 | td.dtcr-moving-first { 24 | border-left: 1px solid $colreorder-insert-color; 25 | } 26 | 27 | td.dtcr-moving-last { 28 | border-right: 1px solid $colreorder-insert-color; 29 | } 30 | } 31 | } 32 | 33 | html.dark { 34 | table.dtcr-cloned.dataTable { 35 | background-color: rgba(33, 33, 33, 0.9); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /css/colReorder.foundation.scss: -------------------------------------------------------------------------------- 1 | 2 | $colreorder-insert-color: #008CBA !default; 3 | 4 | @import 'colReorder.dataTables.scss'; 5 | -------------------------------------------------------------------------------- /css/colReorder.jqueryui.scss: -------------------------------------------------------------------------------- 1 | 2 | @import 'colReorder.dataTables.scss'; 3 | -------------------------------------------------------------------------------- /css/colReorder.semanticui.scss: -------------------------------------------------------------------------------- 1 | 2 | $colreorder-insert-color: #888 !default; 3 | 4 | @import 'colReorder.dataTables.scss'; 5 | -------------------------------------------------------------------------------- /docs/api/colReorder.disable().xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.disable() 4 | Disable ColReorder's interactions 5 | 1.5.0 6 | 7 | 8 | colReorder.disable() 9 | colReorder.disable() 10 | 11 | Disable end user ability to reorder columns. 12 | 13 | DataTables API instance 14 | 15 | 16 | 17 | This method can be used to disable the ColReorder end user interaction with a DataTable. 18 | 19 | Please note that in order to be able to enable and disable ColReorder, the `-init colReorder` option _must_ be specified in the DataTable initialisation. This method cannot be used to control enablement of ColReorder if `-init colReorder` has not be used. 20 | 21 | 22 | 35 | 36 | -init colReorder.enable 37 | -api colReorder.enable() 38 | -api colReorder.disable() 39 | 40 | -------------------------------------------------------------------------------- /docs/api/colReorder.enable().xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.enable() 4 | Enable or disable ColReorder's interactions 5 | 1.5.0 6 | 7 | 8 | colReorder.enable() 9 | colReorder.enable( [ flag ] ) 10 | 11 | Enable and disable user ability to reorder columns in a table. 12 | 13 | 14 | This option can be given as the following values: 15 | 16 | * `true` - Enable ColReorder 17 | * `false` - Disable ColReorder 18 | 19 | DataTables API instance 20 | 21 | 22 | 23 | This method can be used to enable and disable the ColReorder end user interaction with a DataTable. In its simplest form it can be used to enable ColReorder, but it can also be used to disable it using the optional parameter. This is useful to be able to toggle the enablement state using a ternary operator or some external variable. 24 | 25 | Please note that in order to be able to enable ColReorder, the `-init colReorder` option _must_ be specified in the DataTable initialisation. This method cannot be used to enable ColReorder if `-init colReorder` has not be used. 26 | 27 | 28 | 41 | 42 | -init colReorder.enable 43 | -api colReorder.enable() 44 | -api colReorder.disable() 45 | 46 | -------------------------------------------------------------------------------- /docs/api/colReorder.move().xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.move() 4 | Programmatically move columns 5 | 1.4.0 6 | 7 | 8 | colReorder.move( from, to [, drop [, invalidate ] ] ) 9 | 10 | Programmatically reorder columns 11 | 12 | 13 | Column index to move. 14 | 15 | 16 | New index to move the column to. 17 | 18 | 19 | Indicate that this is the final move. Set this to `false` if you are performing multiple moves. 20 | 21 | 22 | Invalidate the row data. As with `drop` it can be useful to set this to `false` if performing multiple moves. Otherwise allow it to default which will ensure that the table's data is fully insync with the column order. 23 | 24 | 25 | Unmodified API instance. 26 | 27 | 28 | 29 | 30 | This method provides the ability to programmatically change the order of one column at a time (while `-api colReorder.order()` will set the order of all columns together). 31 | 32 | 33 | 40 | 41 | -------------------------------------------------------------------------------- /docs/api/colReorder.order().xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.order() 4 | Get / set column order 5 | 1.2.0 6 | 7 | 8 | colReorder.order() 9 | 10 | Get the current column order. 11 | 12 | 13 | Returns an array of column indexes. The column index given is the original column index, with its new position defined by the location in the returned array. 14 | 15 | 16 | 17 | 18 | colReorder.order( order [, originalIndexes ] ) 19 | 20 | Set the column order. 21 | 22 | 23 | Array of column indexes that define where the columns should be placed after the reorder. 24 | 25 | Please note that by default the column indexes given by this array are assumed to be the **current** column indexes, i.e. `0` will always refer to the first column in the table, regardless of the table ordering. The optional second parameter can be used to indicate that they should actually be treated as the original indexes. 26 | 27 | 28 | The order array defines the positions that columns should be shown through column indexes but these indexes can be one of: 29 | 30 | 1. The **current** column index (i.e. even if column reordering has already happened) 31 | 2. The **original** column index (i.e. the original index of the column before ColReorder has done any reordering) 32 | 33 | Set to be `true` to indicate that the indexes passed in are the original indexes. `false` or `undefined` (default) will treat them as the current indexes. 34 | 35 | 36 | DataTables API instance for chaining 37 | 38 | 39 | 40 | 41 | This method provides the ability to get the current column order of a DataTable and also to set a new order. 42 | 43 | The reorder triggered by this method is immediate and there is no requirement to redraw the table. 44 | 45 | 46 | 55 | 56 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /docs/api/colReorder.reset().xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.reset() 4 | Restore the loaded column order 5 | 1.2.0 6 | 7 | 8 | colReorder.reset() 9 | 10 | Restore the column order that the columns were in when initially loaded. 11 | 12 | DataTables API instance 13 | 14 | 15 | 16 | This method provides the ability to restore the original order of the columns, as was defined in the HTML during the table's initialisation. This will undo any reordering changes that the end user or the API has made prior to calling this method. 17 | 18 | The reorder triggered by this method is immediate and there is no requirement to redraw the table. 19 | 20 | 21 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/api/colReorder.transpose().xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.transpose() 4 | Convert one or more column indexes to and from current and original indexes 5 | 1.3.0 6 | 7 | 8 | colReorder.transpose( idx [, direction ] ) 9 | 10 | The index, or array of indexes to transpose. 11 | 12 | 13 | Set what transposition is required. This can be one of: 14 | 15 | * `-string toCurrent` - the input value is an original index and you wish to know its current index 16 | * `-string toOriginal` - the input value is the current index and you wish to know its original index 17 | * `-string fromOriginal` - As `-string toCurrent` 18 | * `-string fromCurrent` - As `-string toOriginal`. 19 | 20 | 21 | Get one or more current column indexes form their original index. 22 | 23 | The transpose values 24 | 25 | 26 | 27 | ColReorder will change the indexes of columns when columns are reordered and it can often be useful to convert between the original column index and the current column index. This method provides that ability. 28 | 29 | This ability to transpose between current and original values can be really useful if you have a reference to a column (`dt-api column().index()`) and you want to ensure that your index refers to the correct column, regardless of ordering. 30 | 31 | For example consider column index 0 is moved by the end user to index 5. You need to find out what its index is - this method provides that ability. 32 | 33 | Note that if ColReorder is not enabled on the target table this method can still be safely called - the input data will simply be returned (since no transposition is required). 34 | 35 | 36 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /docs/event/column-reorder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | column-reorder 4 | Columns have been reordered by the end user or API 5 | 1.2.0 6 | 7 | 8 | function( e, settings, details ) 9 | 10 | jQuery event object 11 | 12 | 13 | DataTables settings object for the table that has been modified 14 | 15 | 16 | An object that contains information about the reordered columns: 17 | 18 | * `-type array` `from` - An array of the column indexes that are being moved 19 | * `-type integer` `to` - Column index that the column has been moved to 20 | * `-type array` `mapping` - Array of integers that define how the old column positions map to the new positions 21 | 22 | HTML table element 23 | 24 | 25 | 26 | When using ColReorder you may wish to know when a table has been reordered by an end user or through the API. This event provides that information. 27 | 28 | This event is triggered when a column's data structure is moved internally - it does not signify that the reordering has been completed for all columns. For example the `-api colReorder.order()` method can cause multiple column reordering actions. Listen for `-event columns-reordered` to know then all columns have been updated and the table is fully up to date with the requested action. 29 | 30 | Please note that, as with all DataTables emitted events, this event is triggered with the `dt` namespace. As such, to listen for this event, you must also use the `dt` namespace by simply appending `.dt` to your event name, or use the `dt-api on()` method to listen for the event which will automatically append this namespace. 31 | 32 | 33 | 48 | -------------------------------------------------------------------------------- /docs/event/columns-reordered.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | columns-reordered 4 | Column reordering is finished and table updated 5 | 2.0 6 | 7 | 8 | function( e, details ) 9 | 10 | jQuery event object 11 | 12 | 13 | An object that contains information about the reordered columns: 14 | 15 | * `-type array` `order` - Current column order (`-api colReorder.order()`) 16 | * `-type array` `mapping` - Array of integers that define how the old column positions map to the new positions 17 | 18 | HTML table element 19 | 20 | 21 | 22 | This event let's you know when column reordering has finished. That might be after an individual call to `-api colReorder.move()` (which is used by the UI drag and drop reordering), or after all updates required by a call to `-api colReorder.order()`. 23 | 24 | The `-event column-reorder` event can also be used to know when reordering happens - it happens for every reorder action (i.e. it might trigger multiple times from `-api colReorder.order()`). 25 | 26 | Please note that, as with all DataTables emitted events, this event is triggered with the `dt` namespace. As such, to listen for this event, you must also use the `dt` namespace by simply appending `.dt` to your event name, or use the `dt-api on()` method to listen for the event which will automatically append this namespace. 27 | 28 | 29 | 38 | -------------------------------------------------------------------------------- /docs/option/colReorder.columns.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.columns 4 | Select which columns can be reordered 5 | 6 | 7 | 8 | Any column selector, including class names, CSS pseudo selectors and column indexes. 9 | 10 | 11 | 12 | 13 | Empty string - all columns can be reordered. 14 | 15 | 16 | 17 | This option can be used to select which columns ColReorder will allow the end user to perform reordering on. Columns which are not selected cannot have column reordering started on them (i.e. can't click and drag a header for them), nor can another column that is being reordered be dropped in this column's position. 18 | 19 | Locking columns in this way can be particularly useful if you have important information or controls at the start / end of a table that you want to always be in the same place. 20 | 21 | Please note that this method does not have any impact on column reordering via the API (e.g. `-api colReorder.move()` and `-api colReorder.order()`). If you have your own UI layer for column reordering that uses those methods, you will need to implement any column reordering restrictions you might want to impose on the end user. 22 | 23 | Please note that this option replaces the `colReorder.fixedColumnsLeft` and `colReorder.fixedColumnsRight` options from ColReorder 1.x. 24 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /docs/option/colReorder.enable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.enable 4 | Initial enablement state of ColReorder 5 | 1.5.0 6 | 7 | 8 | 9 | * `true` - ColReorder is enabled when the DataTable is created 10 | * `false` - ColReorder is not enabled, and can later be enabled via the API. 11 | 12 | 13 | 14 | 15 | ColReorder is enabled 16 | 17 | 18 | 19 | It can be useful to disable ColReorder's user input controls at certain times, depending on the state of your application. This option provides that ability when the table is initially created, while the `-api colReorder.enable()` and `-api colReorder.disable()` methods provide the option to enabling the user interaction after the table has been created. 20 | 21 | 22 | 29 | 30 | -init colReorder.enable 31 | -api colReorder.enable() 32 | -api colReorder.disable() 33 | 34 | -------------------------------------------------------------------------------- /docs/option/colReorder.headerRows.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.headerRows 4 | Select which columns can be reordered 5 | 2.1.0 6 | 7 | 8 | 9 | An array of the header row indexes that should be considered for the ColReorder drag and drop action. 10 | 11 | 12 | 13 | 14 | If null, then all header cells will received a click and drag listener (allowing for `-init colReorder.columns`). 15 | 16 | 17 | 18 | If you have a header in your DataTable that spans multiple rows, you might wish to only have the event handler for ColReorder's click and drag action apply to a specific row in the header (e.g. if you use a second row for column search inputs). This option provides that ability by being able to specify which rows the click and drag listener should be applied to. 19 | 20 | 21 | 28 | -------------------------------------------------------------------------------- /docs/option/colReorder.order.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder.order 4 | Set a default order for the columns in the table 5 | 6 | 7 | 8 | An array of integer values that define the order the columns should appear in. The position in the array is the position the column will take, and the value is the current column index that should be shown in that new position. 9 | 10 | The array _must_ contain all columns in the table, and cannot contain duplicates. 11 | 12 | 13 | 14 | 15 | The table's default column ordering will be used 16 | 17 | 18 | 19 | This option provides the option to define a default order for the columns in a table. Typically you will wish to have the columns in the order defined in the HTML, or from state saving (`dt-init stateSave`), but if required, this option can be used to define an initial default order. 20 | 21 | 22 | 29 | 30 | cr-api colReorder.order() 31 | -------------------------------------------------------------------------------- /docs/option/colReorder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | colReorder 4 | Enable and configure the ColReorder extension for DataTables 5 | 6 | 7 | 8 | As a boolean value this property will enable ColReorder on the DataTable that is being created. `true` will enable ColReorder, while `false` will not. 9 | 10 | This is a short-cut option to enable ColReorder with the default configuration options. Customisations can be made by giving this parameter as an object, see below. 11 | 12 | 13 | 14 | 15 | 16 | If given as an object, ColReorder will be enabled on the target DataTable, with default values (`$.fn.dataTable.ColReorder.defaults`) extended, and potentially overwritten, by the options provided in this object. This is how ColReorder can be configured on an individual table basis, or through the defaults. 17 | 18 | 19 | 20 | 21 | ColReorder will not be initialised by default 22 | 23 | 24 | 25 | ColReorder provides the option for end users to reorder columns in a DataTable by click and drag, or for yourself, the developer using DataTable, through the API. 26 | 27 | This option provides the ability to enable and configure ColReorder for DataTables. In its simplest form as the boolean `true` it will enable ColReorder with the default configuration options (as defined by `$.fn.dataTable.ColReorder.defaults`). It can also be used as an object to provide custom configuration options as described below. 28 | 29 | Please note that as with all other configuration options for ColReorder, this option is an extension to the [default set of DataTables options](/reference/option). This property should be set in the DataTables initialisation object. 30 | 31 | 32 | 37 | 38 | 45 | 46 | -------------------------------------------------------------------------------- /examples/index.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ColReorder examples 5 | 6 | 7 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/initialisation/col_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tfoot input { 6 | width: 100%; 7 | } 8 | 9 | 10 | ') 22 | .appendTo($(column.footer()).empty()) 23 | .on('keyup change clear', function () { 24 | if (column.search() !== this.value) { 25 | api 26 | .column( 27 | $(this) 28 | .parent() 29 | .index() + ':visible' 30 | ) 31 | .search(this.value) 32 | .draw(); 33 | } 34 | }); 35 | }); 36 | } 37 | }); 38 | 39 | ]]> 40 | 41 | 42 | 43 | { 61 | if (column.search() !== this.value) { 62 | let idx = [...th.parentNode.children].indexOf(th); 63 | 64 | api 65 | .column(idx + ':visible') 66 | .search(input.value) 67 | .draw(); 68 | } 69 | }); 70 | }); 71 | } 72 | }); 73 | 74 | ]]> 75 | 76 | 77 | Individual column filtering 78 | 79 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /examples/initialisation/column-selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | Selected columns 26 | 27 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/initialisation/complex-header-row.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | Complex headers - specific row 26 | 27 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /examples/initialisation/complex-header.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | Complex headers - all cells 22 | 23 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/initialisation/enableDisable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 32 | 33 | 34 | 35 | 61 | 62 | 63 | Enable / disable API 64 | 65 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/initialisation/footer_callback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 46 | 47 | 48 | 49 | intVal(a) + intVal(b), 0); 70 | 71 | // Total over this page 72 | pageTotal = api 73 | .column(currentPosition, { page: 'current' }) 74 | .data() 75 | .reduce((a, b) => intVal(a) + intVal(b), 0); 76 | 77 | // Update footer 78 | api.column(currentPosition).footer().innerHTML = 79 | '$' + pageTotal + ' ( $' + total + ' total)'; 80 | } 81 | }); 82 | 83 | ]]> 84 | 85 | 86 | Footer callback 87 | 88 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /examples/initialisation/index.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Initialisation 5 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/initialisation/new_init.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | Initialisation using `new` 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/initialisation/orthogonal-data-attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | HTML5 data-* orthogonal data 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/initialisation/predefined.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 24 | 25 | 26 | Predefined column ordering 27 | 28 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/initialisation/reset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | 19 | { 25 | table.colReorder.reset(); 26 | }); 27 | 28 | ]]> 29 | 30 | 31 | Reset ordering API 32 | 33 | 40 | 41 | 42 | 43 |

44 |
45 | 46 |
47 | -------------------------------------------------------------------------------- /examples/initialisation/scrolling.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | Scrolling table 26 | 27 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/initialisation/simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | Basic initialisation 22 | 23 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/integration/colvis.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | 19 | 29 | 30 | 31 | Column visibility integration 32 | 33 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/integration/fixedcolumns.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 25 | 26 | 27 | 28 | 47 | 48 | 49 | FixedColumns integration 50 | 51 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/integration/fixedheader.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | FixedHeader integration 24 | 25 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/integration/index.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Integration 5 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/integration/responsive.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | Responsive integration 24 | 25 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/integration/server_side.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 22 | 23 | 24 | 25 | 41 | 42 | 43 | Server-side processing 44 | 45 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /examples/integration/state_save.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | State saving 24 | 25 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/styling/bootstrap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | Bootstrap styling 22 | 23 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/styling/bootstrap4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | Bootstrap 4 styling 22 | 23 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/styling/bootstrap5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | Bootstrap 5 styling 22 | 23 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/styling/bulma.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | Bulma styling 22 | 23 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/styling/foundation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | Foundation styling 22 | 23 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/styling/index.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Styling 5 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/styling/jqueryui.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | jQuery UI styling 22 | 23 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/styling/semanticui.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | Fomantic-UI (formally Semantic-UI) styling 22 | 23 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /js/ColReorder.ts: -------------------------------------------------------------------------------- 1 | import { Api, ColumnSelector } from '../../../types/types'; // declare var DataTable: any; 2 | import { init, getOrder, setOrder, validateMove, move } from './functions'; 3 | 4 | interface IDropZone { 5 | colIdx: number; 6 | inlineStart: number; 7 | self: boolean; 8 | width: number; 9 | } 10 | 11 | interface IConfig { 12 | columns: ColumnSelector; 13 | 14 | enable: boolean; 15 | 16 | headerRows: null | number[]; 17 | 18 | order: number[]; 19 | } 20 | 21 | interface ISettings { 22 | dropZones: IDropZone[]; 23 | mouse: { 24 | absLeft: number; 25 | offset: { 26 | x: number; 27 | y: number; 28 | }; 29 | start: { 30 | x: number; 31 | y: number; 32 | }; 33 | target: JQuery; 34 | targets: number[]; 35 | }; 36 | scrollInterval: number; 37 | } 38 | 39 | /** 40 | * This is one possible UI for column reordering in DataTables. In this case 41 | * columns are reordered by clicking and dragging a column header. It calculates 42 | * where columns can be dropped based on the column header used to start the drag 43 | * and then `colReorder.move()` method to alter the DataTable. 44 | */ 45 | export default class ColReorder { 46 | private dom = { 47 | drag: null 48 | }; 49 | 50 | private dt: Api; 51 | 52 | private c: IConfig = { 53 | columns: null, 54 | enable: null, 55 | headerRows: null, 56 | order: null 57 | }; 58 | 59 | private s: ISettings = { 60 | dropZones: [], 61 | 62 | mouse: { 63 | absLeft: -1, 64 | offset: { 65 | x: -1, 66 | y: -1 67 | }, 68 | start: { 69 | x: -1, 70 | y: -1 71 | }, 72 | target: null, 73 | targets: [] 74 | }, 75 | 76 | scrollInterval: null 77 | }; 78 | 79 | public disable() { 80 | this.c.enable = false; 81 | 82 | return this; 83 | } 84 | 85 | public enable(flag: boolean = true) { 86 | if (flag === false) { 87 | return this.disable(); 88 | } 89 | 90 | this.c.enable = true; 91 | 92 | return this; 93 | } 94 | 95 | constructor(dt: Api, opts: typeof ColReorder.defaults) { 96 | let that = this; 97 | let ctx = dt.settings()[0]; 98 | 99 | // Check if ColReorder already has been initialised on this DataTable - only 100 | // one can exist. 101 | if (ctx._colReorder) { 102 | return; 103 | } 104 | dt.settings()[0]._colReorder = this; 105 | 106 | this.dt = dt; 107 | $.extend(this.c, ColReorder.defaults, opts); 108 | 109 | init(dt); 110 | 111 | dt.on('stateSaveParams', function (e, s, d) { 112 | d.colReorder = getOrder(dt); 113 | }); 114 | 115 | dt.on('destroy', function () { 116 | dt.off('.colReorder'); 117 | (dt as any).colReorder.reset(); 118 | }); 119 | 120 | // Initial ordering / state restoring 121 | let loaded = dt.state.loaded() as any; 122 | let order = this.c.order; 123 | 124 | if (loaded && loaded.colReorder && dt.columns().count() === loaded.colReorder.length) { 125 | order = loaded.colReorder; 126 | } 127 | 128 | if (order) { 129 | dt.ready(function () { 130 | setOrder(dt, order, true); 131 | }); 132 | } 133 | 134 | dt.table() 135 | .header.structure() 136 | .forEach(function (row, rowIdx) { 137 | let headerRows = opts.headerRows; 138 | 139 | for (let i = 0; i < row.length; i++) { 140 | if (!headerRows || headerRows.includes(rowIdx)) { 141 | if (row[i] && row[i].cell) { 142 | that._addListener(row[i].cell); 143 | } 144 | } 145 | } 146 | }); 147 | } 148 | 149 | /** 150 | * Attach the mouse down listener to an element to start a column reorder action 151 | * 152 | * @param el 153 | */ 154 | private _addListener(el) { 155 | let that = this; 156 | 157 | $(el) 158 | .on('selectstart.colReorder', function () { 159 | return false; 160 | }) 161 | .on('mousedown.colReorder touchstart.colReorder', function (e: any) { 162 | // Ignore middle and right click 163 | if (e.type === 'mousedown' && e.which !== 1) { 164 | return; 165 | } 166 | 167 | // Ignore if disabled 168 | if (!that.c.enable) { 169 | return; 170 | } 171 | 172 | // ColumnControl integration - if there is a CC reorder button in the header 173 | // then the mousedown is limited to that 174 | let btn = $('button.dtcc-button_reorder', this); 175 | 176 | if (btn.length && e.target !== btn[0] && btn.find(e.target).length === 0) { 177 | return; 178 | } 179 | 180 | that._mouseDown(e, this); 181 | }); 182 | } 183 | 184 | /** 185 | * Create the element that is dragged around the page 186 | */ 187 | private _createDragNode() { 188 | let origCell = this.s.mouse.target; 189 | let origTr = origCell.parent(); 190 | let origThead = origTr.parent(); 191 | let origTable = origThead.parent(); 192 | let cloneCell = origCell.clone(); 193 | 194 | // This is a slightly odd combination of jQuery and DOM, but it is the 195 | // fastest and least resource intensive way I could think of cloning 196 | // the table with just a single header cell in it. 197 | this.dom.drag = $(origTable[0].cloneNode(false)) 198 | .addClass('dtcr-cloned') 199 | .append( 200 | $(origThead[0].cloneNode(false)).append( 201 | $(origTr[0].cloneNode(false)).append(cloneCell[0]) as any 202 | ) as any // Not sure why it doesn't want to append a jQuery node 203 | ) 204 | .css({ 205 | position: 'absolute', 206 | top: 0, 207 | left: 0, 208 | width: $(origCell).outerWidth(), 209 | height: $(origCell).outerHeight() 210 | }) 211 | .appendTo('body'); 212 | } 213 | 214 | /** 215 | * Get cursor position regardless of mouse or touch input 216 | * 217 | * @param e Event 218 | * @param prop Property name to get 219 | * @returns Value - assuming a number here 220 | */ 221 | private _cursorPosition(e: JQuery.TriggeredEvent, prop: string): number { 222 | return e.type.indexOf('touch') !== -1 ? (e.originalEvent as any).touches[0][prop] : e[prop]; 223 | } 224 | 225 | /** 226 | * Cache values at start 227 | * 228 | * @param e Triggering event 229 | * @param cell Cell that the action started on 230 | * @returns 231 | */ 232 | private _mouseDown(e: JQuery.TriggeredEvent, cell: HTMLElement) { 233 | let target = $(e.target).closest('th, td'); 234 | let offset = target.offset(); 235 | let moveableColumns = this.dt.columns(this.c.columns).indexes().toArray(); 236 | let moveColumnIndexes = $(cell) 237 | .attr('data-dt-column') 238 | .split(',') 239 | .map(function (val) { 240 | return parseInt(val, 10); 241 | }); 242 | 243 | // Don't do anything for columns which are not selected as moveable 244 | for (let j = 0; j < moveColumnIndexes.length; j++) { 245 | if (!moveableColumns.includes(moveColumnIndexes[j])) { 246 | return false; 247 | } 248 | } 249 | 250 | this.s.mouse.start.x = this._cursorPosition(e, 'pageX'); 251 | this.s.mouse.start.y = this._cursorPosition(e, 'pageY'); 252 | this.s.mouse.offset.x = this._cursorPosition(e, 'pageX') - offset.left; 253 | this.s.mouse.offset.y = this._cursorPosition(e, 'pageY') - offset.top; 254 | this.s.mouse.target = target; 255 | this.s.mouse.targets = moveColumnIndexes; 256 | 257 | // Classes to highlight the columns being moved 258 | for (let i = 0; i < moveColumnIndexes.length; i++) { 259 | let cells = this.dt 260 | .cells(null, moveColumnIndexes[i] as any, { page: 'current' }) 261 | .nodes() 262 | .to$(); 263 | let klass = 'dtcr-moving'; 264 | 265 | if (i === 0) { 266 | klass += ' dtcr-moving-first'; 267 | } 268 | 269 | if (i === moveColumnIndexes.length - 1) { 270 | klass += ' dtcr-moving-last'; 271 | } 272 | 273 | cells.addClass(klass); 274 | } 275 | 276 | this._regions(moveColumnIndexes); 277 | this._scrollRegions(); 278 | 279 | /* Add event handlers to the document */ 280 | $(document) 281 | .on('mousemove.colReorder touchmove.colReorder', (e) => { 282 | this._mouseMove(e); 283 | }) 284 | .on('mouseup.colReorder touchend.colReorder', (e) => { 285 | this._mouseUp(e); 286 | }); 287 | } 288 | 289 | private _mouseMove(e: JQuery.TriggeredEvent) { 290 | if (this.dom.drag === null) { 291 | // Only create the drag element if the mouse has moved a specific distance from the start 292 | // point - this allows the user to make small mouse movements when sorting and not have a 293 | // possibly confusing drag element showing up 294 | if ( 295 | Math.pow( 296 | Math.pow(this._cursorPosition(e, 'pageX') - this.s.mouse.start.x, 2) + 297 | Math.pow(this._cursorPosition(e, 'pageY') - this.s.mouse.start.y, 2), 298 | 0.5 299 | ) < 5 300 | ) { 301 | return; 302 | } 303 | 304 | $(document.body).addClass('dtcr-dragging'); 305 | 306 | this._createDragNode(); 307 | } 308 | 309 | // Position the element - we respect where in the element the click occurred 310 | this.dom.drag.css({ 311 | left: this._cursorPosition(e, 'pageX') - this.s.mouse.offset.x, 312 | top: this._cursorPosition(e, 'pageY') - this.s.mouse.offset.y 313 | }); 314 | 315 | // Find cursor's left position relative to the table 316 | const tableNode = this.dt.table().node(); 317 | 318 | let tableOffset = $(tableNode).offset().left; 319 | let cursorMouseLeft = this._cursorPosition(e, 'pageX') - tableOffset; 320 | 321 | let cursorInlineStart : number; 322 | 323 | if (this._isRtl()) { 324 | const tableWidth = tableNode.clientWidth; 325 | cursorInlineStart = tableWidth - cursorMouseLeft; 326 | } 327 | else { 328 | cursorInlineStart = cursorMouseLeft; 329 | } 330 | 331 | let dropZone = this.s.dropZones.find(function (zone) { 332 | if (zone.inlineStart <= cursorInlineStart && cursorInlineStart <= zone.inlineStart + zone.width) { 333 | return true; 334 | } 335 | 336 | return false; 337 | }); 338 | 339 | this.s.mouse.absLeft = this._cursorPosition(e, 'pageX'); 340 | 341 | if (!dropZone) { 342 | return; 343 | } 344 | 345 | if (!dropZone.self) { 346 | this._move(dropZone, cursorInlineStart); 347 | } 348 | } 349 | 350 | private _mouseUp(e: JQuery.TriggeredEvent) { 351 | $(document).off('.colReorder'); 352 | $(document.body).removeClass('dtcr-dragging'); 353 | 354 | if (this.dom.drag) { 355 | this.dom.drag.remove(); 356 | this.dom.drag = null; 357 | 358 | // Once the element is removed, Firefox will then complete the mouse event 359 | // sequence by firing a click event on the element that the mouse is now 360 | // over. This means that a click event happens on the header cell triggering 361 | // a sort. A setTimeout on the remove doesn't appear to work around this. 362 | // 363 | // Therefore, we need to add a click event listener that will kill the 364 | // bubbling of the event, and then _almost_ immediately remove it. 365 | this.s.mouse.target.on('click.dtcr', function (e) { 366 | return false; 367 | }); 368 | 369 | setTimeout(() => { 370 | this.s.mouse.target.off('.dtcr'); 371 | }, 10); 372 | } 373 | 374 | if (this.s.scrollInterval) { 375 | clearInterval(this.s.scrollInterval); 376 | } 377 | 378 | this.dt.cells('.dtcr-moving').nodes().to$().removeClass('dtcr-moving dtcr-moving-first dtcr-moving-last'); 379 | } 380 | 381 | /** 382 | * Shift columns around 383 | * 384 | * @param dropZone Where to move to 385 | * @param cursorInlineStart Cursor position, relative to the inline start (left or right) of the table 386 | */ 387 | private _move(dropZone: IDropZone, cursorInlineStart: number) { 388 | let that = this; 389 | 390 | (this.dt as any).colReorder.move(this.s.mouse.targets, dropZone.colIdx); 391 | 392 | // Update the targets 393 | this.s.mouse.targets = $(this.s.mouse.target) 394 | .attr('data-dt-column') 395 | .split(',') 396 | .map(function (val) { 397 | return parseInt(val, 10); 398 | }); 399 | 400 | this._regions(this.s.mouse.targets); 401 | 402 | let visibleTargets = this.s.mouse.targets.filter(function (val) { 403 | return that.dt.column(val).visible(); 404 | }); 405 | 406 | // If the column being moved is smaller than the column it is replacing, 407 | // the drop zones might need a correction to allow for this since, otherwise 408 | // we might immediately be changing the column order as soon as it was placed. 409 | 410 | // Find the drop zone for the first in the list of targets - is its 411 | // left greater than the mouse position. If so, it needs correcting 412 | let dz = this.s.dropZones.find(function (zone) { 413 | return zone.colIdx === visibleTargets[0]; 414 | }); 415 | let dzIdx = this.s.dropZones.indexOf(dz); 416 | 417 | if (dz.inlineStart > cursorInlineStart) { 418 | let previousDiff = dz.inlineStart - cursorInlineStart; 419 | let previousDz = this.s.dropZones[dzIdx - 1]; 420 | 421 | dz.inlineStart -= previousDiff; 422 | dz.width += previousDiff; 423 | 424 | if (previousDz) { 425 | previousDz.width -= previousDiff; 426 | } 427 | } 428 | 429 | // And for the last in the list 430 | dz = this.s.dropZones.find(function (zone) { 431 | return zone.colIdx === visibleTargets[visibleTargets.length - 1]; 432 | }); 433 | 434 | if (dz.inlineStart + dz.width < cursorInlineStart) { 435 | let nextDiff = cursorInlineStart - (dz.inlineStart + dz.width); 436 | let nextDz = this.s.dropZones[dzIdx + 1]; 437 | 438 | dz.width += nextDiff; 439 | 440 | if (nextDz) { 441 | nextDz.inlineStart += nextDiff; 442 | nextDz.width -= nextDiff; 443 | } 444 | } 445 | } 446 | 447 | /** 448 | * Determine the boundaries for where drops can happen and where they would 449 | * insert into. 450 | */ 451 | private _regions(moveColumns: number[]) { 452 | let that = this; 453 | let dropZones: IDropZone[] = []; 454 | let totalWidth = 0; 455 | let negativeCorrect = 0; 456 | let allowedColumns = this.dt.columns(this.c.columns).indexes().toArray(); 457 | let widths = this.dt.columns().widths(); 458 | 459 | // Each column is a drop zone 460 | this.dt.columns().every(function (colIdx, tabIdx, i) { 461 | if (!this.visible()) { 462 | return; 463 | } 464 | 465 | let columnWidth = widths[colIdx]; 466 | 467 | // Check that we are allowed to move into this column - if not, need 468 | // to offset the widths 469 | if (!allowedColumns.includes(colIdx)) { 470 | totalWidth += columnWidth; 471 | return; 472 | } 473 | 474 | let valid = validateMove(that.dt, moveColumns, colIdx); 475 | 476 | if (valid) { 477 | // New drop zone. Note that it might have it's offset moved 478 | // by the final condition in this logic set 479 | dropZones.push({ 480 | colIdx: colIdx, 481 | inlineStart: totalWidth - negativeCorrect, 482 | self: moveColumns[0] <= colIdx && colIdx <= moveColumns[moveColumns.length - 1], 483 | width: columnWidth + negativeCorrect 484 | }); 485 | } 486 | else if (colIdx < moveColumns[0]) { 487 | // Not valid and before the column(s) to be moved - the drop 488 | // zone for the previous valid drop point is extended 489 | if (dropZones.length) { 490 | dropZones[dropZones.length - 1].width += columnWidth; 491 | } 492 | } 493 | else if (colIdx > moveColumns[moveColumns.length - 1]) { 494 | // Not valid and after the column(s) to be moved - the next 495 | // drop zone to be created will be extended 496 | negativeCorrect += columnWidth; 497 | } 498 | 499 | totalWidth += columnWidth; 500 | }); 501 | 502 | this.s.dropZones = dropZones; 503 | // this._drawDropZones(); 504 | } 505 | 506 | /** 507 | * Check if the table is scrolling or not. It is it the `table` isn't the same for 508 | * the header and body parents. 509 | * 510 | * @returns 511 | */ 512 | private _isScrolling() { 513 | return this.dt.table().body().parentNode !== this.dt.table().header().parentNode; 514 | } 515 | 516 | /** 517 | * Set an interval clock that will check to see if the scrolling of the table body should be moved 518 | * as the mouse moves on the scroll (allowing a drag and drop to columns which aren't yet visible) 519 | */ 520 | private _scrollRegions() { 521 | if (!this._isScrolling()) { 522 | // Not scrolling - nothing to do 523 | return; 524 | } 525 | 526 | let that = this; 527 | let tableLeft = $(this.dt.table().container()).offset().left; 528 | let tableWidth = $(this.dt.table().container()).outerWidth(); 529 | let mouseBuffer = 75; 530 | let scrollContainer = this.dt.table().body().parentElement.parentElement; 531 | 532 | this.s.scrollInterval = setInterval(function () { 533 | let mouseLeft = that.s.mouse.absLeft; 534 | 535 | // On initial mouse down the mouse position can be -1, which we want to ignore 536 | if (mouseLeft === -1) { 537 | return; 538 | } 539 | 540 | if (mouseLeft < tableLeft + mouseBuffer && scrollContainer.scrollLeft) { 541 | scrollContainer.scrollLeft -= 5; 542 | } 543 | else if ( 544 | mouseLeft > tableLeft + tableWidth - mouseBuffer && 545 | scrollContainer.scrollLeft < scrollContainer.scrollWidth 546 | ) { 547 | scrollContainer.scrollLeft += 5; 548 | } 549 | }, 25); 550 | } 551 | 552 | // This is handy for debugging where the drop zones actually are! 553 | // private _drawDropZones () { 554 | // let dropZones = this.s.dropZones; 555 | 556 | // $('div.allan').remove(); 557 | 558 | // for (let i=0 ; i') 563 | // .addClass('allan') 564 | // .css({ 565 | // position: 'absolute', 566 | // top: 0, 567 | // width: zone.width - 4, 568 | // height: 20, 569 | // left: zone.left + 2, 570 | // border: '1px solid red', 571 | // }) 572 | // ); 573 | // } 574 | // } 575 | 576 | private _isRtl() { 577 | return $(this.dt.table()).css('direction') === 'rtl'; 578 | } 579 | 580 | static defaults: IConfig = { 581 | columns: '', 582 | 583 | enable: true, 584 | 585 | headerRows: null, 586 | 587 | order: null 588 | }; 589 | 590 | static version = '2.1.1'; 591 | } 592 | -------------------------------------------------------------------------------- /js/colReorder.bootstrap.js: -------------------------------------------------------------------------------- 1 | /*! Bootstrap 3 styling wrapper for ColReorder 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | -------------------------------------------------------------------------------- /js/colReorder.bootstrap4.js: -------------------------------------------------------------------------------- 1 | /*! Bootstrap 4 styling wrapper for ColReorder 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | -------------------------------------------------------------------------------- /js/colReorder.bootstrap5.js: -------------------------------------------------------------------------------- 1 | /*! Bootstrap 5 styling wrapper for ColReorder 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | -------------------------------------------------------------------------------- /js/colReorder.bulma.js: -------------------------------------------------------------------------------- 1 | /*! Bulma styling wrapper for ColReorder 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | -------------------------------------------------------------------------------- /js/colReorder.dataTables.js: -------------------------------------------------------------------------------- 1 | /*! DataTables styling wrapper for ColReorder 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | -------------------------------------------------------------------------------- /js/colReorder.foundation.js: -------------------------------------------------------------------------------- 1 | /*! Foundation styling wrapper for ColReorder 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | -------------------------------------------------------------------------------- /js/colReorder.jqueryui.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI styling wrapper for ColReorder 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | -------------------------------------------------------------------------------- /js/colReorder.semanticui.js: -------------------------------------------------------------------------------- 1 | /*! Semanic UI styling wrapper for ColReorder 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | -------------------------------------------------------------------------------- /js/dataTables.colReorder.ts: -------------------------------------------------------------------------------- 1 | /*! ColReorder 2.1.1 2 | * © SpryMedia Ltd - datatables.net/license 3 | */ 4 | 5 | /** 6 | * @summary ColReorder 7 | * @description Provide the ability to reorder columns in a DataTable 8 | * @version 2.1.1 9 | * @author SpryMedia Ltd 10 | * @contact datatables.net 11 | * @copyright SpryMedia Ltd. 12 | * 13 | * This source file is free software, available under the following license: 14 | * MIT license - http://datatables.net/license/mit 15 | * 16 | * This source file is distributed in the hope that it will be useful, but 17 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 18 | * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 19 | * 20 | * For details please refer to: http://www.datatables.net 21 | */ 22 | 23 | import DataTable from '../../../types/types'; // declare var DataTable: any; 24 | 25 | import ColReorder from './ColReorder'; 26 | import { 27 | finalise, 28 | init, 29 | invertKeyValues, 30 | getOrder, 31 | move, 32 | orderingIndexes, 33 | setOrder, 34 | transpose, 35 | validateMove 36 | } from './functions'; 37 | 38 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 39 | * UI interaction class 40 | */ 41 | 42 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 43 | * DataTables API integration 44 | */ 45 | 46 | /** Enable mouse column reordering */ 47 | DataTable.Api.register('colReorder.enable()', function (flag) { 48 | return this.iterator('table', function (ctx) { 49 | if (ctx._colReorder) { 50 | ctx._colReorder.enable(flag); 51 | } 52 | }); 53 | }); 54 | 55 | /** Disable mouse column reordering */ 56 | DataTable.Api.register('colReorder.disable()', function () { 57 | return this.iterator('table', function (ctx) { 58 | if (ctx._colReorder) { 59 | ctx._colReorder.disable(); 60 | } 61 | }); 62 | }); 63 | 64 | /** 65 | * Change the ordering of the columns in the DataTable. 66 | */ 67 | DataTable.Api.register('colReorder.move()', function (from, to) { 68 | init(this); 69 | 70 | if (!Array.isArray(from)) { 71 | from = [from]; 72 | } 73 | 74 | if (!validateMove(this, from, to)) { 75 | this.error('ColReorder - invalid move'); 76 | return this; 77 | } 78 | 79 | return this.tables().every(function () { 80 | move(this, from, to); 81 | finalise(this); 82 | }); 83 | }); 84 | 85 | DataTable.Api.register('colReorder.order()', function (set?: number[], original?) { 86 | init(this); 87 | 88 | if (!set) { 89 | return this.context.length ? getOrder(this) : null; 90 | } 91 | 92 | return this.tables().every(function () { 93 | setOrder(this, set, original); 94 | }); 95 | }); 96 | 97 | DataTable.Api.register('colReorder.reset()', function () { 98 | init(this); 99 | 100 | return this.tables().every(function () { 101 | var order = this.columns() 102 | .every(function (i) { 103 | return i; 104 | }) 105 | .flatten() 106 | .toArray(); 107 | 108 | setOrder(this, order, true); 109 | }); 110 | }); 111 | 112 | DataTable.Api.register('colReorder.transpose()', function (idx: any, dir) { 113 | init(this); 114 | 115 | if (!dir) { 116 | dir = 'toCurrent'; 117 | } 118 | 119 | return transpose(this, idx, dir); 120 | }); 121 | 122 | (DataTable as any).ColReorder = ColReorder; 123 | 124 | // Called when DataTables is going to load a state. That might be 125 | // before the table is ready (state saving) or after (state restoring). 126 | // Also note that it happens _before_ preInit (below). 127 | $(document).on('stateLoadInit.dt', function (e, settings, state) { 128 | if (e.namespace !== 'dt') { 129 | return; 130 | } 131 | 132 | let dt = new DataTable.Api(settings); 133 | 134 | if (state.colReorder && dt.columns().count() === state.colReorder.length) { 135 | if (dt.ready()) { 136 | // Table is fully loaded - do the column reordering here 137 | // so that the stored indexes are in the correct place 138 | // e.g. column visibility 139 | setOrder(dt, state.colReorder, true); 140 | } 141 | else { 142 | // If the table is not ready, column reordering is done 143 | // after it becomes fully ready. That means that saved 144 | // column indexes need to be updated for where those columns 145 | // currently are. Any properties which refer to column indexes 146 | // would need to be updated here. 147 | 148 | // State's ordering indexes 149 | orderingIndexes(state.colReorder, state.order); 150 | 151 | // State's columns array - sort by restore index 152 | if (state.columns) { 153 | for (let i = 0; i < state.columns.length; i++) { 154 | state.columns[i]._cr_sort = state.colReorder[i]; 155 | } 156 | 157 | state.columns.sort(function (a, b) { 158 | return a._cr_sort - b._cr_sort; 159 | }); 160 | } 161 | } 162 | } 163 | }); 164 | 165 | $(document).on('preInit.dt', function (e, settings) { 166 | if (e.namespace !== 'dt') { 167 | return; 168 | } 169 | 170 | var init = settings.oInit.colReorder; 171 | var defaults = (DataTable.defaults as any).colReorder; 172 | 173 | if (init || defaults) { 174 | var opts = $.extend({}, defaults, init); 175 | 176 | if (init !== false) { 177 | let dt = new DataTable.Api(settings); 178 | 179 | new ColReorder(dt, opts); 180 | } 181 | } 182 | }); 183 | -------------------------------------------------------------------------------- /js/functions.ts: -------------------------------------------------------------------------------- 1 | import DataTable, { Api, HeaderStructure } from '../../../types/types'; // declare var DataTable: any; 2 | 3 | /** 4 | * Mutate an array, moving a set of elements into a new index position 5 | * 6 | * @param arr Array to modify 7 | * @param from Start move index 8 | * @param count Number of elements to move 9 | * @param to Index where the start element will move to 10 | */ 11 | export function arrayMove(arr: any[], from: number, count: number, to: number): void { 12 | let movers: any[] = arr.splice(from, count); 13 | 14 | // Add delete and start to the array, so we can use it for the `apply` 15 | movers.unshift(0); // splice delete param 16 | movers.unshift(to < from ? to : to - count + 1); // splice start param 17 | 18 | arr.splice.apply(arr, movers); 19 | } 20 | 21 | /** 22 | * Run finishing activities after one or more columns have been reordered. 23 | * 24 | * @param dt DataTable being operated on - must be a single table instance 25 | */ 26 | export function finalise(dt: Api) { 27 | // Cache invalidation. Always read from the data object rather 28 | // than reading back from the DOM since it could have been 29 | // changed by a renderer 30 | dt.rows().invalidate('data'); 31 | 32 | // Redraw the header / footer. Its a little bit of a hack this, as DT 33 | // doesn't expose the header draw as an API method. It calls state 34 | // saving, so we don't need to here. 35 | dt.column(0).visible(dt.column(0).visible()); 36 | 37 | dt.columns.adjust(); 38 | 39 | // Fire an event so other plug-ins can update 40 | let order = (dt as any).colReorder.order(); 41 | 42 | dt.trigger('columns-reordered', [ 43 | { 44 | order: order, 45 | mapping: invertKeyValues(order) 46 | } 47 | ]); 48 | } 49 | 50 | /** 51 | * Get the original indexes in their current order 52 | * 53 | * @param dt DataTable being operated on - must be a single table instance 54 | * @returns Original indexes in current order 55 | */ 56 | export function getOrder(dt: Api): number[] { 57 | return dt.settings()[0].aoColumns.map(function (col) { 58 | return col._crOriginalIdx; 59 | }); 60 | } 61 | 62 | /** 63 | * Manipulate a header / footer array in DataTables settings to reorder 64 | * the columns. 65 | */ 66 | export function headerUpdate(structure: any[], map: number[], from: number[], to: number): void { 67 | let done: HTMLElement[] = []; 68 | 69 | for (let i = 0; i < structure.length; i++) { 70 | let headerRow = structure[i]; 71 | 72 | arrayMove(headerRow, from[0], from.length, to); 73 | 74 | for (let j = 0; j < headerRow.length; j++) { 75 | let cell = headerRow[j].cell; 76 | 77 | // Only work on a DOM element once, otherwise we risk remapping a 78 | // remapped value (etc). 79 | if (done.includes(cell)) { 80 | continue; 81 | } 82 | 83 | let indexes = cell.getAttribute('data-dt-column').split(','); 84 | let mapped = indexes 85 | .map(function (idx) { 86 | return map[idx]; 87 | }) 88 | .join(','); 89 | 90 | // Update data attributes for the new column position 91 | cell.setAttribute('data-dt-column', mapped); 92 | 93 | done.push(cell); 94 | } 95 | } 96 | } 97 | 98 | /** 99 | * Setup for ColReorder API operations 100 | * 101 | * @param dt DataTable(s) being operated on - might have multiple tables! 102 | */ 103 | export function init(api: Api): void { 104 | // Assign the original column index to a parameter that we can lookup. 105 | // On the first pass (i.e. when the parameter hasn't yet been set), the 106 | // index order will be the original order, so this is quite a simple 107 | // assignment. 108 | api.columns().iterator('column', function (s, idx) { 109 | let columns = s.aoColumns; 110 | 111 | if (columns[idx]._crOriginalIdx === undefined) { 112 | columns[idx]._crOriginalIdx = idx; 113 | } 114 | }); 115 | } 116 | 117 | /** 118 | * Switch the key value pairing of an index array to be value key (i.e. the old value is now the 119 | * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ]. 120 | * 121 | * @param array arr Array to switch around 122 | */ 123 | export function invertKeyValues(arr: number[]): number[] { 124 | let result = []; 125 | 126 | for (let i = 0; i < arr.length; i++) { 127 | result[arr[i]] = i; 128 | } 129 | 130 | return result; 131 | } 132 | 133 | /** 134 | * Move one or more columns from one index to another. 135 | * 136 | * This method has a lot of knowledge about how DataTables works internally. 137 | * If DataTables changes how it handles cells, columns, etc, then this 138 | * method would need to be updated accordingly. 139 | * 140 | * @param dt DataTable being operated on - must be a single table instance 141 | * @param from Column indexes to move 142 | * @param to Destination index (starting if multiple) 143 | */ 144 | export function move(dt: Api, from: number[], to: number): void { 145 | let i, j; 146 | let settings = dt.settings()[0]; 147 | let columns = settings.aoColumns; 148 | let newOrder = columns.map(function (col, idx) { 149 | return idx; 150 | }); 151 | 152 | // The to column in already inside the from column(s) (might be the same) 153 | // no change required 154 | if (from.includes(to)) { 155 | return; 156 | } 157 | 158 | // A reverse index array so we can look up new indexes from old 159 | arrayMove(newOrder, from[0], from.length, to); 160 | let reverseIndexes = invertKeyValues(newOrder); 161 | 162 | // Main column 163 | arrayMove(columns, from[0], from.length, to); 164 | 165 | // Per row manipulations 166 | for (i = 0; i < settings.aoData.length; i++) { 167 | var data = settings.aoData[i]; 168 | 169 | // Allow for sparse array 170 | if (! data) { 171 | continue; 172 | } 173 | 174 | var cells = data.anCells; 175 | 176 | // Not yet rendered 177 | if (! cells) { 178 | continue; 179 | } 180 | 181 | // Array of cells 182 | arrayMove(cells, from[0], from.length, to); 183 | 184 | for (j = 0; j < cells.length; j++) { 185 | // Reinsert into the document in the new order 186 | if (data.nTr && cells[j] && columns[j].bVisible) { 187 | data.nTr.appendChild(cells[j]); 188 | } 189 | 190 | // Update lookup index 191 | if (cells[j] && cells[j]._DT_CellIndex) { 192 | cells[j]._DT_CellIndex.column = j; 193 | } 194 | } 195 | } 196 | 197 | // Per column manipulation 198 | for (i = 0; i < columns.length; i++) { 199 | let column = columns[i]; 200 | 201 | // Data column sorting 202 | for (j = 0; j < column.aDataSort.length; j++) { 203 | column.aDataSort[j] = reverseIndexes[column.aDataSort[j]]; 204 | } 205 | 206 | // Update the column indexes 207 | column.idx = reverseIndexes[column.idx]; 208 | 209 | // Reorder the colgroup > col elements for the new order 210 | if (column.bVisible) { 211 | settings.colgroup.append(column.colEl); 212 | } 213 | } 214 | 215 | // Header and footer 216 | headerUpdate(settings.aoHeader, reverseIndexes, from, to); 217 | headerUpdate(settings.aoFooter, reverseIndexes, from, to); 218 | 219 | // Search - columns 220 | arrayMove(settings.aoPreSearchCols, from[0], from.length, to); 221 | 222 | // Ordering indexes update - note that the sort listener on the 223 | // header works out the index to apply on each draw, so it doesn't 224 | // need to be updated here. 225 | orderingIndexes(reverseIndexes, settings.aaSorting); 226 | 227 | if (Array.isArray(settings.aaSortingFixed)) { 228 | orderingIndexes(reverseIndexes, settings.aaSortingFixed); 229 | } 230 | else if (settings.aaSortingFixed.pre) { 231 | orderingIndexes(reverseIndexes, settings.aaSortingFixed.pre); 232 | } 233 | else if (settings.aaSortingFixed.post) { 234 | orderingIndexes(reverseIndexes, settings.aaSortingFixed.pre); 235 | } 236 | 237 | settings.aLastSort.forEach(function (el) { 238 | el.src = reverseIndexes[el.src]; 239 | }); 240 | 241 | // Fire an event so other plug-ins can update 242 | dt.trigger('column-reorder', [ 243 | dt.settings()[0], 244 | { 245 | from: from, 246 | to: to, 247 | mapping: reverseIndexes 248 | } 249 | ]); 250 | } 251 | 252 | /** 253 | * Update the indexing for ordering arrays 254 | * 255 | * @param map Reverse index map 256 | * @param order Array to update 257 | */ 258 | export function orderingIndexes(map: number[], order?: any[]): void { 259 | // Can happen if the order was deleted from a saved state 260 | if (! order) { 261 | return; 262 | } 263 | 264 | for (let i = 0; i < order.length; i++) { 265 | let el = order[i]; 266 | 267 | if (typeof el === 'number') { 268 | // Just a number 269 | order[i] = map[el]; 270 | } 271 | else if ($.isPlainObject(el) && el.idx !== undefined) { 272 | // New index in an object style 273 | el.idx = map[el.idx]; 274 | } 275 | else if (Array.isArray(el) && typeof el[0] === 'number') { 276 | // The good old fixes length array 277 | el[0] = map[el[0]]; 278 | } 279 | // No need to update if in object + .name style 280 | } 281 | } 282 | 283 | /** 284 | * Take an index array for the current positioned, reordered to what you want 285 | * them to be. 286 | * 287 | * @param dt DataTable being operated on - must be a single table instance 288 | * @param order Indexes from current order, positioned as you want them to be 289 | */ 290 | export function setOrder(dt: Api, order: number[], original: boolean): void { 291 | let changed = false; 292 | let i; 293 | 294 | if (order.length !== dt.columns().count()) { 295 | dt.error('ColReorder - column count mismatch'); 296 | return; 297 | } 298 | 299 | // The order given is based on the original indexes, rather than the 300 | // existing ones, so we need to translate from the original to current 301 | // before then doing the order 302 | if (original) { 303 | order = transpose(dt, order, 'toCurrent'); 304 | } 305 | 306 | // The API is array index as the desired position, but our algorithm below is 307 | // for array index as the current position. So we need to invert for it to work. 308 | let setOrder = invertKeyValues(order); 309 | 310 | // Move columns, one by one with validation disabled! 311 | for (i = 0; i < setOrder.length; i++) { 312 | let currentIndex = setOrder.indexOf(i); 313 | 314 | if (i !== currentIndex) { 315 | // Reorder our switching error 316 | arrayMove(setOrder, currentIndex, 1, i); 317 | 318 | // Do the reorder 319 | move(dt, [currentIndex], i); 320 | 321 | changed = true; 322 | } 323 | } 324 | 325 | // Reorder complete 326 | if (changed) { 327 | finalise(dt); 328 | } 329 | } 330 | 331 | /** 332 | * Convert the DataTables header structure array into a 2D array where each 333 | * element has a reference to its TH/TD cell (regardless of spanning). 334 | * 335 | * @param structure Header / footer structure object 336 | * @returns 2D array of header cells 337 | */ 338 | export function structureFill(structure: HeaderStructure[][]) { 339 | let filledIn: Array> = []; 340 | 341 | for (let row = 0; row < structure.length; row++) { 342 | filledIn.push([]); 343 | 344 | for (let col = 0; col < structure[row].length; col++) { 345 | let cell = structure[row][col]; 346 | 347 | if (cell) { 348 | for (let rowInner = 0; rowInner < cell.rowspan; rowInner++) { 349 | if (! filledIn[row + rowInner]) { 350 | filledIn[row + rowInner] = []; 351 | } 352 | 353 | for (let colInner = 0; colInner < cell.colspan; colInner++) { 354 | filledIn[row + rowInner][col + colInner] = cell.cell; 355 | } 356 | } 357 | } 358 | } 359 | } 360 | 361 | return filledIn; 362 | } 363 | 364 | /** 365 | * Convert the index type 366 | * 367 | * @param dt DataTable to work on 368 | * @param idx Index to transform 369 | * @param dir Transform direction 370 | * @returns Converted number(s) 371 | */ 372 | export function transpose( 373 | dt: Api, 374 | idx: number | number[], 375 | dir: 'toCurrent' | 'toOriginal' | 'fromCurrent' | 'fromOriginal' 376 | ): any { 377 | var order = (dt as any).colReorder.order() as number[]; 378 | var columns = dt.settings()[0].aoColumns; 379 | 380 | if (dir === 'toCurrent' || dir === 'fromOriginal') { 381 | // Given an original index, want the current 382 | return !Array.isArray(idx) 383 | ? order.indexOf(idx) 384 | : idx.map(function (index) { 385 | return order.indexOf(index); 386 | }); 387 | } 388 | 389 | // Given a current index, want the original 390 | return !Array.isArray(idx) 391 | ? columns[idx]._crOriginalIdx 392 | : idx.map(function (index) { 393 | return columns[index]._crOriginalIdx; 394 | }); 395 | } 396 | 397 | /** 398 | * Validate that a requested move is okay. This includes bound checking 399 | * and that it won't split colspan'ed cells. 400 | * 401 | * @param table API instance 402 | * @param from Column indexes to move 403 | * @param to Destination index (starting if multiple) 404 | * @returns Validation result 405 | */ 406 | export function validateMove(table: Api, from: number[], to: number) { 407 | let columns = table.columns().count(); 408 | 409 | // Sanity and bound checking 410 | if (from[0] < to && to < from[from.length]) { 411 | return false; 412 | } 413 | 414 | if (from[0] < 0 && from[from.length - 1] > columns) { 415 | return false; 416 | } 417 | 418 | if (to < 0 && to > columns) { 419 | return false; 420 | } 421 | 422 | // No change - it's valid 423 | if (from.includes(to)) { 424 | return true; 425 | } 426 | 427 | if (!validateStructureMove(table.table().header.structure(), from, to)) { 428 | return false; 429 | } 430 | 431 | if (!validateStructureMove(table.table().footer.structure(), from, to)) { 432 | return false; 433 | } 434 | 435 | return true; 436 | } 437 | 438 | /** 439 | * For a given structure check that the move is valid. 440 | * @param structure 441 | * @param from 442 | * @param to 443 | * @returns 444 | */ 445 | export function validateStructureMove(structure: HeaderStructure[][], from: number[], to: number): boolean { 446 | let header = structureFill(structure); 447 | let i; 448 | 449 | // Shuffle the header cells around 450 | for (i = 0; i < header.length; i++) { 451 | arrayMove(header[i], from[0], from.length, to); 452 | } 453 | 454 | // Sanity check that the headers are next to each other 455 | for (i = 0; i < header.length; i++) { 456 | let seen = []; 457 | 458 | for (let j = 0; j < header[i].length; j++) { 459 | let cell = header[i][j]; 460 | 461 | if (!seen.includes(cell)) { 462 | // Hasn't been seen before 463 | seen.push(cell); 464 | } 465 | else if (seen[seen.length - 1] !== cell) { 466 | // Has been seen before and is not the previous cell - validation failed 467 | return false; 468 | } 469 | } 470 | } 471 | 472 | return true; 473 | } 474 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../.." 4 | if [ "$1" = "debug" ]; then 5 | DEBUG="debug" 6 | else 7 | OUT_DIR=$1 8 | DEBUG=$2 9 | fi 10 | 11 | # If not run from DataTables build script, redirect to there 12 | if [ -z "$DT_BUILD" ]; then 13 | cd $DT_DIR/build 14 | ./make.sh extension ColReorder $DEBUG 15 | cd - 16 | exit 17 | fi 18 | 19 | # Change into script's own dir 20 | cd $(dirname $0) 21 | 22 | DT_SRC=$(dirname $(dirname $(pwd))) 23 | DT_BUILT="${DT_SRC}/built/DataTables" 24 | . $DT_SRC/build/include.sh 25 | 26 | # Copy CSS 27 | rsync -r css $OUT_DIR 28 | css_frameworks colReorder $OUT_DIR/css 29 | 30 | # JS - compile and then copy into place 31 | $DT_SRC/node_modules/typescript/bin/tsc 32 | 33 | rsync -r js/*.js $OUT_DIR/js 34 | 35 | ## Remove the import - our wrapper does it for UMD as well as ESM 36 | sed -i "s#import DataTable from '../../../types/types';##" $OUT_DIR/js/dataTables.colReorder.js 37 | 38 | js_frameworks colReorder $OUT_DIR/js "jquery datatables.net-FW datatables.net-colreorder" 39 | 40 | HEADER="$(head -n 3 js/dataTables.colReorder.ts)" 41 | OUT=$OUT_DIR $DT_SRC/node_modules/rollup/dist/bin/rollup \ 42 | --banner "$HEADER" \ 43 | --config rollup.config.mjs 44 | 45 | js_wrap $OUT_DIR/js/dataTables.colReorder.js "jquery datatables.net" 46 | 47 | rm js/*.d.ts 48 | rm js/dataTables.colReorder.js js/functions.js js/ColReorder.js 49 | 50 | # Copy Types 51 | if [ -d $OUT_DIR/types ]; then 52 | rm -r $OUT_DIR/types 53 | fi 54 | mkdir $OUT_DIR/types 55 | 56 | if [ -d types/ ]; then 57 | cp types/* $OUT_DIR/types 58 | else 59 | if [ -f types.d.ts ]; then 60 | cp types.d.ts $OUT_DIR/types 61 | fi 62 | fi 63 | 64 | # Copy and build examples 65 | rsync -r examples $OUT_DIR 66 | examples_process $OUT_DIR/examples 67 | 68 | # Readme and license 69 | cp Readme.md $OUT_DIR 70 | cp License.txt $OUT_DIR 71 | 72 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | 2 | export default [ 3 | { 4 | input: process.env.OUT + '/js/dataTables.colReorder.js', 5 | output: { 6 | file: process.env.OUT + '/js/dataTables.colReorder.js', 7 | format: 'es' 8 | }, 9 | plugins: [] 10 | } 11 | ]; 12 | -------------------------------------------------------------------------------- /test/api/move().js: -------------------------------------------------------------------------------- 1 | describe('colReorder - move()', function() { 2 | var table; 3 | 4 | dt.libs({ 5 | js: ['jquery', 'datatables', 'colreorder'], 6 | css: ['datatables', 'colreorder'] 7 | }); 8 | 9 | describe('Check the defaults', function() { 10 | dt.html('basic'); 11 | let table; 12 | it('Exists and is a function', function() { 13 | table = $('#example').DataTable({ 14 | colReorder: true 15 | }); 16 | expect(typeof table.colReorder.move).toBe('function'); 17 | }); 18 | 19 | it('Returns an API instance', function() { 20 | expect(table.colReorder.move(1, 0) instanceof $.fn.dataTable.Api).toBe(true); 21 | }); 22 | }); 23 | 24 | function checkHeaders(expected) { 25 | for (let i = 0; i < 6; i++) { 26 | expect($('thead tr th:eq(' + i + ')').text()).toBe(expected[i]); 27 | } 28 | } 29 | 30 | describe('Check the behaviour', function() { 31 | dt.html('basic'); 32 | let table; 33 | it('Standard position initially', function() { 34 | table = $('#example').DataTable({ 35 | colReorder: true 36 | }); 37 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 38 | }); 39 | it('Move first column to the end', function() { 40 | table.colReorder.move(0, 5); 41 | checkHeaders(['Position', 'Office', 'Age', 'Start date', 'Salary', 'Name']); 42 | }); 43 | it('Move last column to the start', function() { 44 | table.colReorder.move(5, 0); 45 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 46 | }); 47 | it('Move middle columns', function() { 48 | table.colReorder.move(4, 2); 49 | checkHeaders(['Name', 'Position', 'Start date', 'Office', 'Age', 'Salary']); 50 | }); 51 | it('Move explicitly stating drop', function() { 52 | table.colReorder.move(2, 4, true); 53 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 54 | }); 55 | // TK COLIN not sure what this should do 56 | it('Move without drop', function() { 57 | table.colReorder.move(4, 2, false); 58 | checkHeaders(['Name', 'Position', 'Start date', 'Office', 'Age', 'Salary']); 59 | }); 60 | it('Draw to refresh', function() { 61 | table.draw(); 62 | checkHeaders(['Name', 'Position', 'Start date', 'Office', 'Age', 'Salary']); 63 | }); 64 | it('Move without invalidate', function() { 65 | table.colReorder.move(2, 4, true, false); 66 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 67 | 68 | table.order([2, 'asc']).draw(); 69 | expect($('tbody tr td:eq(2)').text()).toBe('Edinburgh'); 70 | }); 71 | it('Invalidate to refresh', function() { 72 | table 73 | .rows() 74 | .invalidate() 75 | .draw(); 76 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 77 | expect($('tbody tr td:eq(2)').text()).toBe('Edinburgh'); 78 | }); 79 | it('Move with invalidate', function() { 80 | table.colReorder.move(2, 4, true, true); 81 | checkHeaders(['Name', 'Position', 'Age', 'Start date', 'Office', 'Salary']); 82 | 83 | table.order([4, 'asc']).draw(); 84 | expect($('tbody tr td:eq(4)').text()).toBe('Edinburgh'); 85 | }); 86 | it('Table is returned to original order when table is destroyed', function() { 87 | table.destroy(); 88 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 89 | }); 90 | }); 91 | 92 | describe('Advanced/integration test', function() { 93 | dt.html('basic'); 94 | let table; 95 | 96 | it('Ensure row heights are sensible', function() { 97 | table = $('#example').DataTable({ 98 | colReorder: true 99 | }); 100 | 101 | let height = $('tbody tr:eq(1)').height(); 102 | 103 | table.column(1).visible(false); 104 | table.colReorder.move(4, 3); 105 | table.columns().visible(true); 106 | 107 | expect(height).toBe($('tbody tr:eq(1)').height()) 108 | }); 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /test/api/order().js: -------------------------------------------------------------------------------- 1 | describe('colReorder - order()', function() { 2 | var table; 3 | 4 | dt.libs({ 5 | js: ['jquery', 'datatables', 'colreorder'], 6 | css: ['datatables', 'colreorder'] 7 | }); 8 | 9 | describe('Check the defaults', function() { 10 | dt.html('basic'); 11 | let table; 12 | it('Exists and is a function', function() { 13 | table = $('#example').DataTable({ 14 | colReorder: true 15 | }); 16 | expect(typeof table.colReorder.order).toBe('function'); 17 | }); 18 | 19 | it('Setter returns an API instance', function() { 20 | expect(table.colReorder.order() instanceof Array).toBe(true); 21 | }); 22 | 23 | it('Setter returns an API instance', function() { 24 | expect(table.colReorder.order([0, 1, 2, 3, 4, 5]) instanceof $.fn.dataTable.Api).toBe(true); 25 | }); 26 | }); 27 | 28 | describe('Check the getter', function() { 29 | dt.html('basic'); 30 | let table; 31 | it('Standard position initially', function() { 32 | table = $('#example').DataTable({ 33 | colReorder: true 34 | }); 35 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([0, 1, 2, 3, 4, 5])); 36 | }); 37 | it('Reorder all columns', function() { 38 | table.colReorder.order([5, 4, 3, 2, 1, 0]); 39 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([5, 4, 3, 2, 1, 0])); 40 | }); 41 | it('Move a column', function() { 42 | table.colReorder.move(0, 5); 43 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([4, 3, 2, 1, 0, 5])); 44 | }); 45 | 46 | dt.html('basic'); 47 | it('Order changed at initialisation', function() { 48 | table = $('#example').DataTable({ 49 | colReorder: { 50 | order: [5, 4, 3, 2, 1, 0] 51 | } 52 | }); 53 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([5, 4, 3, 2, 1, 0])); 54 | }); 55 | }); 56 | 57 | function checkHeaders(expected) { 58 | for (let i = 0; i < 6; i++) { 59 | expect($('thead tr th:eq(' + i + ')').text()).toBe(expected[i]); 60 | } 61 | } 62 | 63 | describe('Check the setter', function() { 64 | dt.html('basic'); 65 | let table; 66 | it('Standard position initially', function() { 67 | table = $('#example').DataTable({ 68 | colReorder: true 69 | }); 70 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 71 | }); 72 | it('Reorder reverse', function() { 73 | table.colReorder.order([5, 4, 3, 2, 1, 0]); 74 | checkHeaders(['Salary', 'Start date', 'Age', 'Office', 'Position', 'Name']); 75 | }); 76 | it('Reorder back', function() { 77 | table.colReorder.order([5, 4, 3, 2, 1, 0]); 78 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 79 | }); 80 | it('Reorder reverse with false for original index', function() { 81 | table.colReorder.order([5, 4, 3, 2, 1, 0], false); 82 | checkHeaders(['Salary', 'Start date', 'Age', 'Office', 'Position', 'Name']); 83 | }); 84 | it('Reorder back with false for original index', function() { 85 | table.colReorder.order([5, 4, 3, 2, 1, 0], false); 86 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 87 | }); 88 | it('Reorder reverse using original indexes', function() { 89 | table.colReorder.order([5, 4, 3, 2, 1, 0], true); 90 | checkHeaders(['Salary', 'Start date', 'Age', 'Office', 'Position', 'Name']); 91 | }); 92 | it('Reorder back using original indexes', function() { 93 | table.colReorder.order([0, 1, 2, 3, 4, 5], true); 94 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/api/reset().js: -------------------------------------------------------------------------------- 1 | describe('colReorder - reset()', function() { 2 | var table; 3 | 4 | dt.libs({ 5 | js: ['jquery', 'datatables', 'colreorder'], 6 | css: ['datatables', 'colreorder'] 7 | }); 8 | 9 | describe('Check the defaults', function() { 10 | dt.html('basic'); 11 | let table; 12 | it('Exists and is a function', function() { 13 | table = $('#example').DataTable({ 14 | colReorder: true 15 | }); 16 | expect(typeof table.colReorder.reset).toBe('function'); 17 | }); 18 | 19 | it('Returns an API instance', function() { 20 | expect(table.colReorder.reset() instanceof $.fn.dataTable.Api).toBe(true); 21 | }); 22 | }); 23 | 24 | function checkHeaders(expected) { 25 | for (let i = 0; i < 6; i++) { 26 | expect($('thead tr th:eq(' + i + ')').text()).toBe(expected[i]); 27 | } 28 | } 29 | 30 | describe('Check the behaviour', function() { 31 | dt.html('basic'); 32 | let table; 33 | it('Standard position initially', function() { 34 | table = $('#example').DataTable({ 35 | colReorder: true 36 | }); 37 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 38 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([0, 1, 2, 3, 4, 5])); 39 | }); 40 | it('Move first column to the end', function() { 41 | table.colReorder.move(0, 5); 42 | checkHeaders(['Position', 'Office', 'Age', 'Start date', 'Salary', 'Name']); 43 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([1, 2, 3, 4, 5, 0])); 44 | }); 45 | it('Reset', function() { 46 | table.colReorder.reset(); 47 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 48 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([0, 1, 2, 3, 4, 5])); 49 | }); 50 | it('Reorder all columns', function() { 51 | table.colReorder.order([5, 4, 3, 2, 1, 0]); 52 | checkHeaders(['Salary', 'Start date', 'Age', 'Office', 'Position', 'Name']); 53 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([5, 4, 3, 2, 1, 0])); 54 | }); 55 | it('Reset', function() { 56 | table.colReorder.reset(); 57 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 58 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([0, 1, 2, 3, 4, 5])); 59 | }); 60 | 61 | dt.html('basic'); 62 | it('Order changed at initialisation', function() { 63 | table = $('#example').DataTable({ 64 | colReorder: { 65 | order: [5, 4, 3, 2, 1, 0] 66 | } 67 | }); 68 | checkHeaders(['Salary', 'Start date', 'Age', 'Office', 'Position', 'Name']); 69 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([5, 4, 3, 2, 1, 0])); 70 | }); 71 | it('Reset', function() { 72 | table.colReorder.reset(); 73 | checkHeaders(['Name', 'Position', 'Office', 'Age', 'Start date', 'Salary']); 74 | expect(JSON.stringify(table.colReorder.order())).toBe(JSON.stringify([0, 1, 2, 3, 4, 5])); 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /test/api/transpose().js: -------------------------------------------------------------------------------- 1 | describe('colReorder - transpose()', function() { 2 | let table; 3 | 4 | dt.libs({ 5 | js: ['jquery', 'datatables', 'colreorder'], 6 | css: ['datatables', 'colreorder'] 7 | }); 8 | 9 | describe('Check the defaults', function() { 10 | dt.html('basic'); 11 | it('Exists and is a function', function() { 12 | table = $('#example').DataTable({ 13 | colReorder: true 14 | }); 15 | expect(typeof table.colReorder.transpose).toBe('function'); 16 | }); 17 | 18 | it('Returns an integer instance', function() { 19 | expect(typeof table.colReorder.transpose(1)).toBe('number'); 20 | }); 21 | 22 | it('Returns an array instance', function() { 23 | expect(table.colReorder.transpose([1, 2]) instanceof Array).toBe(true); 24 | }); 25 | }); 26 | 27 | function checkTranspose(idx, expected, direction) { 28 | // check individuals 29 | for (let i = 0; i < idx.length; i++) { 30 | expect(table.colReorder.transpose(idx[i], direction)).toBe(expected[i]); 31 | } 32 | 33 | // check array 34 | expect(JSON.stringify(table.colReorder.transpose(idx, direction))).toBe(JSON.stringify(expected)); 35 | } 36 | 37 | describe('Check the behaviour', function() { 38 | dt.html('basic'); 39 | it('Standard position initially', function() { 40 | table = $('#example').DataTable({ 41 | colReorder: true 42 | }); 43 | checkTranspose([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], 'toCurrent'); 44 | checkTranspose([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], 'fromOriginal'); 45 | checkTranspose([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], 'toOriginal'); 46 | checkTranspose([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], 'fromCurrent'); 47 | }); 48 | it('Move a column', function() { 49 | table.colReorder.move(0, 5); 50 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 0, 1, 2, 3, 4], 'toCurrent'); 51 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 0, 1, 2, 3, 4], 'fromOriginal'); 52 | checkTranspose([0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 0], 'toOriginal'); 53 | checkTranspose([0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 0], 'fromCurrent'); 54 | }); 55 | it('Check a subset', function() { 56 | checkTranspose([0, 1], [5, 0], 'toCurrent'); 57 | checkTranspose([0, 1], [5, 0], 'fromOriginal'); 58 | checkTranspose([0, 1], [1, 2], 'toOriginal'); 59 | checkTranspose([0, 1], [1, 2], 'fromCurrent'); 60 | }); 61 | it('After a reset', function() { 62 | table.colReorder.reset(); 63 | checkTranspose([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], 'toCurrent'); 64 | checkTranspose([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], 'fromOriginal'); 65 | checkTranspose([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], 'toOriginal'); 66 | checkTranspose([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], 'fromCurrent'); 67 | }); 68 | it('Reorder', function() { 69 | table.colReorder.order([5, 4, 3, 2, 1, 0]); 70 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0], 'toCurrent'); 71 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0], 'fromOriginal'); 72 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0], 'toOriginal'); 73 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0], 'fromCurrent'); 74 | }); 75 | 76 | dt.html('basic'); 77 | it('Modified position initially', function() { 78 | table = $('#example').DataTable({ 79 | colReorder: { 80 | order: [5, 4, 3, 2, 1, 0] 81 | } 82 | }); 83 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0], 'toCurrent'); 84 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0], 'fromOriginal'); 85 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0], 'toOriginal'); 86 | checkTranspose([0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0], 'fromCurrent'); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /test/events/column-reorder.js: -------------------------------------------------------------------------------- 1 | describe('colReorder - column-reorder', function() { 2 | let table; 3 | 4 | dt.libs({ 5 | js: ['jquery', 'datatables', 'colreorder'], 6 | css: ['datatables', 'colreorder'] 7 | }); 8 | 9 | describe('Check the defaults', function() { 10 | let table; 11 | let params = undefined; 12 | let count = 0; 13 | let headers = ''; 14 | 15 | dt.html('basic'); 16 | it('Set stuff up', function() { 17 | table = $('#example').DataTable({ 18 | colReorder: true 19 | }); 20 | }); 21 | it('Is called with the right parameters', function() { 22 | table.on('column-reorder', function() { 23 | params = arguments; 24 | count++; 25 | headers = $('thead tr th').text(); 26 | }); 27 | 28 | table.colReorder.move(0, 5); 29 | 30 | expect(params.length).toBe(3); 31 | expect(params[0] instanceof $.Event).toBe(true); 32 | expect(params[1]).toBe(table.settings()[0]); 33 | expect(typeof params[2]).toBe('object'); 34 | }); 35 | it('Header is not changed until after the event', function() { 36 | expect(headers).toBe('NamePositionOfficeAgeStart dateSalary'); 37 | expect($('thead tr th').text()).toBe('PositionOfficeAgeStart dateSalaryName'); 38 | }); 39 | it('called once per move', function() { 40 | expect(count).toBe(1); 41 | }); 42 | }); 43 | 44 | describe('Full ordering', function() { 45 | let table; 46 | let params; 47 | let count = 0; 48 | let headers; 49 | 50 | dt.html('basic'); 51 | it('Set stuff up', function() { 52 | table = $('#example').DataTable({ 53 | colReorder: true 54 | }); 55 | 56 | table.on('column-reorder', function() { 57 | params = arguments; 58 | count++; 59 | headers = $('thead tr th').text(); 60 | }); 61 | 62 | table.colReorder.order([5,4,3,2,1,0]); 63 | expect(params.length).toBe(3); 64 | }); 65 | it('header was updated', function() { 66 | expect(headers).toBe('NamePositionOfficeAgeStart dateSalary'); 67 | expect($('thead tr th').text()).toBe('SalaryStart dateAgeOfficePositionName'); 68 | }); 69 | it('called once per column move', function() { 70 | expect(count).toBe(5); 71 | }); 72 | }); 73 | 74 | describe('Functional tests', function() { 75 | let table; 76 | let params; 77 | let count; 78 | let headers; 79 | 80 | function resetTest() { 81 | table.colReorder.reset(); 82 | count = 0; 83 | params = undefined; 84 | headers = ''; 85 | } 86 | 87 | dt.html('basic'); 88 | it('Set stuff up', function() { 89 | table = $('#example').DataTable({ 90 | colReorder: true 91 | }); 92 | 93 | table.on('column-reorder', function() { 94 | params = arguments; 95 | count++; 96 | headers = $('thead tr th').text(); 97 | }); 98 | }); 99 | it('Called just the once for a move', function() { 100 | resetTest(); 101 | table.colReorder.move(0, 5); 102 | 103 | expect(count).toBe(1); 104 | }); 105 | it('from has the correct value', function() { 106 | expect(params[2].from).toEqual([0]); 107 | }); 108 | it('to has the correct value', function() { 109 | expect(params[2].to).toBe(5); 110 | }); 111 | it('mapping has the correct value', function() { 112 | expect(JSON.stringify(params[2].mapping)).toBe(JSON.stringify([5, 0, 1, 2, 3, 4])); 113 | }); 114 | it('drop has the correct value', function() { 115 | // DD-744 expect(params[2].drop).toBe(true); 116 | }); 117 | 118 | it('Called for each move when order() called', function() { 119 | resetTest(); 120 | table.colReorder.order([5, 4, 3, 2, 1, 0]); 121 | 122 | expect(count).toBe(5); 123 | }); 124 | it('last from has the correct value', function() { 125 | expect(params[2].from).toEqual([5]); 126 | }); 127 | it('last to has the correct value', function() { 128 | expect(params[2].to).toBe(4); 129 | }); 130 | it('last mapping has the correct value', function() { 131 | expect(JSON.stringify(params[2].mapping)).toBe(JSON.stringify([0, 1, 2, 3, 5, 4])); 132 | }); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /test/events/columns-reordered.js: -------------------------------------------------------------------------------- 1 | describe('colReorder - columns-reordered', function() { 2 | let table; 3 | 4 | dt.libs({ 5 | js: ['jquery', 'datatables', 'colreorder'], 6 | css: ['datatables', 'colreorder'] 7 | }); 8 | 9 | describe('Check the defaults', function() { 10 | let table; 11 | let params = undefined; 12 | let count = 0; 13 | let headers = ''; 14 | 15 | dt.html('basic'); 16 | it('Set stuff up', function() { 17 | table = $('#example').DataTable({ 18 | colReorder: true 19 | }); 20 | }); 21 | it('Is called with the right parameters', function() { 22 | table.on('columns-reordered', function() { 23 | params = arguments; 24 | count++; 25 | headers = $('thead tr th').text(); 26 | }); 27 | 28 | table.colReorder.move(0, 5); 29 | 30 | expect(params.length).toBe(2); 31 | expect(params[0] instanceof $.Event).toBe(true); 32 | expect(typeof params[1]).toBe('object'); 33 | }); 34 | it('Is called after the move', function() { 35 | expect(headers).toBe('PositionOfficeAgeStart dateSalaryName'); 36 | }); 37 | it('called once per move', function() { 38 | expect(count).toBe(1); 39 | }); 40 | }); 41 | 42 | describe('Full ordering', function() { 43 | let table; 44 | let params; 45 | let count = 0; 46 | let headers; 47 | 48 | dt.html('basic'); 49 | it('Set stuff up', function() { 50 | table = $('#example').DataTable({ 51 | colReorder: true 52 | }); 53 | 54 | table.on('columns-reordered', function() { 55 | params = arguments; 56 | count++; 57 | headers = $('thead tr th').text(); 58 | }); 59 | 60 | table.colReorder.order([5,4,3,2,1,0]); 61 | expect(params.length).toBe(2); 62 | }); 63 | it('header was updated', function() { 64 | expect(headers).toBe('SalaryStart dateAgeOfficePositionName'); 65 | }); 66 | it('called once per order', function() { 67 | expect(count).toBe(1); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/options/enable.js: -------------------------------------------------------------------------------- 1 | describe('colReorder - enable', function() { 2 | let table; 3 | 4 | dt.libs({ 5 | js: ['jquery', 'datatables', 'colreorder'], 6 | css: ['datatables', 'colreorder'] 7 | }); 8 | 9 | dt.html('basic'); 10 | it('No order set by default', function() { 11 | expect($.fn.dataTable.ColReorder.defaults.enable).toBe(true); 12 | }); 13 | 14 | // TK COLIN need to add test to confirm user interaction 15 | dt.html('basic'); 16 | it('Can still move columns with the API', function() { 17 | table = $('#example').DataTable({ 18 | colReorder: { 19 | enable: false 20 | } 21 | }); 22 | table.colReorder.move(5,0); 23 | expect($('thead tr th').text()).toBe('SalaryNamePositionOfficeAgeStart date'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/options/order.js: -------------------------------------------------------------------------------- 1 | describe('colReorder - order', function() { 2 | let table; 3 | 4 | dt.libs({ 5 | js: ['jquery', 'datatables', 'colreorder'], 6 | css: ['datatables', 'colreorder'] 7 | }); 8 | 9 | dt.html('basic'); 10 | it('No order set by default', function() { 11 | table = $('#example').DataTable({ 12 | colReorder: true 13 | }); 14 | expect($.fn.dataTable.ColReorder.defaults.order).toBe(null); 15 | expect($('thead tr th').text()).toBe('NamePositionOfficeAgeStart dateSalary'); 16 | }); 17 | 18 | dt.html('basic'); 19 | it('Order complied with', function() { 20 | table = $('#example').DataTable({ 21 | colReorder: { 22 | order: [5, 4, 3, 2, 1, 0] 23 | } 24 | }); 25 | expect($('thead tr th').text()).toBe('SalaryStart dateAgeOfficePositionName'); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "ES6", 5 | "lib": ["es2017", "es2016", "dom"], 6 | "allowUnreachableCode": true, 7 | "allowSyntheticDefaultImports": true, 8 | "pretty": true, 9 | "declaration": true 10 | }, 11 | "files": [ 12 | "js/dataTables.colReorder.ts" 13 | ] 14 | } -------------------------------------------------------------------------------- /types/colReorder.bootstrap.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import DataTable from 'datatables.net-colreorder'; 3 | 4 | export default DataTable; 5 | export * from 'datatables.net-colreorder'; 6 | -------------------------------------------------------------------------------- /types/colReorder.bootstrap4.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import DataTable from 'datatables.net-colreorder'; 3 | 4 | export default DataTable; 5 | export * from 'datatables.net-colreorder'; 6 | -------------------------------------------------------------------------------- /types/colReorder.bootstrap5.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import DataTable from 'datatables.net-colreorder'; 3 | 4 | export default DataTable; 5 | export * from 'datatables.net-colreorder'; 6 | -------------------------------------------------------------------------------- /types/colReorder.bulma.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import DataTable from 'datatables.net-colreorder'; 3 | 4 | export default DataTable; 5 | export * from 'datatables.net-colreorder'; 6 | -------------------------------------------------------------------------------- /types/colReorder.dataTables.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import DataTable from 'datatables.net-colreorder'; 3 | 4 | export default DataTable; 5 | export * from 'datatables.net-colreorder'; 6 | -------------------------------------------------------------------------------- /types/colReorder.foundation.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import DataTable from 'datatables.net-colreorder'; 3 | 4 | export default DataTable; 5 | export * from 'datatables.net-colreorder'; 6 | -------------------------------------------------------------------------------- /types/colReorder.jqueryui.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import DataTable from 'datatables.net-colreorder'; 3 | 4 | export default DataTable; 5 | export * from 'datatables.net-colreorder'; 6 | -------------------------------------------------------------------------------- /types/colReorder.semanticui.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import DataTable from 'datatables.net-colreorder'; 3 | 4 | export default DataTable; 5 | export * from 'datatables.net-colreorder'; 6 | -------------------------------------------------------------------------------- /types/types.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for DataTables ColReorder 2 | // 3 | // Project: https://datatables.net/extensions/colreorder/, https://datatables.net 4 | // Definitions by: 5 | // SpryMedia 6 | // Andy Ma 7 | 8 | /// 9 | 10 | import DataTables, {Api, ColumnSelector} from 'datatables.net'; 11 | 12 | export default DataTables; 13 | 14 | 15 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 16 | * DataTables' types integration 17 | */ 18 | declare module 'datatables.net' { 19 | interface Config { 20 | /** 21 | * ColReorder extension options 22 | */ 23 | colReorder?: boolean | ConfigColReorder; 24 | } 25 | 26 | interface Api { 27 | /** 28 | * ColReorder methods container 29 | * 30 | * @returns Api for chaining with the additional ColReorder methods 31 | */ 32 | colReorder: ApiColReorderMethods; 33 | } 34 | 35 | interface DataTablesStatic { 36 | /** 37 | * ColReorder class 38 | */ 39 | ColReorder: { 40 | /** 41 | * Create a new ColReorder instance for the target DataTable 42 | */ 43 | new (dt: Api, settings: boolean | ConfigColReorder): DataTablesStatic['ColReorder']; 44 | 45 | /** 46 | * ColReorder version 47 | */ 48 | version: string; 49 | 50 | /** 51 | * Default configuration values 52 | */ 53 | defaults: ConfigColReorder; 54 | } 55 | } 56 | } 57 | 58 | 59 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 60 | * Options 61 | */ 62 | 63 | interface ConfigColReorder { 64 | /** 65 | * Columns to allow reordering on via the UI. 66 | */ 67 | columns?: ColumnSelector; 68 | 69 | /** 70 | * Initial enablement state of ColReorder - Since 1.5.0 71 | */ 72 | enable?: boolean; 73 | 74 | /** 75 | * Set a default order for the columns in the table 76 | */ 77 | order?: number[]; 78 | } 79 | 80 | 81 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 82 | * API 83 | */ 84 | 85 | interface ApiColReorderMethods extends Omit, 'order'> { 86 | /** 87 | * Disable end user ability to reorder columns. 88 | * 89 | * @returns DataTables Api instance. 90 | */ 91 | disable(): Api; 92 | 93 | /** 94 | * Enable and disable user ability to reorder columns in a table. 95 | * 96 | * @param flag if true enable colReorder, if false disable. 97 | * @returns DataTables Api instance 98 | */ 99 | enable(flag?: boolean): Api; 100 | 101 | /** 102 | * Programmatically reorder columns 103 | * 104 | * @param from Column index to move. 105 | * @param to New index to move the column to. 106 | * @param drop Indicate that this is the final move. Set this to false if you are performing multiple moves 107 | * @param invalidate Invalidate the row data. As with drop it can be useful to set this to false if performing multiple moves. Otherwise allow it to default which will ensure that the table's data is fully insync with the column order. 108 | * @returns Unmodified API instance. 109 | */ 110 | move(from: number, to: number, drop: boolean, invalidate: boolean): Api; 111 | 112 | /** 113 | * Get the current column order. 114 | * 115 | * @returns Returns an array of column indexes. The column index given is the original column index, with its new position defined by the location in the returned array. 116 | */ 117 | order(): Array; 118 | 119 | /** 120 | * Set column order 121 | * 122 | * @param newOrder Array of column indexes that define where the columns should be placed after the reorder. 123 | * @param originalIndexes Set to be true to indicate that the indexes passed in are the original indexes. false or undefined (default) will treat them as the current indexes. 124 | * @returns DataTables Api instance for chaining 125 | */ 126 | order(newOrder: number[], originalIndexes?: boolean): Api; 127 | 128 | /** 129 | * Restore the loaded column order 130 | * 131 | * @returns DataTables Api instance. 132 | */ 133 | reset(): Api; 134 | 135 | /** 136 | * Convert one or more column indexes to and from current and original indexes 137 | * 138 | * @param idx The index, or array of indexes to transpose. 139 | * @param direction Set what transposition is required. 140 | * @returns The transpose values 141 | */ 142 | transpose(idx: number | number[], direction?: "toCurrent" | "toOriginal" | "fromOriginal" | "fromCurrent"): Array; 143 | } 144 | --------------------------------------------------------------------------------