浏览代码

Merge pull request #4131 from wenzhixin/feature/upgrade

Feature/upgrade
文翼 6 年之前
父节点
当前提交
84772e910d
共有 7 个文件被更改,包括 562 次插入548 次删除
  1. 4 2
      .gitignore
  2. 7 0
      CHANGELOG.md
  3. 1 0
      package.json
  4. 140 119
      site/docs/api/table-options.md
  5. 0 337
      src/bootstrap-table.css
  6. 102 90
      src/bootstrap-table.js
  7. 308 0
      src/bootstrap-table.scss

+ 4 - 2
.gitignore

@@ -4,8 +4,10 @@ bower_components
 # docs site
 _gh_pages
 
-# build the extensions
-extensions.js
+# use scss instead
+src/bootstrap-table.css
+src/bootstrap-table.css.map
+.sass-cache
 
 # old docs
 docs_

+ 7 - 0
CHANGELOG.md

@@ -3,10 +3,17 @@ ChangeLog
 
 ### 1.13.3
 
+- **New(js):** Supported full table classes of bootstrap v4.
+- **New(css):** Rewrited bootstrap-table.css to scss.
 - **New(accent-neutralise extension):** Rewrited accent-neutralise extension to ES6.
 - **New(addrbar extension):** Rewrited addrbar extension to ES6 and supported attribute option.
 - **New(group-by-v2 extension):** New `groupByFormatter` option.
 - **New(pipeline extension):** New pipeline extension `bootstrap-table-pipeline`.
+- **Remove(js):** Removed `striped` option and use classes instead.
+- **Update(js):** Fixed `locale` option bug.
+- **Update(js):** Fixed `sortClass` option bug.
+- **Update(js):** Fixed `sortStable` option cannot work bug.
+- **Update(js):** Improved built-in sort function and `customSort` logic.
 - **Update(cookie extension):** Improved cookie extension code.
 
 ### 1.13.2

+ 1 - 0
package.json

@@ -23,6 +23,7 @@
     "js:build:concat": "babel src/locale -o dist/bootstrap-table-locale-all.js && NODE_ENV=production babel src/locale -o dist/bootstrap-table-locale-all.min.js",
     "js:build:banner": "find dist -name '*.min.js' -exec headr {} -o {} --version --homepage --author --license \\;",
     "js:build": "run-s js:build:*",
+    "css:build:scss": "sass src/bootstrap-table.scss src/bootstrap-table.css",
     "css:build:base": "find src -name '*.css' | sed -e 'p;s/src/dist/' | xargs -n2 cp",
     "css:build:min": "find dist -name '*.css' | sed -e 'p;s/.css/.min.css/' | xargs -n2 cssmin",
     "css:build:banner": "find dist -name '*.min.css' -exec headr {} -o {} --version --homepage --author --license \\;",

+ 140 - 119
site/docs/api/table-options.md

@@ -25,25 +25,7 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `'table'`
 
-## locale
-
-- **Attribute:** `data-locale`
-
-- **Type:** `String`
-
-- **Detail:**
-
-  Sets the locale to use (i.e. `'zh-CN'`). Locale files must be pre-loaded.
-  Allows for fallback locales, if loaded, in the following order:
-
-  * First tries for the locale as specified,
-  * Then tries the locale with '_' translated to '-' and the region code upper cased,
-  * Then tries the the short locale code (i.e. `'zh'` instead of `'fr-CA'`),
-  * And finally will use the last locale file loaded (or the default locale if no locales loaded).
-
-  If left `undfined` or an empty string, uses the last locale loaded (or `'en-US'` if no locale files loaded).
-
-- **Default:** `undefined`
+- **Example:** [From HTML](https://examples.bootstrap-table.com/#options/from-html.html)
 
 ## height
 
@@ -57,17 +39,7 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `undefined`
 
-## undefinedText
-
-- **Attribute:** `data-undefined-text`
-
-- **Type:** `String`
-
-- **Detail:**
-
-  Defines the default `undefined` text.
-
-- **Default:** `'-'`
+- **Example:** [Table Height](https://examples.bootstrap-table.com/#options/table-height.html)
 
 ## classes
 
@@ -77,9 +49,11 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Detail:**
 
-  The class name of table. By default, the table is bordered, you can add `'table-no-bordered'` to remove table-bordered style.
+  The class name of table. `'table'`, `'table-bordered'`, `'table-hover'`, `'table-striped'`, `'table-dark'`, `'table-sm'` and `'table-borderless'` can be used. By default, the table is bordered.
+
+- **Default:** `'table table-bordered table-hover'`
 
-- **Default:** `'table table-hover'`
+- **Example:** [Table Classes](https://examples.bootstrap-table.com/#options/table-classes.html)
 
 ## theadClasses
 
@@ -93,29 +67,7 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `''`
 
-## sortClass
-
-- **Attribute:** `data-sort-class`
-
-- **Type:** `String`
-
-- **Detail:**
-
-  The class name of the `td` elements which are sorted.
-
-- **Default:** `undefined`
-
-## striped
-
-- **Attribute:** `data-striped`
-
-- **Type:** `Boolean`
-
-- **Detail:**
-
-  Set `true` to stripe the rows.
-
-- **Default:** `false`
+- **Example:** [Thead Classes](https://examples.bootstrap-table.com/#options/thead-classes.html)
 
 ## rowStyle
 
@@ -130,19 +82,12 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
   * `row`: the row record data.
   * `index`: the row index.
 
-  Support classes or css. Example usage:
-
-  {% highlight javascript %}
-  function rowStyle(row, index) {
-    return {
-      classes: 'text-nowrap another-class',
-      css: {color: 'blue', 'font-size': '50px'}
-    }
-  }
-  {% endhighlight %}
+  Support classes or css.
 
 - **Default:** `{}`
 
+- **Example:** [Row Style](https://examples.bootstrap-table.com/#options/row-style.html)
+
 ## rowAttributes
 
 - **Attribute:** `data-row-attributes`
@@ -160,6 +105,44 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `{}`
 
+- **Example:** [Row Attributes](https://examples.bootstrap-table.com/#options/row-attributes.html)
+
+## undefinedText
+
+- **Attribute:** `data-undefined-text`
+
+- **Type:** `String`
+
+- **Detail:**
+
+  Defines the default `undefined` text.
+
+- **Default:** `'-'`
+
+- **Example:** [Undefined Text](https://examples.bootstrap-table.com/#options/undefined-text.html)
+
+## locale
+
+- **Attribute:** `data-locale`
+
+- **Type:** `String`
+
+- **Detail:**
+
+  Sets the locale to use (i.e. `'zh-CN'`). Locale files must be pre-loaded.
+  Allows for fallback locales, if loaded, in the following order:
+
+  * First tries for the locale as specified,
+  * Then tries the locale with '_' translated to '-' and the region code upper cased,
+  * Then tries the the short locale code (i.e. `'zh'` instead of `'fr-CA'`),
+  * And finally will use the last locale file loaded (or the default locale if no locales loaded).
+
+  If left `undfined` or an empty string, uses the last locale loaded (or `'en-US'` if no locale files loaded).
+
+- **Default:** `undefined`
+
+- **Example:** [Table Locale](https://examples.bootstrap-table.com/#options/table-locale.html)
+
 ## sortable
 
 - **Attribute:** `data-sortable`
@@ -172,6 +155,22 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `true`
 
+- **Example:** [Table Sortable](https://examples.bootstrap-table.com/#options/table-sortable.html)
+
+## sortClass
+
+- **Attribute:** `data-sort-class`
+
+- **Type:** `String`
+
+- **Detail:**
+
+  The class name of the `td` elements which are sorted.
+
+- **Default:** `undefined`
+
+- **Example:** [Sort Class](https://examples.bootstrap-table.com/#options/sort-class.html)
+
 ## silentSort
 
 - **Attribute:** `data-silent-sort`
@@ -180,10 +179,12 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Detail:**
 
-  Set `false` to sort the data silently. This options works when the sidePagination option is set to `'server'`.
+  Set `false` to sort the data with loading message. This options works when the sidePagination option is set to `'server'`.
 
 - **Default:** `true`
 
+- **Example:** [Silent Sort](https://examples.bootstrap-table.com/#options/silent-sort.html)
+
 ## sortName
 
 - **Attribute:** `data-sort-name`
@@ -196,6 +197,8 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `undefined`
 
+- **Example:** [Sort Name Order](https://examples.bootstrap-table.com/#options/sort-name-order.html)
+
 ## sortOrder
 
 - **Attribute:** `data-sort-order`
@@ -208,6 +211,8 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `'asc'`
 
+- **Example:** [Sort Name Order](https://examples.bootstrap-table.com/#options/sort-name-order.html)
+
 ## sortStable
 
 - **Attribute:** `data-sort-stable`
@@ -220,6 +225,8 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `false`
 
+- **Example:** [Sort Stable](https://examples.bootstrap-table.com/#options/sort-stable.html)
+
 ## rememberOrder
 
 - **Attribute:** `data-remember-order`
@@ -228,10 +235,12 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Detail:**
 
-  Set true remember the order for each column.
+  Set `true` to remember the order for each column.
 
 - **Default:** `false`
 
+- **Example:** [Remember Order](https://examples.bootstrap-table.com/#options/remember-order.html)
+
 ## customSort
 
 - **Attribute:** `data-custom-sort`
@@ -240,21 +249,15 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Detail:**
 
-  The custom sort function is executed instead of built-in sort function, takes two parameters:
+  The custom sort function is executed instead of built-in sort function, takes three parameters:
 
   * `sortName`: the sort name.
   * `sortOrder`: the sort order.
+  * `data`: the rows data.
 
-  Example usage:
-
-  {% highlight javascript %}
-  function customSort(sortName, sortOrder) {
-    //Sort logic here.
-    //You must use `this.data` array in order to sort the data. NO use `this.options.data`.
-  }
-  {% endhighlight %}
+- **Default:** `undefined`
 
-- **Default:** `$.noop`
+- **Example:** [Custom Order](https://examples.bootstrap-table.com/#options/custom-order.html)
 
 ## columns
 
@@ -268,6 +271,8 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `[]`
 
+- **Example:** [Table Columns](https://examples.bootstrap-table.com/#options/table-columns.html)
+
 ## data
 
 - **Attribute:** `-`
@@ -280,41 +285,7 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `[]`
 
-## totalField
-
-- **Attribute:** `data-total-field`
-
-- **Type:** `String`
-
-- **Detail:**
-
-  Key in incoming json containing `'total'` data .
-
-- **Default:** `'total'`
-
-## dataField
-
-- **Attribute:** `data-data-field`
-
-- **Type:** `String`
-
-- **Detail:**
-
-  Key in incoming json containing `'rows'` data list.
-
-- **Default:** `'rows'`
-
-## method
-
-- **Attribute:** `data-method`
-
-- **Type:** `String`
-
-- **Detail:**
-
-  The method type to request remote data.
-
-- **Default:** `'get'`
+- **Example:** [From Data](https://examples.bootstrap-table.com/#options/from-data.html)
 
 ## url
 
@@ -333,17 +304,21 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `undefined`
 
-## ajax
+- **Example:** [From URL](https://examples.bootstrap-table.com/#options/from-url.html)
 
-- **Attribute:** `data-ajax`
+## method
 
-- **Type:** `Function`
+- **Attribute:** `data-method`
+
+- **Type:** `String`
 
 - **Detail:**
 
-  A method to replace ajax call. Should implement the same API as jQuery ajax method.
+  The method type to request remote data.
 
-- **Default:** `undefined`
+- **Default:** `'get'`
+
+- **Example:** [Table Method](https://examples.bootstrap-table.com/#options/table-method.html)
 
 ## cache
 
@@ -357,6 +332,8 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `true`
 
+- **Example:** [Table Cache](https://examples.bootstrap-table.com/#options/table-cache.html)
+
 ## contentType
 
 - **Attribute:** `data-content-type`
@@ -365,10 +342,12 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Detail:**
 
-  The contentType of request remote data.
+  The contentType of request remote data, for example: `application/x-www-form-urlencoded`.
 
 - **Default:** `'application/json'`
 
+- **Example:** [Content Type](https://examples.bootstrap-table.com/#options/content-type.html)
+
 ## dataType
 
 - **Attribute:** `data-data-type`
@@ -381,6 +360,22 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `'json'`
 
+- **Example:** [Data Type](https://examples.bootstrap-table.com/#options/data-type.html)
+
+## ajax
+
+- **Attribute:** `data-ajax`
+
+- **Type:** `Function`
+
+- **Detail:**
+
+  A method to replace ajax call. Should implement the same API as jQuery ajax method.
+
+- **Default:** `undefined`
+
+- **Example:** [Table AJAX](https://examples.bootstrap-table.com/#options/table-ajax.html)
+
 ## ajaxOptions
 
 - **Attribute:** `data-ajax-options`
@@ -393,6 +388,8 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `{}`
 
+- **Example:** [AJAX Options](https://examples.bootstrap-table.com/#options/ajax-options.html)
+
 ## queryParams
 
 - **Attribute:** `data-query-params`
@@ -437,6 +434,30 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
 
 - **Default:** `function(res) { return res }`
 
+## totalField
+
+- **Attribute:** `data-total-field`
+
+- **Type:** `String`
+
+- **Detail:**
+
+  Key in incoming json containing `'total'` data .
+
+- **Default:** `'total'`
+
+## dataField
+
+- **Attribute:** `data-data-field`
+
+- **Type:** `String`
+
+- **Detail:**
+
+  Key in incoming json containing `'rows'` data list.
+
+- **Default:** `'rows'`
+
 ## pagination
 
 - **Attribute:** `data-pagination`
@@ -740,7 +761,7 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
   }
   {% endhighlight %}
 
-- **Default:** `$.noop`
+- **Default:** `undefined`
 
 ## showHeader
 

+ 0 - 337
src/bootstrap-table.css

@@ -1,337 +0,0 @@
-/**
- * @author zhixin wen <wenzhixin2010@gmail.com>
- * version: 1.13.2
- * https://github.com/wenzhixin/bootstrap-table/
- */
-
-.bootstrap-table .table {
-    margin-bottom: 0 !important;
-    border-bottom: 1px solid #dddddd;
-    border-collapse: collapse !important;
-    border-radius: 1px;
-}
-
-.bootstrap-table .table:not(.table-condensed),
-.bootstrap-table .table:not(.table-condensed) > tbody > tr > th,
-.bootstrap-table .table:not(.table-condensed) > tfoot > tr > th,
-.bootstrap-table .table:not(.table-condensed) > thead > tr > td,
-.bootstrap-table .table:not(.table-condensed) > tbody > tr > td,
-.bootstrap-table .table:not(.table-condensed) > tfoot > tr > td {
-    padding: 8px;
-}
-
-.bootstrap-table .table.table-no-bordered > thead > tr > th {
-    border-top: none;
-}
-
-.bootstrap-table .table.table-no-bordered > thead > tr > th,
-.bootstrap-table .table.table-no-bordered > tbody > tr > td {
-    border-right: 2px solid transparent;
-}
-
-.bootstrap-table .table.table-no-bordered > tbody > tr > td:last-child {
-    border-right: none;
-}
-
-.fixed-table-container {
-    position: relative;
-    clear: both;
-    border: 1px solid #dddddd;
-    border-radius: 4px;
-    -webkit-border-radius: 4px;
-    -moz-border-radius: 4px;
-}
-
-.fixed-table-container.table-no-bordered {
-    border: 1px solid transparent;
-}
-
-.fixed-table-footer,
-.fixed-table-header {
-    overflow: hidden;
-}
-
-.fixed-table-footer {
-    border-top: 1px solid #dddddd;
-}
-
-.fixed-table-body {
-    overflow-x: auto;
-    overflow-y: auto;
-    height: 100%;
-}
-
-.fixed-table-container table {
-    width: 100%;
-}
-
-.fixed-table-container thead th {
-    height: 0;
-    padding: 0;
-    margin: 0;
-    border-left: 1px solid #dddddd;
-}
-
-.fixed-table-container thead th:focus {
-    outline: 0 solid transparent;
-}
-
-.fixed-table-container thead th:first-child:not([data-not-first-th]) {
-    border-left: none;
-    border-top-left-radius: 4px;
-    -webkit-border-top-left-radius: 4px;
-    -moz-border-radius-topleft: 4px;
-}
-
-.fixed-table-container thead th .th-inner,
-.fixed-table-container tbody td .th-inner {
-    padding: 8px;
-    line-height: 24px;
-    vertical-align: top;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-}
-
-.fixed-table-container thead th .sortable {
-    cursor: pointer;
-    background-position: right;
-    background-repeat: no-repeat;
-    padding-right: 30px;
-}
-
-.fixed-table-container thead th .both {
-    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC');
-}
-
-.fixed-table-container thead th .asc {
-    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==');
-}
-
-.fixed-table-container thead th .desc {
-    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ');
-}
-
-.fixed-table-container th.detail {
-    width: 30px;
-}
-
-.fixed-table-container tbody td {
-    border-left: 1px solid #dddddd;
-}
-
-.fixed-table-container tbody tr:first-child td {
-    border-top: none;
-}
-
-.fixed-table-container tbody td:first-child {
-    border-left: none;
-}
-
-/* the same color with .active */
-.fixed-table-container tbody .selected td {
-    background-color: #f5f5f5;
-}
-
-.fixed-table-container .bs-checkbox {
-    text-align: center;
-}
-
-.fixed-table-container input[type="radio"],
-.fixed-table-container input[type="checkbox"] {
-    margin: 0 auto !important;
-}
-
-.fixed-table-container .no-records-found {
-    text-align: center;
-}
-
-.fixed-table-pagination div.pagination,
-.fixed-table-pagination .pagination-detail {
-    margin-top: 10px;
-    margin-bottom: 10px;
-}
-
-.fixed-table-pagination div.pagination .pagination {
-    margin: 0;
-}
-
-.fixed-table-pagination .pagination a {
-    padding: 6px 12px;
-    line-height: 1.428571429;
-}
-
-.fixed-table-pagination ul.pagination li.page-intermediate a {
-    color:#c8c8c8;
-}
-
-.fixed-table-pagination ul.pagination li.page-intermediate a:before {
-    content: '\2B05';
-}
-
-.fixed-table-pagination ul.pagination li.page-intermediate a:after {
-    content: '\27A1';
-}
-
-.fixed-table-pagination .pagination-info {
-    line-height: 34px;
-    margin-right: 5px;
-}
-
-.fixed-table-pagination .btn-group {
-    position: relative;
-    display: inline-block;
-    vertical-align: middle;
-}
-
-.fixed-table-pagination .dropup .dropdown-menu {
-    margin-bottom: 0;
-}
-
-.fixed-table-pagination .page-list {
-    display: inline-block;
-}
-
-.fixed-table-toolbar .columns-left {
-    margin-right: 5px;
-}
-
-.fixed-table-toolbar .columns-right {
-    margin-left: 5px;
-}
-
-.fixed-table-toolbar .columns label {
-    display: block;
-    padding: 3px 20px;
-    clear: both;
-    font-weight: normal;
-    line-height: 1.428571429;
-}
-
-.fixed-table-toolbar .bs-bars,
-.fixed-table-toolbar .search,
-.fixed-table-toolbar .columns {
-    position: relative;
-    margin-top: 10px;
-    margin-bottom: 10px;
-}
-
-.fixed-table-pagination li.disabled a {
-    pointer-events: none;
-    cursor: default;
-}
-
-.fixed-table-loading {
-    display: none;
-    position: absolute;
-    top: 42px;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    z-index: 99;
-    background-color: #fff;
-    text-align: center;
-}
-
-.fixed-table-body .card-view .title {
-    font-weight: bold;
-    display: inline-block;
-    min-width: 30%;
-    text-align: left !important;
-}
-
-/* support bootstrap 2 */
-.fixed-table-body thead th .th-inner {
-    box-sizing: border-box;
-}
-
-.table th, .table td {
-    vertical-align: middle;
-    box-sizing: border-box;
-}
-
-.fixed-table-toolbar .dropdown-menu {
-    text-align: left;
-    max-height: 300px;
-    overflow: auto;
-}
-
-.fixed-table-toolbar .btn-group > .btn-group {
-    display: inline-block;
-    margin-left: -1px !important;
-}
-
-.fixed-table-toolbar .btn-group > .btn-group > .btn {
-    border-radius: 0;
-}
-
-.fixed-table-toolbar .btn-group > .btn-group:first-child > .btn {
-    border-top-left-radius: 4px;
-    border-bottom-left-radius: 4px;
-}
-
-.fixed-table-toolbar .btn-group > .btn-group:last-child > .btn {
-    border-top-right-radius: 4px;
-    border-bottom-right-radius: 4px;
-}
-
-.bootstrap-table .table > thead > tr > th {
-    vertical-align: bottom;
-    border-bottom: 1px solid #ddd;
-}
-
-.bootstrap-table .table > thead.thead-dark > tr > th {
-    border-bottom: 1px solid #212529;
-}
-
-/* support bootstrap 3 */
-.bootstrap-table .table thead > tr > th {
-    padding: 0;
-    margin: 0;
-}
-
-.bootstrap-table .fixed-table-footer tbody > tr > td {
-    padding: 0 !important;
-}
-
-.bootstrap-table .fixed-table-footer .table {
-    border-bottom: none;
-    border-radius: 0;
-    padding: 0 !important;
-}
-
-.bootstrap-table .pull-right .dropdown-menu {
-    right: 0;
-    left: auto;
-}
-
-/* calculate scrollbar width */
-div.fixed-table-scroll-inner {
-    width: 100%;
-    height: 200px;
-}
-
-div.fixed-table-scroll-outer {
-    top: 0;
-    left: 0;
-    visibility: hidden;
-    width: 200px;
-    height: 150px;
-    overflow: hidden;
-}
-
-/* for get correct heights  */
-.fixed-table-toolbar:after, .fixed-table-pagination:after {
-    content: "";
-    display: block;
-    clear: both;
-}
-
-.bootstrap-table.fullscreen {
-    position: fixed;
-    top: 0;
-    left: 0;
-    z-index: 1050;
-    width: 100%!important;
-    background: #FFF;
-}

+ 102 - 90
src/bootstrap-table.js

@@ -272,38 +272,35 @@
   // ======================
 
   const DEFAULTS = {
-    locale: undefined,
     height: undefined,
-    undefinedText: '-',
-    classes: 'table table-hover',
+    classes: 'table table-bordered table-hover',
     theadClasses: '',
-    sortClass: undefined,
-    striped: false,
     rowStyle (row, index) {
       return {}
     },
     rowAttributes (row, index) {
       return {}
     },
+    undefinedText: '-',
+    locale: undefined,
     sortable: true,
+    sortClass: undefined,
     silentSort: true,
     sortName: undefined,
     sortOrder: 'asc',
     sortStable: false,
     rememberOrder: false,
-    customSort: $.noop,
+    customSort: undefined,
     columns: [
       []
     ],
     data: [],
-    totalField: 'total',
-    dataField: 'rows',
-    method: 'get',
     url: undefined,
-    ajax: undefined,
+    method: 'get',
     cache: true,
     contentType: 'application/json',
     dataType: 'json',
+    ajax: undefined,
     ajaxOptions: {},
     queryParams (params) {
       return params
@@ -312,11 +309,13 @@
     responseHandler (res) {
       return res
     },
+    totalField: 'total',
+    dataField: 'rows',
     pagination: false,
     onlyInfoPagination: false,
     paginationLoop: true,
     sidePagination: 'client', // client or server
-    totalRows: 0, // server side need to set
+    totalRows: 0,
     pageNumber: 1,
     pageSize: 10,
     pageList: [10, 25, 50, 100],
@@ -335,7 +334,7 @@
     searchAlign: 'right',
     searchTimeOut: 500,
     searchText: '',
-    customSearch: $.noop,
+    customSearch: undefined,
     showHeader: true,
     showFooter: false,
     footerStyle (row, index) {
@@ -591,18 +590,17 @@
       if (this.options.locale) {
         const locales = $.fn.bootstrapTable.locales
         const parts = this.options.locale.split(/-|_/)
-        parts[0].toLowerCase()
+
+        parts[0] = parts[0].toLowerCase()
         if (parts[1]) {
-          parts[1].toUpperCase()
+          parts[1] = parts[1].toUpperCase()
         }
+
         if (locales[this.options.locale]) {
-          // locale as requested
           $.extend(this.options, locales[this.options.locale])
-        } else if ($.fn.bootstrapTable.locales[parts.join('-')]) {
-          // locale with sep set to - (in case original was specified with _)
+        } else if (locales[parts.join('-')]) {
           $.extend(this.options, locales[parts.join('-')])
-        } else if ($.fn.bootstrapTable.locales[parts[0]]) {
-          // short locale language code (i.e. 'en')
+        } else if (locales[parts[0]]) {
           $.extend(this.options, locales[parts[0]])
         }
       }
@@ -649,11 +647,15 @@
       this.$container.after('<div class="clearfix"></div>')
 
       this.$el.addClass(this.options.classes)
-      if (this.options.striped) {
-        this.$el.addClass('table-striped')
-      }
-      if (this.options.classes.split(' ').includes('table-no-bordered')) {
-        this.$tableContainer.addClass('table-no-bordered')
+
+      if (this.options.height) {
+        this.$tableContainer.addClass('fixed-height')
+
+        if (this.options.classes.split(' ').includes('table-bordered')) {
+          this.$tableBody.append('<div class="fixed-table-border"></div>')
+          this.$tableBorder = this.$tableBody.find('.fixed-table-border')
+          this.$tableLoading.addClass('fixed-table-border')
+        }
       }
     }
 
@@ -961,82 +963,87 @@
       const index = this.header.fields.indexOf(this.options.sortName)
       let timeoutId = 0
 
-      if (this.options.customSort !== $.noop) {
-        this.options.customSort.apply(this, [this.options.sortName, this.options.sortOrder])
-        return
-      }
-
       if (index !== -1) {
         if (this.options.sortStable) {
           this.data.forEach((row, i) => {
-            row._position = i
+            if (!row.hasOwnProperty('_position')) {
+              row._position = i
+            }
           })
         }
 
-        this.data.sort((a, b) => {
-          if (this.header.sortNames[index]) {
-            name = this.header.sortNames[index]
-          }
-          let aa = Utils.getItemField(a, name, this.options.escape)
-          let bb = Utils.getItemField(b, name, this.options.escape)
-          const value = Utils.calculateObjectValue(this.header, this.header.sorters[index], [aa, bb, a, b])
+        if (this.options.customSort) {
+          Utils.calculateObjectValue(this.options, this.options.customSort, [
+            this.options.sortName,
+            this.options.sortOrder,
+            this.data
+          ])
+        } else {
+          this.data.sort((a, b) => {
+            if (this.header.sortNames[index]) {
+              name = this.header.sortNames[index]
+            }
+            let aa = Utils.getItemField(a, name, this.options.escape)
+            let bb = Utils.getItemField(b, name, this.options.escape)
+            const value = Utils.calculateObjectValue(this.header, this.header.sorters[index], [aa, bb, a, b])
 
-          if (value !== undefined) {
-            if (this.options.sortStable && value === 0) {
-              return a._position - b._position
+            if (value !== undefined) {
+              if (this.options.sortStable && value === 0) {
+                return order * (a._position - b._position)
+              }
+              return order * value
             }
-            return order * value
-          }
 
-          // Fix #161: undefined or null string sort bug.
-          if (aa === undefined || aa === null) {
-            aa = ''
-          }
-          if (bb === undefined || bb === null) {
-            bb = ''
-          }
+            // Fix #161: undefined or null string sort bug.
+            if (aa === undefined || aa === null) {
+              aa = ''
+            }
+            if (bb === undefined || bb === null) {
+              bb = ''
+            }
 
-          if (this.options.sortStable && aa === bb) {
-            aa = a._position
-            bb = b._position
-            return a._position - b._position
-          }
+            if (this.options.sortStable && aa === bb) {
+              aa = a._position
+              bb = b._position
+            }
 
-          // IF both values are numeric, do a numeric comparison
-          if ($.isNumeric(aa) && $.isNumeric(bb)) {
-            // Convert numerical values form string to float.
-            aa = parseFloat(aa)
-            bb = parseFloat(bb)
-            if (aa < bb) {
-              return order * -1
+            // IF both values are numeric, do a numeric comparison
+            if ($.isNumeric(aa) && $.isNumeric(bb)) {
+              // Convert numerical values form string to float.
+              aa = parseFloat(aa)
+              bb = parseFloat(bb)
+              if (aa < bb) {
+                return order * -1
+              }
+              if (aa > bb) {
+                return order
+              }
+              return 0
             }
-            return order
-          }
 
-          if (aa === bb) {
-            return 0
-          }
+            if (aa === bb) {
+              return 0
+            }
 
-          // If value is not a string, convert to string
-          if (typeof aa !== 'string') {
-            aa = aa.toString()
-          }
+            // If value is not a string, convert to string
+            if (typeof aa !== 'string') {
+              aa = aa.toString()
+            }
 
-          if (aa.localeCompare(bb) === -1) {
-            return order * -1
-          }
+            if (aa.localeCompare(bb) === -1) {
+              return order * -1
+            }
 
-          return order
-        })
+            return order
+          })
+        }
 
         if (this.options.sortClass !== undefined) {
           clearTimeout(timeoutId)
           timeoutId = setTimeout(() => {
             this.$el.removeClass(this.options.sortClass)
-            const index = this.$header.find(Utils.sprintf('[data-field="%s"]',
-              this.options.sortName).index() + 1)
-            this.$el.find(Utils.sprintf('tr td:nth-child(%s)', index))
-              .addClass(this.options.sortClass)
+            const index = this.$header.find(`[data-field="${this.options.sortName}"]`).index()
+            this.$el.find(`tr td:nth-child(${index + 1})`).addClass(this.options.sortClass)
           }, 250)
         }
       }
@@ -1271,7 +1278,7 @@
 
     initSearch () {
       if (this.options.sidePagination !== 'server') {
-        if (this.options.customSearch !== $.noop) {
+        if (this.options.customSearch) {
           Utils.calculateObjectValue(this.options, this.options.customSearch, [this.searchText])
           return
         }
@@ -2085,7 +2092,9 @@
 
           this.load(res)
           this.trigger('load-success', res)
-          if (!silent) this.$tableLoading.hide()
+          if (!silent) {
+            this.$tableLoading.hide()
+          }
         },
         error: jqXHR => {
           let data = []
@@ -2229,7 +2238,9 @@
 
         if (this.options.detailView && !this.options.cardView) {
           if (i === 0) {
-            this.$header_.find('th.detail').find('.fht-cell').width($this.innerWidth())
+            const $thDetail = $ths.filter('.detail')
+            const zoomWidth = $thDetail.width() - $thDetail.find('.fht-cell').width()
+            $thDetail.find('.fht-cell').width($this.innerWidth() - zoomWidth)
           }
           index = i - 1
         }
@@ -2400,14 +2411,6 @@
       this.$selectAll.prop('checked', this.$selectItem.length > 0 &&
         this.$selectItem.length === this.$selectItem.filter(':checked').length)
 
-      if (this.options.height) {
-        const toolbarHeight = this.$toolbar.outerHeight(true)
-        const paginationHeight = this.$pagination.outerHeight(true)
-        const height = this.options.height - toolbarHeight - paginationHeight
-
-        this.$tableContainer.css('height', `${height}px`)
-      }
-
       if (this.options.cardView) {
         // remove the element css
         this.$el.css('margin-top', '0')
@@ -2432,6 +2435,15 @@
         }
       }
 
+      if (this.options.height) {
+        const toolbarHeight = this.$toolbar.outerHeight(true)
+        const paginationHeight = this.$pagination.outerHeight(true)
+        const height = this.options.height - toolbarHeight - paginationHeight
+        const tableHeight = this.$tableBody.find('table').outerHeight(true)
+        this.$tableContainer.css('height', `${height}px`)
+        this.$tableBorder && this.$tableBorder.css('height', `${height - tableHeight - padding - 1}px`)
+      }
+
       // Assign the correct sortable arrow
       this.getCaret()
       this.$tableContainer.css('padding-bottom', `${padding}px`)

+ 308 - 0
src/bootstrap-table.scss

@@ -0,0 +1,308 @@
+/**
+ * @author zhixin wen <wenzhixin2010@gmail.com>
+ * version: 1.13.2
+ * https://github.com/wenzhixin/bootstrap-table/
+ */
+
+$border-color: #dee2e6;
+$hover-bg: rgba(0,0,0,.075);
+$dark-border-color: #32383e;
+
+.bootstrap-table {
+  .fixed-table-toolbar {
+    &:after {
+      content: "";
+      display: block;
+      clear: both;
+    }
+
+    .bs-bars,
+    .search,
+    .columns {
+      position: relative;
+      margin-top: 10px;
+      margin-bottom: 10px;
+    }
+
+    .columns {
+      .btn-group > .btn-group {
+        display: inline-block;
+        margin-left: -1px !important;
+
+        &:first-child > .btn {
+          border-top-left-radius: 4px;
+          border-bottom-left-radius: 4px;
+        }
+
+        &:last-child > .btn {
+          border-top-right-radius: 4px;
+          border-bottom-right-radius: 4px;
+        }
+
+        > .btn {
+          border-radius: 0;
+        }
+      }
+
+      .dropdown-menu {
+        text-align: left;
+        max-height: 300px;
+        overflow: auto;
+      }
+
+      label {
+        display: block;
+        padding: 3px 20px;
+        clear: both;
+        font-weight: normal;
+        line-height: 1.428571429;
+      }
+    }
+
+    .columns-left {
+      margin-right: 5px;
+    }
+    .columns-right {
+      margin-left: 5px;
+    }
+
+    .pull-right .dropdown-menu {
+      right: 0;
+      left: auto;
+    }
+  }
+
+  .fixed-table-container {
+    position: relative;
+    clear: both;
+
+    &.fixed-height {
+      border-bottom: 1px solid $border-color;
+
+      .fixed-table-border {
+        border-left: 1px solid $border-color;
+        border-right: 1px solid $border-color;
+      }
+
+      .table {
+        thead th {
+          border-bottom: 1px solid $border-color;
+        }
+      }
+
+      .table-dark {
+        thead th {
+          border-bottom: 1px solid $dark-border-color;
+        }
+      }
+    }
+
+    .fixed-table-header {
+      overflow: hidden;
+    }
+
+    .fixed-table-body {
+      overflow-x: auto;
+      overflow-y: auto;
+      height: 100%;
+
+      .fixed-table-loading {
+        display: none;
+        position: absolute;
+        top: 42px;
+        right: 0;
+        bottom: 0;
+        left: 0;
+        z-index: 99;
+        background-color: #fff;
+        text-align: center;
+      }
+    }
+
+    .table {
+      width: 100%;
+      margin-bottom: 0 !important;
+
+      th,
+      td {
+        vertical-align: middle;
+        box-sizing: border-box;
+      }
+
+      thead th {
+        vertical-align: bottom;
+        padding: 0;
+        margin: 0;
+
+        &:focus {
+          outline: 0 solid transparent;
+        }
+
+        &.detail {
+          width: 36px;
+        }
+
+        .th-inner {
+          padding: .75rem;
+          vertical-align: bottom;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+
+        .sortable {
+          cursor: pointer;
+          background-position: right;
+          background-repeat: no-repeat;
+          padding-right: 30px;
+        }
+
+        .both {
+          background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC');
+        }
+
+        .asc {
+          background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==');
+        }
+
+        .desc {
+          background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ');
+        }
+      }
+
+      tbody tr {
+        &.selected td {
+          background-color: $hover-bg;
+        }
+
+        &.no-records-found {
+          text-align: center;
+        }
+
+        .card-view .title {
+          font-weight: bold;
+          display: inline-block;
+          min-width: 30%;
+          text-align: left !important;
+        }
+      }
+
+      .bs-checkbox {
+        text-align: center;
+      }
+
+      input[type="radio"],
+      input[type="checkbox"] {
+        margin: 0 auto !important;
+      }
+
+      &.table-sm .th-inner {
+        padding: .3rem;
+      }
+    }
+
+    .fixed-table-footer {
+      overflow: hidden;
+      border-top: 1px solid $border-color;
+
+      .table {
+        border-bottom: none;
+        border-radius: 0;
+        padding: 0 !important;
+
+        tbody > tr > td {
+          padding: 0 !important;
+        }
+      }
+    }
+  }
+
+  .fixed-table-pagination {
+    &:after {
+      content: "";
+      display: block;
+      clear: both;
+    }
+
+    > .pagination-detail,
+    > .pagination {
+      margin-top: 10px;
+      margin-bottom: 10px;
+    }
+
+    > .pagination-detail {
+      .pagination-info {
+        line-height: 34px;
+        margin-right: 5px;
+      }
+
+      .page-list {
+        display: inline-block;
+
+        .btn-group {
+          position: relative;
+          display: inline-block;
+          vertical-align: middle;
+
+          .dropdown-menu {
+            margin-bottom: 0;
+          }
+        }
+      }
+    }
+
+    > .pagination {
+      ul.pagination {
+        margin: 0;
+
+        a {
+          padding: 6px 12px;
+          line-height: 1.428571429;
+        }
+
+        li.page-intermediate {
+          a {
+            &:before {
+              content: '\2B05';
+            }
+
+            &:after {
+              content: '\27A1';
+            }
+
+            color:#c8c8c8;
+          }
+        }
+
+        li.disabled a {
+          pointer-events: none;
+          cursor: default;
+        }
+      }
+    }
+  }
+
+  &.fullscreen {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 1050;
+    width: 100%!important;
+    background: #FFF;
+  }
+}
+
+/* calculate scrollbar width */
+div.fixed-table-scroll-inner {
+  width: 100%;
+  height: 200px;
+}
+
+div.fixed-table-scroll-outer {
+  top: 0;
+  left: 0;
+  visibility: hidden;
+  width: 200px;
+  height: 150px;
+  overflow: hidden;
+}