Browse Source

Re writing the extension

djhvscf 4 years ago
parent
commit
9abdb40194

+ 211 - 118
src/extensions/filter-control/bootstrap-table-filter-control.js

@@ -1,7 +1,7 @@
 /**
  * @author: Dennis Hernández
  * @webSite: http://djhvscf.github.io/Blog
- * @version: v2.3.0
+ * @version: v3.0.0
  */
 
 import * as UtilsFilterControl from './utils.js'
@@ -49,7 +49,8 @@ $.extend($.fn.bootstrapTable.defaults, {
   searchOnEnterKey: false,
   showFilterControlSwitch: false,
   // internal variables
-  valuesFilterControl: []
+  valuesFilterControl: [],
+  initialized: false
 })
 
 $.extend($.fn.bootstrapTable.columnDefaults, {
@@ -112,6 +113,7 @@ $.BootstrapTable = class extends $.BootstrapTable {
     if (this.options.filterControl) {
       // Make sure that the internal variables are set correctly
       this.options.valuesFilterControl = []
+      this.options.initialized = false
 
       this.$el
         .on('reset-view.bs.table', () => {
@@ -132,43 +134,128 @@ $.BootstrapTable = class extends $.BootstrapTable {
 
           UtilsFilterControl.createControls(this, $controlContainer)
         })
-        .on('post-header.bs.table', () => {
-          UtilsFilterControl.setValues(this)
-        })
         .on('post-body.bs.table', () => {
           if (this.options.height && !this.options.filterControlContainer) {
             UtilsFilterControl.fixHeaderCSS(this)
           }
           this.$tableLoading.css('top', this.$header.outerHeight() + 1)
         })
-        .on('column-switch.bs.table', () => {
-          UtilsFilterControl.setValues(this)
-        })
-        .on('load-success.bs.table', () => {
-          this.enableControls(true)
-        })
-        .on('load-error.bs.table', () => {
-          this.enableControls(true)
-        })
     }
 
     super.init()
   }
 
+  initBody () {
+    super.initBody()
+  }
+
   initHeader () {
     super.initHeader()
-
-    if (!this.options.filterControl || this.options.height) {
+    if (!this.options.filterControl) {
       return
     }
 
     UtilsFilterControl.createControls(this, UtilsFilterControl.getControlContainer(this))
   }
 
-  initBody () {
-    super.initBody()
-    UtilsFilterControl.syncControls(this)
-    UtilsFilterControl.initFilterSelectControls(this)
+  fitHeader () {
+    if (this.$el.is(':hidden')) {
+      this.timeoutId_ = setTimeout(() => this.fitHeader(), 100)
+      return
+    }
+
+    const fixedBody = this.$tableBody.get(0)
+    const scrollWidth = fixedBody.scrollWidth > fixedBody.clientWidth &&
+    fixedBody.scrollHeight > fixedBody.clientHeight + this.$header.outerHeight() ?
+      Utils.getScrollBarWidth() : 0
+
+    this.$el.css('margin-top', -this.$header.outerHeight())
+
+    const focused = $(':focus')
+
+    if (focused.length > 0) {
+      const $th = focused.parents('th')
+
+      if ($th.length > 0) {
+        const dataField = $th.attr('data-field')
+
+        if (dataField !== undefined) {
+          const $headerTh = this.$header.find(`[data-field='${dataField}']`)
+
+          if ($headerTh.length > 0) {
+            $headerTh.find(':input').addClass('focus-temp')
+          }
+        }
+      }
+    }
+
+    if (this.options.height && this.options.filterControl && this.options.initialized) {
+      this.$header_ = $('.fixed-table-header table thead').clone(true, true)
+    } else {
+      this.$header_ = this.$header.clone(true, true)
+      this.options.initialized = true
+    }
+    this.$selectAll_ = this.$header_.find('[name="btSelectAll"]')
+    this.$tableHeader
+      .css('margin-right', scrollWidth)
+      .find('table').css('width', this.$el.outerWidth())
+      .html('').attr('class', this.$el.attr('class'))
+      .append(this.$header_)
+
+    this.$tableLoading.css('width', this.$el.outerWidth())
+
+    const focusedTemp = $('.focus-temp:visible:eq(0)')
+
+    if (focusedTemp.length > 0) {
+      focusedTemp.focus()
+      this.$header.find('.focus-temp').removeClass('focus-temp')
+    }
+
+    // fix bug: $.data() is not working as expected after $.append()
+    this.$header.find('th[data-field]').each((i, el) => {
+      this.$header_.find(Utils.sprintf('th[data-field="%s"]', $(el).data('field'))).data($(el).data())
+    })
+
+    const visibleFields = this.getVisibleFields()
+    const $ths = this.$header_.find('th')
+    let $tr = this.$body.find('>tr:not(.no-records-found,.virtual-scroll-top)').eq(0)
+
+    while ($tr.length && $tr.find('>td[colspan]:not([colspan="1"])').length) {
+      $tr = $tr.next()
+    }
+
+    const trLength = $tr.find('> *').length
+
+    $tr.find('> *').each((i, el) => {
+      const $this = $(el)
+
+      if (Utils.hasDetailViewIcon(this.options)) {
+        if (
+          i === 0 && this.options.detailViewAlign !== 'right' ||
+          i === trLength - 1 && this.options.detailViewAlign === 'right'
+        ) {
+          const $thDetail = $ths.filter('.detail')
+          const zoomWidth = $thDetail.innerWidth() - $thDetail.find('.fht-cell').width()
+
+          $thDetail.find('.fht-cell').width($this.innerWidth() - zoomWidth)
+          return
+        }
+      }
+
+      const index = i - Utils.getDetailViewIndexOffset(this.options)
+      let $th = this.$header_.find(Utils.sprintf('th[data-field="%s"]', visibleFields[index]))
+
+      if ($th.length > 1) {
+        $th = $($ths[$this[0].cellIndex])
+      }
+
+      const zoomWidth = $th.innerWidth() - $th.find('.fht-cell').width()
+
+      $th.find('.fht-cell').width($this.innerWidth() - zoomWidth)
+    })
+
+    this.horizontalScroll()
+    this.trigger('post-header')
   }
 
   initSearch () {
@@ -300,8 +387,6 @@ $.BootstrapTable = class extends $.BootstrapTable {
   }
 
   initColumnSearch (filterColumnsDefaults) {
-    UtilsFilterControl.copyValues(this)
-
     if (filterColumnsDefaults) {
       this.filterColumnsPartial = filterColumnsDefaults
       this.updatePagination()
@@ -313,31 +398,6 @@ $.BootstrapTable = class extends $.BootstrapTable {
     }
   }
 
-  onColumnSearch ({ currentTarget, keyCode }) {
-    if ($.inArray(keyCode, [37, 38, 39, 40]) > -1) {
-      return
-    }
-
-    UtilsFilterControl.copyValues(this)
-    const text = $.trim($(currentTarget).val())
-    const $field = $(currentTarget).closest('[data-field]').data('field')
-
-    this.trigger('column-search', $field, text)
-
-    if ($.isEmptyObject(this.filterColumnsPartial)) {
-      this.filterColumnsPartial = {}
-    }
-    if (text) {
-      this.filterColumnsPartial[$field] = text
-    } else {
-      delete this.filterColumnsPartial[$field]
-    }
-
-    this.options.pageNumber = 1
-    this.enableControls(false)
-    this.onSearch({ currentTarget }, false)
-  }
-
   initToolbar () {
     this.showToolbar = this.showToolbar || this.options.showFilterControlSwitch
     this.showSearchClearButton = this.options.filterControl && this.options.showSearchClearButton
@@ -367,99 +427,104 @@ $.BootstrapTable = class extends $.BootstrapTable {
   }
 
   clearFilterControl () {
-    if (this.options.filterControl) {
-      const that = this
-      const cookies = UtilsFilterControl.collectBootstrapCookies()
-      const table = this.$el.closest('table')
-      const controls = UtilsFilterControl.getSearchControls(that)
-      const search = Utils.getSearchInput(this)
-      let hasValues = false
-      let timeoutId = 0
-
-      $.each(that.options.valuesFilterControl, (i, item) => {
-        hasValues = hasValues ? true : item.value !== ''
-        item.value = ''
-      })
+    if (!this.options.filterControl) {
+      return
+    }
 
-      $.each(that.options.filterControls, (i, item) => {
-        item.text = ''
-      })
+    const that = this
+    const cookies = UtilsFilterControl.collectBootstrapCookies()
+    const table = this.$el.closest('table')
+    const controls = UtilsFilterControl.getSearchControls(that)
+    const search = Utils.getSearchInput(this)
+    let hasValues = false
+    let timeoutId = 0
+
+    $.each(that.options.valuesFilterControl, (i, item) => {
+      hasValues = hasValues ? true : item.value !== ''
+      item.value = ''
+    })
 
-      UtilsFilterControl.setValues(that)
+    $.each(that.options.filterControls, (i, item) => {
+      item.text = ''
+    })
 
-      // clear cookies once the filters are clean
-      clearTimeout(timeoutId)
-      timeoutId = setTimeout(() => {
-        if (cookies && cookies.length > 0) {
-          $.each(cookies, (i, item) => {
-            if (that.deleteCookie !== undefined) {
-              that.deleteCookie(item)
-            }
-          })
-        }
-      }, that.options.searchTimeOut)
+    UtilsFilterControl.setValues(that)
 
-      // If there is not any value in the controls exit this method
-      if (!hasValues) {
-        return
+    // clear cookies once the filters are clean
+    clearTimeout(timeoutId)
+    timeoutId = setTimeout(() => {
+      if (cookies && cookies.length > 0) {
+        $.each(cookies, (i, item) => {
+          if (that.deleteCookie !== undefined) {
+            that.deleteCookie(item)
+          }
+        })
       }
+    }, that.options.searchTimeOut)
 
-      // 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[0]).trigger(
-          controls[0].tagName === 'INPUT' ? 'keyup' : 'change', { keyCode: 13 }
-        )
-      } else {
-        return
-      }
+    // If there is not any value in the controls exit this method
+    if (!hasValues) {
+      return
+    }
 
-      if (search.length > 0) {
-        that.resetSearch()
-      }
+    // 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[0]).trigger(
+        controls[0].tagName === 'INPUT' ? 'keyup' : 'change', { keyCode: 13 }
+      )
+    } else {
+      return
+    }
 
-      // 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 (search.length > 0) {
+      that.resetSearch()
+    }
 
-        if (sorter.length > 0) {
-          that.onSort({ type: 'keypress', currentTarget: sorter })
-          $(sorter).find('.sortable').trigger('click')
-        }
+    // 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')
       }
     }
   }
 
-  triggerSearch () {
-    const searchControls = UtilsFilterControl.getSearchControls(this)
+  // EVENTS
+  onColumnSearch ({ currentTarget, keyCode }) {
+    if (UtilsFilterControl.isKeyAllowed(keyCode)) {
+      return
+    }
 
-    searchControls.each(function () {
-      const el = $(this)
+    const $currentTarget = $(currentTarget)
 
-      if (el.is('select')) {
-        el.change()
-      } else {
-        el.keyup()
-      }
-    })
-  }
+    UtilsFilterControl.copyValues(this)
+    const text = $currentTarget.val().trim()
+    const $field = $currentTarget.closest('[data-field]').data('field')
 
-  enableControls (enable) {
-    if (this.options.disableControlWhenSearch && this.options.sidePagination === 'server') {
-      const searchControls = UtilsFilterControl.getSearchControls(this)
+    this.trigger('column-search', $field, text)
 
-      if (!enable) {
-        searchControls.prop('disabled', 'disabled')
-      } else {
-        searchControls.removeProp('disabled')
-      }
+    if ($.isEmptyObject(this.filterColumnsPartial)) {
+      this.filterColumnsPartial = {}
     }
+
+    if (text) {
+      this.filterColumnsPartial[$field] = text
+    } else {
+      delete this.filterColumnsPartial[$field]
+    }
+
+    this.options.pageNumber = 1
+    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) {
@@ -468,10 +533,38 @@ $.BootstrapTable = class extends $.BootstrapTable {
       $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')
+
+      if (this.options.filterControlVisible) {
+        $fixedControls.show()
+      } else {
+        $fixedControls.hide()
+      }
+
+      UtilsFilterControl.fixHeaderCSS(this, '49px')
+    }
+
     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')
+      }
+    })
+  }
 }

+ 20 - 50
src/extensions/filter-control/utils.js

@@ -1,3 +1,4 @@
+/* eslint-disable no-use-before-define */
 const Utils = $.fn.bootstrapTable.utils
 const searchControls = 'select, input:not([type="checkbox"]):not([type="radio"])'
 
@@ -12,6 +13,10 @@ export function getControlContainer (that) {
   return that.$header
 }
 
+export function isKeyAllowed (keyCode) {
+  return $.inArray(keyCode, [37, 38, 39, 40]) > -1
+}
+
 export function getSearchControls (that) {
   return getControlContainer(that).find(searchControls)
 }
@@ -73,8 +78,8 @@ export function sortSelectControl (selectControl, orderBy) {
   $selectControl.append($opts)
 }
 
-export function fixHeaderCSS ({ $tableHeader }) {
-  $tableHeader.css('height', '89px')
+export function fixHeaderCSS ({ $tableHeader }, pixels = '89px') {
+  $tableHeader.css('height', pixels)
 }
 
 export function getElementClass ($element) {
@@ -307,13 +312,14 @@ export function createControls (that, header) {
     if (!column.filterControl && !that.options.filterControlContainer) {
       html.push('<div class="no-filter-control"></div>')
     } else if (that.options.filterControlContainer) {
+      // Use a filter control container instead of th
       const $filterControls = $(`.bootstrap-table-filter-control-${column.field}`)
 
       $.each($filterControls, (_, filterControl) => {
         const $filterControl = $(filterControl)
 
         if (!$filterControl.is('[type=radio]')) {
-          const placeholder = column.filterControlPlaceholder ? column.filterControlPlaceholder : ''
+          const placeholder = column.filterControlPlaceholder || ''
 
           $filterControl.attr('placeholder', placeholder).val(column.filterDefault)
         }
@@ -323,6 +329,7 @@ export function createControls (that, header) {
 
       addedFilterControl = true
     } else {
+      // Create the control based on the html defined in the filterTemplate array.
       const nameControl = column.filterControl.toLowerCase()
 
       html.push('<div class="filter-control">')
@@ -342,6 +349,7 @@ export function createControls (that, header) {
       }
     }
 
+    // Filtering by default when it is set.
     if (!column.filterControl && '' !== column.filterDefault && 'undefined' !== typeof column.filterDefault) {
       if ($.isEmptyObject(that.filterColumnsPartial)) {
         that.filterColumnsPartial = {}
@@ -350,7 +358,7 @@ export function createControls (that, header) {
       that.filterColumnsPartial[column.field] = column.filterDefault
     }
 
-    $.each(header.find('th'), (i, th) => {
+    $.each(header.find('th'), (_, th) => {
       const $th = $(th)
 
       if ($th.data('field') === column.field) {
@@ -360,19 +368,12 @@ export function createControls (that, header) {
     })
 
     if (column.filterData && column.filterData.toLowerCase() !== 'column') {
-      const filterDataType = getFilterDataMethod(
-        /* eslint-disable no-use-before-define */
-        filterDataMethods,
-        column.filterData.substring(0, column.filterData.indexOf(':'))
-      )
+      const filterDataType = getFilterDataMethod(filterDataMethods, column.filterData.substring(0, column.filterData.indexOf(':')))
       let filterDataSource
       let selectControl
 
       if (filterDataType) {
-        filterDataSource = column.filterData.substring(
-          column.filterData.indexOf(':') + 1,
-          column.filterData.length
-        )
+        filterDataSource = column.filterData.substring(column.filterData.indexOf(':') + 1, column.filterData.length)
         selectControl = header.find(`.bootstrap-table-filter-control-${escapeID(column.field)}`)
 
         addOptionToSelectControl(selectControl, '', column.filterControlPlaceholder, column.filterDefault)
@@ -388,8 +389,6 @@ export function createControls (that, header) {
 
   if (addedFilterControl) {
     header.off('keyup', 'input').on('keyup', 'input', ({ currentTarget, keyCode }, obj) => {
-      syncControls(that)
-      // Simulate enter key action from clear button
       keyCode = obj ? obj.keyCode : keyCode
 
       if (that.options.searchOnEnterKey && keyCode !== 13) {
@@ -413,15 +412,14 @@ export function createControls (that, header) {
     })
 
     header.off('change', 'select:not(".ms-offscreen")').on('change', 'select:not(".ms-offscreen")', ({ currentTarget, keyCode }) => {
-      syncControls(that)
-      const $select = $(currentTarget)
-      const value = $select.val()
+      const $selectControl = $(currentTarget)
+      const value = $selectControl.val()
 
       if (value && value.length > 0 && value.trim()) {
-        $select.find('option[selected]').removeAttr('selected')
-        $select.find(`option[value="${ value }"]`).attr('selected', true)
+        $selectControl.find('option[selected]').removeAttr('selected')
+        $selectControl.find(`option[value="${ value }"]`).attr('selected', true)
       } else {
-        $select.find('option[selected]').removeAttr('selected')
+        $selectControl.find('option[selected]').removeAttr('selected')
       }
 
       clearTimeout(currentTarget.timeoutId || 0)
@@ -439,7 +437,6 @@ export function createControls (that, header) {
       }
 
       setTimeout(() => {
-        syncControls(that)
         const newValue = $input.val()
 
         if (newValue === '') {
@@ -454,11 +451,11 @@ export function createControls (that, header) {
     header.off('change', 'input[type=radio]').on('change', 'input[type=radio]', ({ currentTarget, keyCode }) => {
       clearTimeout(currentTarget.timeoutId || 0)
       currentTarget.timeoutId = setTimeout(() => {
-        syncControls(that)
         that.onColumnSearch({ currentTarget, keyCode })
       }, that.options.searchTimeOut)
     })
 
+    // Consider support default date picker
     if (header.find('.date-filter-control').length > 0) {
       $.each(that.columns, (i, { filterDefault, filterControl, field, filterDatepickerOptions }) => {
         if (filterControl !== undefined && filterControl.toLowerCase() === 'datepicker') {
@@ -473,7 +470,6 @@ export function createControls (that, header) {
           $datepicker.on('changeDate', ({ currentTarget, keyCode }) => {
             clearTimeout(currentTarget.timeoutId || 0)
             currentTarget.timeoutId = setTimeout(() => {
-              syncControls(that)
               that.onColumnSearch({ currentTarget, keyCode })
             }, that.options.searchTimeOut)
           })
@@ -510,32 +506,6 @@ export function getDirectionOfSelectOptions (_alignment) {
   }
 }
 
-export function syncControls (that) {
-  if (that.options.height) {
-    const controlsTableHeader = that.$tableHeader.find(searchControls)
-
-    that.$header.find(searchControls).each((_, control) => {
-      const $control = $(control)
-      const controlClass = getElementClass($control)
-      const foundControl = controlsTableHeader.filter((_, ele) => {
-        const eleClass = getElementClass($(ele))
-
-        return controlClass === eleClass
-      })
-
-      if (foundControl.length === 0) {
-        return
-      }
-      if ($control.is('select')) {
-        $control.find('option:selected').removeAttr('selected')
-        $control.find(`option[value='${foundControl.val()}']`).attr('selected', true)
-      } else {
-        $control.val(foundControl.val())
-      }
-    })
-  }
-}
-
 const filterDataMethods = {
   func (filterDataSource, selectControl, filterOrderBy, selected) {
     const variableValues = window[filterDataSource].apply()