UNPKG

bootstrap-table

Version:

An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)

451 lines (380 loc) • 16 kB
import Utils from '../utils/index.js' export default { renderButton (buttonName, buttonConfig) { const opts = this.options let buttonHtml if (buttonConfig.hasOwnProperty('html')) { if (typeof buttonConfig.html === 'function') { buttonHtml = buttonConfig.html() } else { buttonHtml = buttonConfig.html } } else { let buttonClass = this.constants.buttonsClass if (buttonConfig.hasOwnProperty('attributes') && buttonConfig.attributes.class) { buttonClass += ` ${buttonConfig.attributes.class}` } buttonHtml = `<button class="${buttonClass}" type="button" name="${buttonName}"` if (buttonConfig.hasOwnProperty('attributes')) { for (const [attributeName, value] of Object.entries(buttonConfig.attributes)) { if (attributeName === 'class') { continue } const attribute = attributeName === 'title' ? opts.buttonsAttributeTitle : attributeName buttonHtml += ` ${attribute}="${value}"` } } buttonHtml += '>' if (opts.showButtonIcons && buttonConfig.hasOwnProperty('icon')) { buttonHtml += `${Utils.sprintf(this.constants.html.icon, opts.iconsPrefix, buttonConfig.icon)} ` } if (opts.showButtonText && buttonConfig.hasOwnProperty('text')) { buttonHtml += buttonConfig.text } buttonHtml += '</button>' } return buttonHtml }, initToolbar () { const opts = this.options let html let timeoutId let $keepOpen let switchableCount = 0 if (this.$toolbar.find('.bs-bars').children().length) { $('body').append($(opts.toolbar)) } this.$toolbar.html('') if (typeof opts.toolbar === 'string' || typeof opts.toolbar === 'object') { $(Utils.sprintf('<div class="bs-bars %s-%s"></div>', this.constants.classes.pull, opts.toolbarAlign)) .appendTo(this.$toolbar) .append($(opts.toolbar)) } // showColumns, showToggle, showRefresh html = [`<div class="${[ 'columns', `columns-${opts.buttonsAlign}`, this.constants.classes.buttonsGroup, `${this.constants.classes.pull}-${opts.buttonsAlign}` ].join(' ')}">`] if (typeof opts.buttonsOrder === 'string') { opts.buttonsOrder = Utils.parseStringArray(opts.buttonsOrder) } this.buttons = Object.assign(this.buttons, { paginationSwitch: { text: opts.pagination ? opts.formatPaginationSwitchUp() : opts.formatPaginationSwitchDown(), icon: opts.pagination ? opts.icons.paginationSwitchDown : opts.icons.paginationSwitchUp, render: false, event: this.togglePagination, attributes: { 'aria-label': opts.formatPaginationSwitch(), title: opts.formatPaginationSwitch() } }, refresh: { text: opts.formatRefresh(), icon: opts.icons.refresh, render: false, event: this.refresh, attributes: { 'aria-label': opts.formatRefresh(), title: opts.formatRefresh() } }, toggle: { text: opts.formatToggleOn(), icon: opts.icons.toggleOff, render: false, event: this.toggleView, attributes: { 'aria-label': opts.formatToggleOn(), title: opts.formatToggleOn() } }, fullscreen: { text: opts.formatFullscreen(), icon: opts.icons.fullscreen, render: false, event: this.toggleFullscreen, attributes: { 'aria-label': opts.formatFullscreen(), title: opts.formatFullscreen() } }, columns: { render: false, html: () => { const html = [] html.push(`<div class="keep-open ${this.constants.classes.buttonsDropdown}"> <button class="${this.constants.buttonsClass} dropdown-toggle" type="button" ${this.constants.dataToggle}="dropdown" aria-label="${opts.formatColumns()}" ${opts.buttonsAttributeTitle}="${opts.formatColumns()}"> ${opts.showButtonIcons ? Utils.sprintf(this.constants.html.icon, opts.iconsPrefix, opts.icons.columns) : ''} ${opts.showButtonText ? opts.formatColumns() : ''} ${this.constants.html.dropdownCaret} </button> ${this.constants.html.toolbarDropdown[0]}`) if (opts.showColumnsSearch) { html.push( Utils.sprintf(this.constants.html.toolbarDropdownItem, Utils.sprintf('<input type="text" class="%s" name="columnsSearch" placeholder="%s" autocomplete="off">', this.constants.classes.input, opts.formatSearch()) ) ) html.push(this.constants.html.toolbarDropdownSeparator) } if (opts.showColumnsToggleAll) { const allFieldsVisible = this.getVisibleColumns().length === this.columns.filter(column => !this.isSelectionColumn(column)).length html.push(Utils.getCheckboxHtml({ name: 'toggle-all', checked: allFieldsVisible, label: opts.formatColumnsToggleAll(), extraClass: 'toggle-all', centered: false, withLabel: true })) html.push(this.constants.html.toolbarDropdownSeparator) } let visibleColumns = 0 this.columns.forEach(column => { if (column.visible) { visibleColumns++ } }) this.columns.forEach((column, i) => { if (this.isSelectionColumn(column)) { return } if (opts.cardView && !column.cardVisible) { return } const checked = column.visible ? ' checked="checked"' : '' const disabled = visibleColumns <= opts.minimumCountColumns && checked ? ' disabled="disabled"' : '' if (column.switchable) { const checkboxHtml = Utils.getDropdownColumnCheckboxHtml({ dataField: column.field, value: i, checked: !!checked, disabled: !!disabled, label: column.switchableLabel || (opts.escape && opts.escapeTitle ? Utils.escapeHTML(column.title) : column.title) }) // Bootstrap 3/4 needs to be wrapped with toolbarDropdownItem if (Utils.getBootstrapVersion() === 5) { html.push(checkboxHtml) } else { html.push(Utils.sprintf(this.constants.html.toolbarDropdownItem, checkboxHtml)) } switchableCount++ } }) html.push(this.constants.html.toolbarDropdown[1], '</div>') return html.join('') } } }) const buttonsHtml = {} for (const [buttonName, buttonConfig] of Object.entries(this.buttons)) { buttonsHtml[buttonName] = this.renderButton(buttonName, buttonConfig) const optionName = `show${buttonName.charAt(0).toUpperCase()}${buttonName.substring(1)}` const showOption = opts[optionName] if (( !buttonConfig.hasOwnProperty('render') || buttonConfig.hasOwnProperty('render') && buttonConfig.render) && (showOption === undefined || showOption === true) ) { opts[optionName] = true } if (!opts.buttonsOrder.includes(buttonName)) { opts.buttonsOrder.push(buttonName) } } // Adding the button html to the final toolbar html when the showOption is true for (const button of opts.buttonsOrder) { const showOption = opts[`show${button.charAt(0).toUpperCase()}${button.substring(1)}`] if (showOption) { html.push(buttonsHtml[button]) } } html.push('</div>') // Fix #188: this.showToolbar is for extensions if (this.showToolbar || html.length > 2) { if (html.some(item => Utils.isDomNode(item))) { // When there are DOM nodes, build the structure manually // Build wrapper element once from the opening div string const wrapper = $(html[0]) // Skip html[0] (opening div) and html[html.length-1] (closing div) // Append each button item into the wrapper for (const item of html.slice(1, -1)) { wrapper.append(...Utils.htmlToNodes(item)) } this.$toolbar.append(wrapper) } else { this.$toolbar.append(html.join('')) } } for (const [buttonName, buttonConfig] of Object.entries(this.buttons)) { if (buttonConfig.hasOwnProperty('event')) { if (typeof buttonConfig.event === 'function' || typeof buttonConfig.event === 'string') { const event = typeof buttonConfig.event === 'string' ? window[buttonConfig.event] : buttonConfig.event this.$toolbar.find(`button[name="${buttonName}"]`) .off('click') .on('click', () => event.call(this)) continue } for (const [eventType, eventFunction] of Object.entries(buttonConfig.event)) { const event = typeof eventFunction === 'string' ? window[eventFunction] : eventFunction this.$toolbar.find(`button[name="${buttonName}"]`) .off(eventType) .on(eventType, () => event.call(this)) } } } if (opts.showColumns) { $keepOpen = this.$toolbar.find('.keep-open') const $checkboxes = $keepOpen.find('input[type="checkbox"]:not(".toggle-all")') const $toggleAll = $keepOpen.find('input[type="checkbox"].toggle-all') if (switchableCount <= opts.minimumCountColumns) { $keepOpen.find('input').prop('disabled', true) } $keepOpen.find('li, label').off('click').on('click', e => { e.stopImmediatePropagation() }) $checkboxes.off('click').on('click', ({ currentTarget }) => { const $this = $(currentTarget) this._toggleColumns([$this.data('field')], $this.prop('checked'), false) this.trigger('column-switch', $this.data('field'), $this.prop('checked')) $toggleAll.prop('checked', $checkboxes.filter(':checked').length === this.columns.filter(column => !this.isSelectionColumn(column)).length) }) $toggleAll.off('click').on('click', ({ currentTarget }) => { this._toggleAllColumns($(currentTarget).prop('checked')) this.trigger('column-switch-all', $(currentTarget).prop('checked')) }) if (opts.showColumnsSearch) { const $columnsSearch = $keepOpen.find('[name="columnsSearch"]') const $listItems = $keepOpen.find('.dropdown-item-marker') $columnsSearch.on('keyup paste change', ({ currentTarget }) => { const $this = $(currentTarget) const searchValue = $this.val().toLowerCase() $listItems.each((_, el) => { el.style.removeProperty('display') }) $checkboxes.each((i, el) => { const $checkbox = $(el) const $listItem = $checkbox.parents('.dropdown-item-marker') const text = $listItem.text().toLowerCase() if (!text.includes(searchValue)) { $listItem.each((_, item) => { item.style.setProperty('display', 'none', 'important') }) } }) }) } } const handleInputEvent = $searchInput => { const eventTriggers = $searchInput.is('select') ? 'change' : 'keyup drop blur mouseup' $searchInput.off(eventTriggers).on(eventTriggers, event => { if (opts.searchOnEnterKey && event.keyCode !== 13) { return } if ([37, 38, 39, 40].includes(event.keyCode)) { return } clearTimeout(timeoutId) // doesn't matter if it's 0 timeoutId = setTimeout(() => { this.onSearch({ currentTarget: event.currentTarget }) }, opts.searchTimeOut) }) } // Fix #4516: this.showSearchClearButton is for extensions if ( (opts.search || this.showSearchClearButton) && typeof opts.searchSelector !== 'string' ) { html = [] const showSearchButton = Utils.sprintf(this.constants.html.searchButton, this.constants.buttonsClass, opts.formatSearch(), opts.showButtonIcons ? Utils.sprintf(this.constants.html.icon, opts.iconsPrefix, opts.icons.search) : '', opts.showButtonText ? opts.formatSearch() : '' ) const showSearchClearButton = Utils.sprintf(this.constants.html.searchClearButton, this.constants.buttonsClass, opts.formatClearSearch(), opts.showButtonIcons ? Utils.sprintf(this.constants.html.icon, opts.iconsPrefix, opts.icons.clearSearch) : '', opts.showButtonText ? opts.formatClearSearch() : '' ) const searchInputHtml = `<input class="${this.constants.classes.input} ${Utils.sprintf(' %s%s', this.constants.classes.inputPrefix, opts.iconSize)} search-input" type="search" aria-label="${opts.formatSearch()}" placeholder="${opts.formatSearch()}" autocomplete="off">` let searchInputFinalHtml = searchInputHtml if (opts.showSearchButton || opts.showSearchClearButton) { const buttonsHtml = (opts.showSearchButton ? showSearchButton : '') + (opts.showSearchClearButton ? showSearchClearButton : '') searchInputFinalHtml = opts.search ? Utils.sprintf(this.constants.html.inputGroup, searchInputHtml, buttonsHtml) : buttonsHtml } html.push(Utils.sprintf(` <div class="${this.constants.classes.pull}-${opts.searchAlign} search ${this.constants.classes.inputGroup}"> %s </div> `, searchInputFinalHtml)) this.$toolbar.append(html.join('')) const searchInput = Utils.getSearchInput(this) const $searchInput = $(searchInput) if (opts.showSearchButton) { this.$toolbar.find('.search button[name=search]').off('click').on('click', () => { clearTimeout(timeoutId) // doesn't matter if it's 0 timeoutId = setTimeout(() => { this.onSearch({ currentTarget: searchInput }) }, opts.searchTimeOut) }) if (opts.searchOnEnterKey) { handleInputEvent($searchInput) } } else { handleInputEvent($searchInput) } if (opts.showSearchClearButton) { this.$toolbar.find('.search button[name=clearSearch]').click(() => { this.resetSearch() }) } } else if (typeof opts.searchSelector === 'string') { handleInputEvent($(Utils.getSearchInput(this))) } }, refresh (params) { if (params && params.url) { this.options.url = params.url } if (params && params.pageNumber) { this.options.pageNumber = params.pageNumber } if (params && params.pageSize) { this.options.pageSize = params.pageSize } if (params && params.query) { this.options.url = Utils.addQueryToUrl(this.options.url, params.query) } this.trigger('refresh', this.initServer(params && params.silent)) }, toggleView () { this.options.cardView = !this.options.cardView this.initHeader() const icon = this.options.showButtonIcons ? this.options.cardView ? this.options.icons.toggleOn : this.options.icons.toggleOff : '' const text = this.options.cardView ? this.options.formatToggleOff() : this.options.formatToggleOn() this.$toolbar.find('button[name="toggle"]') .html(`${Utils.sprintf(this.constants.html.icon, this.options.iconsPrefix, icon)} ${this.options.showButtonText ? text : ''}`) .attr('aria-label', text) .attr(this.options.buttonsAttributeTitle, text) this.initBody() this.trigger('toggle', this.options.cardView) }, toggleFullscreen () { this.$el.closest('.bootstrap-table').toggleClass('fullscreen') this.resetView() } }