Created
June 23, 2021 13:40
-
-
Save MayaGans/d41b9ab56f87326013526989570bcab7 to your computer and use it in GitHub Desktop.
sticky
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| Copyright (c) 2013-2020 NDP Software, Andrew J. Peterson | |
| * https://github.com/ndp/align-column | |
| */ | |
| $.fn.alignColumn = function (columns, options) { | |
| var config = $.extend({}, { | |
| addedCellClass: 'added', | |
| skipTHs: true | |
| }, options); | |
| if (!jQuery.isArray(columns)) { | |
| columns = [columns]; | |
| } | |
| columns = columns.sort().reverse(); | |
| if (config.debug) { | |
| console.log('Adjusting columns ', columns); | |
| } | |
| var splitter; | |
| if (typeof config.center == 'string') { | |
| if (config.debug) { | |
| console.log('Splitting by ', config.center); | |
| } | |
| splitter = function (text) { | |
| if(text === undefined) return ''; | |
| var s = text.split(config.center); | |
| return (s.length == 2) ? [s[0], config.center + s[1]] : [text]; | |
| } | |
| } else if (jQuery.isFunction(config.center)) { | |
| if (config.debug) { | |
| console.log('Splitting using fn'); | |
| } | |
| splitter = config.center; | |
| } else { | |
| if (config.debug) { | |
| console.log('Splitting using decimal align'); | |
| } | |
| splitter = function (text) { | |
| if (!text) return ['']; | |
| var match; | |
| if (text.indexOf('.') == -1) { | |
| match = text.match(/^(\S+[\d]+)(.*)$/); | |
| } else { | |
| match = text.match(/^([^\.]+)(\..*)$/); | |
| } | |
| return match ? [match[1], match[2] || ''] : [text, '']; | |
| }; | |
| } | |
| return $(this).each(function () { | |
| var $table = $(this).css('border-collapse', 'collapse'); // no way around this! | |
| var $rows = $table.find('tr'); | |
| $rows.each(function () { | |
| var cells = $(this).find('>td,>th').not('.' + config.addedCellClass).get(); | |
| for (var index = 0; index < columns.length; index++) { | |
| var colIndex = columns[index], | |
| $cell = $(cells[colIndex]), | |
| text = $cell.html(), | |
| pieces = splitter(text), | |
| existingClasses = $cell.attr('class'); | |
| if (config.debug) { | |
| console.log("Split '" + text + "' into " + pieces.length + " pieces: ", "" + pieces[0], "" + pieces[1]); | |
| } | |
| if (pieces.length === 2 && | |
| (!config.skipTHs || $cell.get(0).nodeName == 'TD')) { | |
| if (' ' == pieces[0][pieces[0].length - 1]) { | |
| pieces[0] = pieces[0].slice(0, pieces[0].length - 1) + ' '; | |
| } | |
| $cell.html(pieces[0]). | |
| css({textAlign: 'right', paddingRight: 0, borderRight: 'none'}); | |
| $('<td></td>'). | |
| html(pieces[1]). | |
| css({textAlign: 'left', paddingLeft: 0, borderLeft: 'none'}). | |
| addClass(config.addedCellClass). | |
| addClass(existingClasses). | |
| insertAfter($cell); | |
| } else { | |
| $cell.attr('colspan', 2); | |
| } | |
| } | |
| }); | |
| }); | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| library(shiny) | |
| library(magrittr) | |
| library(dplyr) | |
| test <- data.frame( | |
| a = c(rep("a", 3), rep("b", 3)), | |
| b = c(10000, 10, 1.1, 2000.3000, 2003.01, 200), | |
| c = c(10000, 10, 1.1, 2000.03, 2003.001, 200), | |
| d = c(10000, 10, 1.1, 2000.3000, 2003.01, 200), | |
| e = c(10000, 10, 1.1, 2000.03, 2003.001, 200) | |
| ) | |
| ui <- fluidPage( | |
| includeCSS("www/styles.css"), | |
| # tags$head(tags$script(src = "rowsGroup.js")), | |
| tags$head(tags$script(src = "alignColumn.js")), | |
| tags$head(tags$script(src = "decimalAlign.js")), | |
| actionButton("change", "change"), | |
| DT::dataTableOutput("example") | |
| ) | |
| server <- function(input, output, session) { | |
| output$example <- DT::renderDT({ | |
| dtable <- DT::datatable(test, | |
| filter = "top", | |
| rownames = FALSE, | |
| options = list( | |
| fixedHeader = TRUE, | |
| rowsGroup = list(0), | |
| scrollX = TRUE, | |
| scrollY = TRUE, | |
| fixedColumns = list(leftColumns = 1) | |
| ), | |
| # callback = DT::JS("decimalAlign({id: 'example', columns: [1,2]})"), | |
| extensions = c("FixedHeader", "FixedColumns") | |
| ) | |
| dep1 <- htmltools::htmlDependency("RowsGroup", | |
| "2.0.0", | |
| src = "www", | |
| script = "rowsGroup.js") | |
| dtable$dependencies <- c(dtable$dependencies, list(dep1)) | |
| dtable | |
| }) | |
| } | |
| shinyApp(ui = ui, server = server) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| decimalAlign = function(message) { | |
| console.log(message) | |
| console.log("its working") | |
| setTimeout(function(){ | |
| $('#' + message.id).alignColumn(message.columns) | |
| }, 200); | |
| } | |
| Shiny.addCustomMessageHandler("decimal_align", decimalAlign) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /*! RowsGroup for DataTables v2.0.0 | |
| * 2015-2016 Alexey Shildyakov ashl1future@gmail.com | |
| * 2016 Tibor Wekerle | |
| */ | |
| /** | |
| * @summary RowsGroup | |
| * @description Group rows by specified columns | |
| * @version 2.0.0 | |
| * @file dataTables.rowsGroup.js | |
| * @author Alexey Shildyakov (ashl1future@gmail.com) | |
| * @contact ashl1future@gmail.com | |
| * @copyright Alexey Shildyakov | |
| * | |
| * License MIT - http://datatables.net/license/mit | |
| * | |
| * This feature plug-in for DataTables automatically merges columns cells | |
| * based on it's values equality. It supports multi-column row grouping | |
| * in according to the requested order with dependency from each previous | |
| * requested columns. Now it supports ordering and searching. | |
| * Please see the example.html for details. | |
| * | |
| * Rows grouping in DataTables can be enabled by using any one of the following | |
| * options: | |
| * | |
| * * Setting the `rowsGroup` parameter in the DataTables initialisation | |
| * to array which containes columns selectors | |
| * (https://datatables.net/reference/type/column-selector) used for grouping. i.e. | |
| * rowsGroup = [1, 'columnName:name', ] | |
| * * Setting the `rowsGroup` parameter in the DataTables defaults | |
| * (thus causing all tables to have this feature) - i.e. | |
| * `$.fn.dataTable.defaults.RowsGroup = [0]`. | |
| * * Creating a new instance: `new $.fn.dataTable.RowsGroup( table, columnsForGrouping );` | |
| * where `table` is a DataTable's API instance and `columnsForGrouping` is the array | |
| * described above. | |
| * | |
| * For more detailed information please see: | |
| * | |
| */ | |
| (function($){ | |
| ShowedDataSelectorModifier = { | |
| order: 'current', | |
| page: 'current', | |
| search: 'applied', | |
| } | |
| GroupedColumnsOrderDir = 'asc'; | |
| /* | |
| * columnsForGrouping: array of DTAPI:cell-selector for columns for which rows grouping is applied | |
| */ | |
| var RowsGroup = function ( dt, columnsForGrouping ) | |
| { | |
| this.table = dt.table(); | |
| this.columnsForGrouping = columnsForGrouping; | |
| // set to True when new reorder is applied by RowsGroup to prevent order() looping | |
| this.orderOverrideNow = false; | |
| this.mergeCellsNeeded = false; // merge after init | |
| this.order = [] | |
| var self = this; | |
| dt.on('order.dt', function ( e, settings) { | |
| if (!self.orderOverrideNow) { | |
| self.orderOverrideNow = true; | |
| self._updateOrderAndDraw() | |
| } else { | |
| self.orderOverrideNow = false; | |
| } | |
| }) | |
| dt.on('preDraw.dt', function ( e, settings) { | |
| if (self.mergeCellsNeeded) { | |
| self.mergeCellsNeeded = false; | |
| self._mergeCells() | |
| } | |
| }) | |
| dt.on('column-visibility.dt', function ( e, settings) { | |
| self.mergeCellsNeeded = true; | |
| }) | |
| dt.on('search.dt', function ( e, settings) { | |
| // This might to increase the time to redraw while searching on tables | |
| // with huge shown columns | |
| self.mergeCellsNeeded = true; | |
| }) | |
| dt.on('page.dt', function ( e, settings) { | |
| self.mergeCellsNeeded = true; | |
| }) | |
| dt.on('length.dt', function ( e, settings) { | |
| self.mergeCellsNeeded = true; | |
| }) | |
| dt.on('xhr.dt', function ( e, settings) { | |
| self.mergeCellsNeeded = true; | |
| }) | |
| this._updateOrderAndDraw(); | |
| /* Events sequence while Add row (also through Editor) | |
| * addRow() function | |
| * draw() function | |
| * preDraw() event | |
| * mergeCells() - point 1 | |
| * Appended new row breaks visible elements because the mergeCells() on previous step doesn't apllied to already processing data | |
| * order() event | |
| * _updateOrderAndDraw() | |
| * preDraw() event | |
| * mergeCells() | |
| * Appended new row now has properly visibility as on current level it has already applied changes from first mergeCells() call (point 1) | |
| * draw() event | |
| */ | |
| }; | |
| RowsGroup.prototype = { | |
| setMergeCells: function(){ | |
| this.mergeCellsNeeded = true; | |
| }, | |
| mergeCells: function() | |
| { | |
| this.setMergeCells(); | |
| this.table.draw(); | |
| }, | |
| _getOrderWithGroupColumns: function (order, groupedColumnsOrderDir) | |
| { | |
| if (groupedColumnsOrderDir === undefined) | |
| groupedColumnsOrderDir = GroupedColumnsOrderDir | |
| var self = this; | |
| var groupedColumnsIndexes = this.columnsForGrouping.map(function(columnSelector){ | |
| return self.table.column(columnSelector).index() | |
| }) | |
| var groupedColumnsKnownOrder = order.filter(function(columnOrder){ | |
| return groupedColumnsIndexes.indexOf(columnOrder[0]) >= 0 | |
| }) | |
| var nongroupedColumnsOrder = order.filter(function(columnOrder){ | |
| return groupedColumnsIndexes.indexOf(columnOrder[0]) < 0 | |
| }) | |
| var groupedColumnsKnownOrderIndexes = groupedColumnsKnownOrder.map(function(columnOrder){ | |
| return columnOrder[0] | |
| }) | |
| var groupedColumnsOrder = groupedColumnsIndexes.map(function(iColumn){ | |
| var iInOrderIndexes = groupedColumnsKnownOrderIndexes.indexOf(iColumn) | |
| if (iInOrderIndexes >= 0) | |
| return [iColumn, groupedColumnsKnownOrder[iInOrderIndexes][1]] | |
| else | |
| return [iColumn, groupedColumnsOrderDir] | |
| }) | |
| groupedColumnsOrder.push.apply(groupedColumnsOrder, nongroupedColumnsOrder) | |
| return groupedColumnsOrder; | |
| }, | |
| // Workaround: the DT reset ordering to 'asc' from multi-ordering if user order on one column (without shift) | |
| // but because we always has multi-ordering due to grouped rows this happens every time | |
| _getInjectedMonoSelectWorkaround: function(order) | |
| { | |
| if (order.length === 1) { | |
| // got mono order - workaround here | |
| var orderingColumn = order[0][0] | |
| var previousOrder = this.order.map(function(val){ | |
| return val[0] | |
| }) | |
| var iColumn = previousOrder.indexOf(orderingColumn); | |
| if (iColumn >= 0) { | |
| // assume change the direction, because we already has that in previos order | |
| return [[orderingColumn, this._toogleDirection(this.order[iColumn][1])]] | |
| } // else This is the new ordering column. Proceed as is. | |
| } // else got milti order - work normal | |
| return order; | |
| }, | |
| _mergeCells: function() | |
| { | |
| var columnsIndexes = this.table.columns(this.columnsForGrouping, ShowedDataSelectorModifier).indexes().toArray() | |
| var showedRowsCount = this.table.rows(ShowedDataSelectorModifier)[0].length | |
| this._mergeColumn(0, showedRowsCount - 1, columnsIndexes) | |
| }, | |
| // the index is relative to the showed data | |
| // (selector-modifier = {order: 'current', page: 'current', search: 'applied'}) index | |
| _mergeColumn: function(iStartRow, iFinishRow, columnsIndexes) | |
| { | |
| var columnsIndexesCopy = columnsIndexes.slice() | |
| currentColumn = columnsIndexesCopy.shift() | |
| currentColumn = this.table.column(currentColumn, ShowedDataSelectorModifier) | |
| var columnNodes = currentColumn.nodes() | |
| var columnValues = currentColumn.data() | |
| var newSequenceRow = iStartRow, | |
| iRow; | |
| for (iRow = iStartRow + 1; iRow <= iFinishRow; ++iRow) { | |
| if (columnValues[iRow] === columnValues[newSequenceRow]) { | |
| $(columnNodes[iRow]).hide() | |
| } else { | |
| $(columnNodes[newSequenceRow]).show() | |
| $(columnNodes[newSequenceRow]).attr('rowspan', (iRow-1) - newSequenceRow + 1) | |
| if (columnsIndexesCopy.length > 0) | |
| this._mergeColumn(newSequenceRow, (iRow-1), columnsIndexesCopy) | |
| newSequenceRow = iRow; | |
| } | |
| } | |
| $(columnNodes[newSequenceRow]).show() | |
| $(columnNodes[newSequenceRow]).attr('rowspan', (iRow-1)- newSequenceRow + 1) | |
| if (columnsIndexesCopy.length > 0) | |
| this._mergeColumn(newSequenceRow, (iRow-1), columnsIndexesCopy) | |
| }, | |
| _toogleDirection: function(dir) | |
| { | |
| return dir == 'asc'? 'desc': 'asc'; | |
| }, | |
| _updateOrderAndDraw: function() | |
| { | |
| this.mergeCellsNeeded = true; | |
| var currentOrder = this.table.order(); | |
| currentOrder = this._getInjectedMonoSelectWorkaround(currentOrder); | |
| this.order = this._getOrderWithGroupColumns(currentOrder) | |
| this.table.order($.extend(true, Array(), this.order)) | |
| this.table.draw() | |
| }, | |
| }; | |
| $.fn.dataTable.RowsGroup = RowsGroup; | |
| $.fn.DataTable.RowsGroup = RowsGroup; | |
| // Automatic initialisation listener | |
| $(document).on( 'init.dt', function ( e, settings ) { | |
| if ( e.namespace !== 'dt' ) { | |
| return; | |
| } | |
| var api = new $.fn.dataTable.Api( settings ); | |
| if ( settings.oInit.rowsGroup || | |
| $.fn.dataTable.defaults.rowsGroup ) | |
| { | |
| options = settings.oInit.rowsGroup? | |
| settings.oInit.rowsGroup: | |
| $.fn.dataTable.defaults.rowsGroup; | |
| var rowsGroup = new RowsGroup( api, options ); | |
| $.fn.dataTable.Api.register( 'rowsgroup.update()', function () { | |
| rowsGroup.mergeCells(); | |
| return this; | |
| } ); | |
| $.fn.dataTable.Api.register( 'rowsgroup.updateNextDraw()', function () { | |
| rowsGroup.setMergeCells(); | |
| return this; | |
| } ); | |
| } | |
| } ); | |
| }(jQuery)); | |
| /* | |
| TODO: Provide function which determines the all <tr>s and <td>s with "rowspan" html-attribute is parent (groupped) for the specified <tr> or <td>. To use in selections, editing or hover styles. | |
| TODO: Feature | |
| Use saved order direction for grouped columns | |
| Split the columns into grouped and ungrouped. | |
| user = grouped+ungrouped | |
| grouped = grouped | |
| saved = grouped+ungrouped | |
| For grouped uses following order: user -> saved (because 'saved' include 'grouped' after first initialisation). This should be done with saving order like for 'groupedColumns' | |
| For ungrouped: uses only 'user' input ordering | |
| */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment