/** * @author Nadim Basalamah * @version: v1.1.0 * https://github.com/dimbslmh/bootstrap-table/tree/master/src/extensions/multiple-sort/bootstrap-table-multiple-sort.js * Modification: ErwannNevou */ let isSingleSort = false const Utils = $.fn.bootstrapTable.utils const bootstrap = { bootstrap3: { icons: { plus: 'glyphicon-plus', minus: 'glyphicon-minus', sort: 'glyphicon-sort' }, html: { multipleSortModal: ` `, multipleSortButton: '', multipleSortSelect: '' } }, semantic: { icons: { 'plus': 'fa-plus', 'minus': 'fa-minus', 'sort': 'fa-sort' }, html: { multipleSortModal: ` `, multipleSortButton: '', multipleSortSelect: '' } }, foundation: { icons: { 'plus': 'fa-plus', 'minus': 'fa-minus', 'sort': 'fa-sort' }, html: { multipleSortModal: ` `, multipleSortButton: '', multipleSortSelect: '' } } }[$.fn.bootstrapTable.theme] $.extend($.fn.bootstrapTable.defaults.icons, bootstrap.icons) $.extend($.fn.bootstrapTable.defaults.html, bootstrap.html) const showSortModal = that => { const _selector = that.sortModalSelector const _id = `#${_selector}` const o = that.options if (!$(_id).hasClass('modal')) { const sModal = Utils.sprintf( that.constants.html.multipleSortModal, _selector, _selector, _selector, that.options.formatMultipleSort(), Utils.sprintf(that.constants.html.icon, o.iconsPrefix, that.constants.icons.plus), that.options.formatAddLevel(), Utils.sprintf(that.constants.html.icon, o.iconsPrefix, that.constants.icons.minus), that.options.formatDeleteLevel(), that.options.formatColumn(), that.options.formatOrder(), that.options.formatCancel(), that.options.formatSort() ) $('body').append($(sModal)) that.$sortModal = $(_id) const $rows = that.$sortModal.find('tbody > tr') that.$sortModal.off('click', '#add').on('click', '#add', () => { const total = that.$sortModal.find('.multi-sort-name:first option').length let current = that.$sortModal.find('tbody tr').length if (current < total) { current++ that.addLevel() that.setButtonStates() } }) that.$sortModal.off('click', '#delete').on('click', '#delete', () => { const total = that.$sortModal.find('.multi-sort-name:first option').length let current = that.$sortModal.find('tbody tr').length if (current > 1 && current <= total) { current-- that.$sortModal.find('tbody tr:last').remove() that.setButtonStates() } }) that.$sortModal.off('click', '.multi-sort-order-button').on('click', '.multi-sort-order-button', () => { const $rows = that.$sortModal.find('tbody > tr') let $alert = that.$sortModal.find('div.alert') const fields = [] const results = [] that.options.sortPriority = $.map($rows, row => { const $row = $(row) const name = $row.find('.multi-sort-name').val() const order = $row.find('.multi-sort-order').val() fields.push(name) return { sortName: name, sortOrder: order } }) const sorted_fields = fields.sort() for (let i = 0; i < fields.length - 1; i++) { if (sorted_fields[i + 1] === sorted_fields[i]) { results.push(sorted_fields[i]) } } if (results.length > 0) { if ($alert.length === 0) { $alert = `` $($alert).insertBefore(that.$sortModal.find('.bars')) } } else { if ($alert.length === 1) { $($alert).remove() } if ($.inArray($.fn.bootstrapTable.theme, ['bootstrap3', 'bootstrap4']) !== -1) { that.$sortModal.modal('hide') } that.options.sortName = '' if (that.options.sidePagination === 'server') { const t = that.options.queryParams that.options.queryParams = params => { params.multiSort = that.options.sortPriority return $.fn.bootstrapTable.utils.calculateObjectValue(that.options, t, [params]) } isSingleSort = false that.initServer(that.options.silentSort) return } that.onMultipleSort() } }) if (that.options.sortPriority === null || that.options.sortPriority.length === 0) { if (that.options.sortName) { that.options.sortPriority = [{ sortName: that.options.sortName, sortOrder: that.options.sortOrder }] } } if (that.options.sortPriority !== null && that.options.sortPriority.length > 0) { if ($rows.length < that.options.sortPriority.length && typeof that.options.sortPriority === 'object') { for (let i = 0; i < that.options.sortPriority.length; i++) { that.addLevel(i, that.options.sortPriority[i]) } } } else { that.addLevel(0) } that.setButtonStates() } } $.fn.bootstrapTable.methods.push('multipleSort') $.extend($.fn.bootstrapTable.defaults, { showMultiSort: false, showMultiSortButton: true, multiSortStrictSort: false, sortPriority: null, onMultipleSort () { return false } }) $.extend($.fn.bootstrapTable.Constructor.EVENTS, { 'multiple-sort.bs.table': 'onMultipleSort' }) $.extend($.fn.bootstrapTable.locales, { formatMultipleSort () { return 'Multiple Sort' }, formatAddLevel () { return 'Add Level' }, formatDeleteLevel () { return 'Delete Level' }, formatColumn () { return 'Column' }, formatOrder () { return 'Order' }, formatSortBy () { return 'Sort by' }, formatThenBy () { return 'Then by' }, formatSort () { return 'Sort' }, formatCancel () { return 'Cancel' }, formatDuplicateAlertTitle () { return 'Duplicate(s) detected!' }, formatDuplicateAlertDescription () { return 'Please remove or change any duplicate column.' }, formatSortOrders () { return { asc: 'Ascending', desc: 'Descending' } } }) $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales) const BootstrapTable = $.fn.bootstrapTable.Constructor const _initToolbar = BootstrapTable.prototype.initToolbar const _destroy = BootstrapTable.prototype.destroy BootstrapTable.prototype.initToolbar = function (...args) { this.showToolbar = this.showToolbar || this.options.showMultiSort const that = this const sortModalSelector = `sortModal_${this.$el.attr('id')}` const sortModalId = `#${sortModalSelector}` this.$sortModal = $(sortModalId) this.sortModalSelector = sortModalSelector if (that.options.sortPriority !== null) { that.onMultipleSort() } _initToolbar.apply(this, Array.prototype.slice.apply(args)) if (that.options.sidePagination === 'server' && !isSingleSort && that.options.sortPriority !== null) { const t = that.options.queryParams that.options.queryParams = params => { params.multiSort = that.options.sortPriority return t(params) } } if (this.options.showMultiSort) { const $btnGroup = this.$toolbar.find('>.' + that.constants.classes.buttonsGroup.split(' ').join('.')).first() let $multiSortBtn = this.$toolbar.find('div.multi-sort') const o = that.options if (!$multiSortBtn.length && this.options.showMultiSortButton) { $multiSortBtn = Utils.sprintf(that.constants.html.multipleSortButton, that.sortModalSelector, this.options.formatMultipleSort(), Utils.sprintf(that.constants.html.icon, o.iconsPrefix, o.icons.sort)) $btnGroup.append($multiSortBtn) if ($.fn.bootstrapTable.theme === 'semantic') { this.$toolbar.find('.multi-sort').on('click', () => { $(sortModalId).modal('show') }) } else if ($.fn.bootstrapTable.theme === 'materialize') { this.$toolbar.find('.multi-sort').on('click', () => { $(sortModalId).modal() }) } else if ($.fn.bootstrapTable.theme === 'foundation') { this.$toolbar.find('.multi-sort').on('click', () => { if (!this.foundationModal) { // eslint-disable-next-line no-undef this.foundationModal = new Foundation.Reveal($(sortModalId)) } this.foundationModal.open() }) } else if ($.fn.bootstrapTable.theme === 'bulma') { this.$toolbar.find('.multi-sort').on('click', () => { $('html').toggleClass('is-clipped') $(sortModalId).toggleClass('is-active') $('button[data-close]').one('click', () => { $('html').toggleClass('is-clipped') $(sortModalId).toggleClass('is-active') }) }) } showSortModal(that) } this.$el.on('sort.bs.table', () => { isSingleSort = true }) this.$el.on('multiple-sort.bs.table', () => { isSingleSort = false }) this.$el.on('load-success.bs.table', () => { if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object' && that.options.sidePagination !== 'server') { that.onMultipleSort() } }) this.$el.on('column-switch.bs.table', (field, checked) => { for (let i = 0; i < that.options.sortPriority.length; i++) { if (that.options.sortPriority[i].sortName === checked) { that.options.sortPriority.splice(i, 1) } } that.assignSortableArrows() that.$sortModal.remove() showSortModal(that) }) this.$el.on('reset-view.bs.table', () => { if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object') { that.assignSortableArrows() } }) } } BootstrapTable.prototype.destroy = function (...args) { _destroy.apply(this, Array.prototype.slice.apply(args)) if (this.options.showMultiSort) { this.$sortModal.remove() } } BootstrapTable.prototype.multipleSort = function () { const that = this if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object' && that.options.sidePagination !== 'server') { that.onMultipleSort() } } BootstrapTable.prototype.onMultipleSort = function () { const that = this const cmp = (x, y) => x > y ? 1 : x < y ? -1 : 0 const arrayCmp = (a, b) => { const arr1 = [] const arr2 = [] for (let i = 0; i < that.options.sortPriority.length; i++) { const order = that.options.sortPriority[i].sortOrder === 'desc' ? -1 : 1 let aa = a[that.options.sortPriority[i].sortName] let bb = b[that.options.sortPriority[i].sortName] if (aa === undefined || aa === null) { aa = '' } if (bb === undefined || bb === null) { bb = '' } if ($.isNumeric(aa) && $.isNumeric(bb)) { aa = parseFloat(aa) bb = parseFloat(bb) } else { aa = aa.toString() bb = bb.toString() if (that.options.multiSortStrictSort) { aa = aa.toLowerCase() bb = bb.toLowerCase() } } arr1.push( order * cmp(aa, bb)) arr2.push( order * cmp(bb, aa)) } return cmp(arr1, arr2) } this.data.sort((a, b) => arrayCmp(a, b)) this.initBody() this.assignSortableArrows() this.trigger('multiple-sort') } BootstrapTable.prototype.addLevel = function (index, sortPriority) { const text = index === 0 ? this.options.formatSortBy() : this.options.formatThenBy() this.$sortModal.find('tbody') .append($('') .append($('').text(text)) .append($('').append($(Utils.sprintf(this.constants.html.multipleSortSelect, this.constants.classes.paginationDropdown, 'multi-sort-name')))) .append($('').append($(Utils.sprintf(this.constants.html.multipleSortSelect, this.constants.classes.paginationDropdown, 'multi-sort-order')))) ) const $multiSortName = this.$sortModal.find('.multi-sort-name').last() const $multiSortOrder = this.$sortModal.find('.multi-sort-order').last() $.each(this.columns, (i, column) => { if (column.sortable === false || column.visible === false) { return true } $multiSortName.append(``) }) $.each(this.options.formatSortOrders(), (value, order) => { $multiSortOrder.append(``) }) if (sortPriority !== undefined) { $multiSortName.find(`option[value="${sortPriority.sortName}"]`).attr('selected', true) $multiSortOrder.find(`option[value="${sortPriority.sortOrder}"]`).attr('selected', true) } } BootstrapTable.prototype.assignSortableArrows = function () { const that = this const headers = that.$header.find('th') for (let i = 0; i < headers.length; i++) { for (let c = 0; c < that.options.sortPriority.length; c++) { if ($(headers[i]).data('field') === that.options.sortPriority[c].sortName) { $(headers[i]).find('.sortable').removeClass('desc asc').addClass(that.options.sortPriority[c].sortOrder) } } } } BootstrapTable.prototype.setButtonStates = function () { const total = this.$sortModal.find('.multi-sort-name:first option').length const current = this.$sortModal.find('tbody tr').length if (current === total) { this.$sortModal.find('#add').attr('disabled', 'disabled') } if (current > 1) { this.$sortModal.find('#delete').removeAttr('disabled') } if (current < total) { this.$sortModal.find('#add').removeAttr('disabled') } if (current === 1) { this.$sortModal.find('#delete').attr('disabled', 'disabled') } }