| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 |
- /**
- * @author: Dennis Hernández
- * @webSite: http://djhvscf.github.io/Blog
- * @version: v3.0.0
- */
- import * as UtilsFilterControl from './utils.js'
- const Utils = $.fn.bootstrapTable.utils
- $.extend($.fn.bootstrapTable.defaults, {
- filterControl: false,
- filterControlVisible: true,
- filterControlMultipleSearch: false,
- filterControlMultipleSearchDelimiter: ',',
- // eslint-disable-next-line no-unused-vars
- onColumnSearch (field, text) {
- return false
- },
- onCreatedControls () {
- return false
- },
- alignmentSelectControlOptions: undefined,
- filterTemplate: {
- input (that, column, placeholder, value) {
- return Utils.sprintf(
- '<input type="search" class="%s bootstrap-table-filter-control-%s search-input" style="width: 100%;" placeholder="%s" value="%s">',
- UtilsFilterControl.getInputClass(that),
- column.field,
- 'undefined' === typeof placeholder ? '' : placeholder,
- 'undefined' === typeof value ? '' : value
- )
- },
- select (that, column) {
- return Utils.sprintf(
- '<select class="%s bootstrap-table-filter-control-%s %s" %s style="width: 100%;" dir="%s"></select>',
- UtilsFilterControl.getInputClass(that, true),
- column.field,
- '',
- '',
- UtilsFilterControl.getDirectionOfSelectOptions(
- that.options.alignmentSelectControlOptions
- )
- )
- },
- datepicker (that, column, value) {
- return Utils.sprintf(
- '<input type="date" class="%s date-filter-control bootstrap-table-filter-control-%s" style="width: 100%;" value="%s">',
- UtilsFilterControl.getInputClass(that),
- column.field,
- 'undefined' === typeof value ? '' : value
- )
- }
- },
- searchOnEnterKey: false,
- showFilterControlSwitch: false,
- sortSelectOptions: false,
- // internal variables
- _valuesFilterControl: [],
- _initialized: false,
- _isRendering: false,
- _usingMultipleSelect: false
- })
- $.extend($.fn.bootstrapTable.columnDefaults, {
- filterControl: undefined, // input, select, datepicker
- filterControlMultipleSelect: false,
- filterControlMultipleSelectOptions: {},
- filterDataCollector: undefined,
- filterData: undefined,
- filterDatepickerOptions: {},
- filterStrictSearch: false,
- filterStartsWithSearch: false,
- filterControlPlaceholder: '',
- filterDefault: '',
- filterOrderBy: 'asc', // asc || desc
- filterCustomSearch: undefined
- })
- $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
- 'column-search.bs.table': 'onColumnSearch',
- 'created-controls.bs.table': 'onCreatedControls'
- })
- $.extend($.fn.bootstrapTable.defaults.icons, {
- filterControlSwitchHide: {
- bootstrap3: 'glyphicon-zoom-out icon-zoom-out',
- bootstrap5: 'bi-zoom-out',
- materialize: 'zoom_out'
- }[$.fn.bootstrapTable.theme] || 'fa-search-minus',
- filterControlSwitchShow: {
- bootstrap3: 'glyphicon-zoom-in icon-zoom-in',
- bootstrap5: 'bi-zoom-in',
- materialize: 'zoom_in'
- }[$.fn.bootstrapTable.theme] || 'fa-search-plus'
- })
- $.extend($.fn.bootstrapTable.locales, {
- formatFilterControlSwitch () {
- return 'Hide/Show controls'
- },
- formatFilterControlSwitchHide () {
- return 'Hide controls'
- },
- formatFilterControlSwitchShow () {
- return 'Show controls'
- }
- })
- $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
- $.extend($.fn.bootstrapTable.defaults, {
- formatClearSearch () {
- return 'Clear filters'
- }
- })
- $.fn.bootstrapTable.methods.push('triggerSearch')
- $.fn.bootstrapTable.methods.push('clearFilterControl')
- $.fn.bootstrapTable.methods.push('toggleFilterControl')
- $.BootstrapTable = class extends $.BootstrapTable {
- init () {
- // Make sure that the filterControl option is set
- if (this.options.filterControl) {
- // Make sure that the internal variables are set correctly
- this._valuesFilterControl = []
- this._initialized = false
- this._usingMultipleSelect = false
- this._isRendering = false
- this.$el
- .on('reset-view.bs.table', Utils.debounce(() => {
- UtilsFilterControl.initFilterSelectControls(this)
- UtilsFilterControl.setValues(this)
- }, 3))
- .on('toggle.bs.table', Utils.debounce((_, cardView) => {
- this._initialized = false
- if (!cardView) {
- UtilsFilterControl.initFilterSelectControls(this)
- UtilsFilterControl.setValues(this)
- this._initialized = true
- }
- }, 1))
- .on('post-header.bs.table', Utils.debounce(() => {
- UtilsFilterControl.initFilterSelectControls(this)
- UtilsFilterControl.setValues(this)
- }, 3))
- .on('column-switch.bs.table', Utils.debounce(() => {
- UtilsFilterControl.setValues(this)
- if (this.options.height) {
- this.fitHeader()
- }
- }, 1))
- .on('post-body.bs.table', Utils.debounce(() => {
- if (this.options.height && !this.options.filterControlContainer && this.options.filterControlVisible) {
- UtilsFilterControl.fixHeaderCSS(this)
- }
- this.$tableLoading.css('top', this.$header.outerHeight() + 1)
- }, 1))
- .on('all.bs.table', () => {
- UtilsFilterControl.syncHeaders(this)
- })
- }
- super.init()
- }
- initBody () {
- super.initBody()
- if (!this.options.filterControl) {
- return
- }
- Utils.debounce(() => {
- UtilsFilterControl.initFilterSelectControls(this)
- UtilsFilterControl.setValues(this)
- }, 3)()
- }
- load (data) {
- super.load(data)
- if (!this.options.filterControl) {
- return
- }
- UtilsFilterControl.createControls(this, UtilsFilterControl.getControlContainer(this))
- UtilsFilterControl.setValues(this)
- }
- initHeader () {
- super.initHeader()
- if (!this.options.filterControl) {
- return
- }
- UtilsFilterControl.createControls(this, UtilsFilterControl.getControlContainer(this))
- this._initialized = true
- }
- initSearch () {
- const that = this
- const filterPartial = $.isEmptyObject(that.filterColumnsPartial) ? null : that.filterColumnsPartial
- super.initSearch()
- if (this.options.sidePagination === 'server' || filterPartial === null) {
- return
- }
- // Check partial column filter
- that.data = filterPartial ?
- that.data.filter((item, i) => {
- const itemIsExpected = []
- const keys1 = Object.keys(item)
- const keys2 = Object.keys(filterPartial)
- const keys = keys1.concat(keys2.filter(item => !keys1.includes(item)))
- keys.forEach(key => {
- const thisColumn = that.columns[that.fieldsColumnsIndex[key]]
- const rawFilterValue = (filterPartial[key] || '')
- let filterValue = rawFilterValue.toLowerCase()
- let value = Utils.unescapeHTML(Utils.getItemField(item, key, false))
- let tmpItemIsExpected
- if (this.options.searchAccentNeutralise) {
- filterValue = Utils.normalizeAccent(filterValue)
- }
- let filterValues = [filterValue]
- if (
- this.options.filterControlMultipleSearch
- ) {
- filterValues = filterValue.split(this.options.filterControlMultipleSearchDelimiter)
- }
- filterValues.forEach(filterValue => {
- if (tmpItemIsExpected === true) {
- return
- }
- filterValue = filterValue.trim()
- if (filterValue === '') {
- tmpItemIsExpected = true
- } else {
- // Fix #142: search use formatted data
- if (thisColumn) {
- if (thisColumn.searchFormatter || thisColumn._forceFormatter) {
- value = $.fn.bootstrapTable.utils.calculateObjectValue(
- that.header,
- that.header.formatters[$.inArray(key, that.header.fields)],
- [value, item, i],
- value
- )
- }
- }
- if ($.inArray(key, that.header.fields) !== -1) {
- if (value === undefined || value === null) {
- tmpItemIsExpected = false
- } else if (typeof value === 'object' && thisColumn.filterCustomSearch) {
- itemIsExpected.push(that.isValueExpected(rawFilterValue, value, thisColumn, key))
- } else if (typeof value === 'object' && Array.isArray(value)) {
- value.forEach(objectValue => {
- if (tmpItemIsExpected) {
- return
- }
- tmpItemIsExpected = that.isValueExpected(filterValue, objectValue, thisColumn, key)
- })
- } else if (typeof value === 'object' && !Array.isArray(value)) {
- Object.values(value).forEach(objectValue => {
- if (tmpItemIsExpected) {
- return
- }
- tmpItemIsExpected = that.isValueExpected(filterValue, objectValue, thisColumn, key)
- })
- } else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
- tmpItemIsExpected = that.isValueExpected(filterValue, value, thisColumn, key)
- }
- }
- }
- })
- itemIsExpected.push(tmpItemIsExpected)
- })
- return !itemIsExpected.includes(false)
- }) :
- that.data
- that.unsortedData = [...that.data]
- }
- isValueExpected (searchValue, value, column, key) {
- let tmpItemIsExpected = false
- if (
- column.filterStrictSearch ||
- (column.filterControl === 'select' && column.passed.filterStrictSearch !== false)
- ) {
- tmpItemIsExpected = value.toString().toLowerCase() === searchValue.toString().toLowerCase()
- } else if (column.filterStartsWithSearch) {
- tmpItemIsExpected = (`${value}`).toLowerCase().indexOf(searchValue) === 0
- } else if (column.filterControl === 'datepicker') {
- tmpItemIsExpected = new Date(value).getTime() === new Date(searchValue).getTime()
- } else if (this.options.regexSearch) {
- tmpItemIsExpected = Utils.regexCompare(value, searchValue)
- } else {
- tmpItemIsExpected = (`${value}`).toLowerCase().includes(searchValue)
- }
- const largerSmallerEqualsRegex = /(?:(<=|=>|=<|>=|>|<)(?:\s+)?(\d+)?|(\d+)?(\s+)?(<=|=>|=<|>=|>|<))/gm
- const matches = largerSmallerEqualsRegex.exec(searchValue)
- if (matches) {
- const operator = matches[1] || `${matches[5]}l`
- const comparisonValue = matches[2] || matches[3]
- const int = parseInt(value, 10)
- const comparisonInt = parseInt(comparisonValue, 10)
- switch (operator) {
- case '>':
- case '<l':
- tmpItemIsExpected = int > comparisonInt
- break
- case '<':
- case '>l':
- tmpItemIsExpected = int < comparisonInt
- break
- case '<=':
- case '=<':
- case '>=l':
- case '=>l':
- tmpItemIsExpected = int <= comparisonInt
- break
- case '>=':
- case '=>':
- case '<=l':
- case '=<l':
- tmpItemIsExpected = int >= comparisonInt
- break
- default:
- break
- }
- }
- if (column.filterCustomSearch) {
- const customSearchResult = Utils.calculateObjectValue(this, column.filterCustomSearch, [searchValue, value, key, this.options.data], true)
- if (customSearchResult !== null) {
- tmpItemIsExpected = customSearchResult
- }
- }
- return tmpItemIsExpected
- }
- initColumnSearch (filterColumnsDefaults) {
- UtilsFilterControl.cacheValues(this)
- if (filterColumnsDefaults) {
- this.filterColumnsPartial = filterColumnsDefaults
- this.updatePagination()
- // eslint-disable-next-line guard-for-in
- for (const filter in filterColumnsDefaults) {
- this.trigger('column-search', filter, filterColumnsDefaults[filter])
- }
- }
- }
- initToolbar () {
- this.showToolbar = this.showToolbar || this.options.showFilterControlSwitch
- this.showSearchClearButton = this.options.filterControl && this.options.showSearchClearButton
- if (this.options.showFilterControlSwitch) {
- this.buttons = Object.assign(this.buttons, {
- filterControlSwitch: {
- text: this.options.filterControlVisible ? this.options.formatFilterControlSwitchHide() : this.options.formatFilterControlSwitchShow(),
- icon: this.options.filterControlVisible ? this.options.icons.filterControlSwitchHide : this.options.icons.filterControlSwitchShow,
- event: this.toggleFilterControl,
- attributes: {
- 'aria-label': this.options.formatFilterControlSwitch(),
- title: this.options.formatFilterControlSwitch()
- }
- }
- })
- }
- super.initToolbar()
- }
- resetSearch (text) {
- if (this.options.filterControl && this.options.showSearchClearButton) {
- this.clearFilterControl()
- }
- super.resetSearch(text)
- }
- clearFilterControl () {
- if (!this.options.filterControl) {
- return
- }
- const that = this
- const table = this.$el.closest('table')
- const cookies = UtilsFilterControl.collectBootstrapTableFilterCookies()
- const controls = UtilsFilterControl.getSearchControls(that)
- // const search = Utils.getSearchInput(this)
- let hasValues = false
- // Clear cache values
- $.each(that._valuesFilterControl, (i, item) => {
- hasValues = hasValues ? true : item.value !== ''
- item.value = ''
- })
- // Clear controls in UI
- $.each(controls, (i, item) => {
- item.value = ''
- })
- // Cache controls again
- UtilsFilterControl.setValues(that)
- // clear cookies once the filters are clean
- Utils.debounce(() => {
- if (cookies && cookies.length > 0) {
- $.each(cookies, (i, item) => {
- if (that.deleteCookie !== undefined) {
- that.deleteCookie(item)
- }
- })
- }
- }, that.options.searchTimeOut)()
- // If there is not any value in the controls exit this method
- if (!hasValues) {
- return
- }
- // Clear each type of filter if it exists.
- // Requires the body to reload each time a type of filter is found because we never know
- // which ones are going to be present.
- if (controls.length > 0) {
- this.filterColumnsPartial = {}
- controls.eq(0).trigger(this.tagName === 'INPUT' ? 'keyup' : 'change', { keyCode: 13 })
- /* controls.each(function () {
- $(this).trigger(this.tagName === 'INPUT' ? 'keyup' : 'change', { keyCode: 13 })
- })*/
- } else {
- return
- }
- /* if (search.length > 0) {
- that.resetSearch('fc')
- }*/
- // use the default sort order if it exists. do nothing if it does not
- if (that.options.sortName !== table.data('sortName') || that.options.sortOrder !== table.data('sortOrder')) {
- const sorter = this.$header.find(Utils.sprintf('[data-field="%s"]', $(controls[0]).closest('table').data('sortName')))
- if (sorter.length > 0) {
- that.onSort({ type: 'keypress', currentTarget: sorter })
- $(sorter).find('.sortable').trigger('click')
- }
- }
- }
- // EVENTS
- onColumnSearch ({ currentTarget, keyCode }) {
- if (UtilsFilterControl.isKeyAllowed(keyCode)) {
- return
- }
- UtilsFilterControl.cacheValues(this)
- // Cookie extension support
- if (!this.options.cookie) {
- this.options.pageNumber = 1
- } else {
- // Force call the initServer method in Cookie extension
- this._filterControlValuesLoaded = true
- }
- if ($.isEmptyObject(this.filterColumnsPartial)) {
- this.filterColumnsPartial = {}
- }
- // If searchOnEnterKey is set to true, then we need to iterate over all controls and grab their values.
- const controls = this.options.searchOnEnterKey ? UtilsFilterControl.getSearchControls(this).toArray() : [currentTarget]
- controls.forEach(element => {
- const $element = $(element)
- const elementValue = $element.val()
- const text = elementValue ? elementValue.trim() : ''
- const $field = $element.closest('[data-field]').data('field')
- this.trigger('column-search', $field, text)
- if (text) {
- this.filterColumnsPartial[$field] = text
- } else {
- delete this.filterColumnsPartial[$field]
- }
- })
- this.onSearch({ currentTarget }, false)
- }
- toggleFilterControl () {
- this.options.filterControlVisible = !this.options.filterControlVisible
- // Controls in original header or container.
- const $filterControls = UtilsFilterControl.getControlContainer(this).find('.filter-control, .no-filter-control')
- if (this.options.filterControlVisible) {
- $filterControls.show()
- } else {
- $filterControls.hide()
- this.clearFilterControl()
- }
- // Controls in fixed header
- if (this.options.height) {
- const $fixedControls = $('.fixed-table-header table thead').find('.filter-control, .no-filter-control')
- $fixedControls.toggle(this.options.filterControlVisible)
- UtilsFilterControl.fixHeaderCSS(this)
- }
- const icon = this.options.showButtonIcons ? this.options.filterControlVisible ? this.options.icons.filterControlSwitchHide : this.options.icons.filterControlSwitchShow : ''
- const text = this.options.showButtonText ? this.options.filterControlVisible ? this.options.formatFilterControlSwitchHide() : this.options.formatFilterControlSwitchShow() : ''
- this.$toolbar.find('>.columns').find('.filter-control-switch')
- .html(`${Utils.sprintf(this.constants.html.icon, this.options.iconsPrefix, icon) } ${ text}`)
- }
- triggerSearch () {
- const searchControls = UtilsFilterControl.getSearchControls(this)
- searchControls.each(function () {
- const $element = $(this)
- if ($element.is('select')) {
- $element.trigger('change')
- } else {
- $element.trigger('keyup')
- }
- })
- }
- _toggleColumn (index, checked, needUpdate) {
- this._initialized = false
- super._toggleColumn(index, checked, needUpdate)
- UtilsFilterControl.syncHeaders(this)
- }
- }
|