Compare commits

..

49 Commits

Author SHA1 Message Date
AllenFang
e2e6c51d40 Publish
- react-bootstrap-table2-example@1.0.24
 - react-bootstrap-table2-filter@1.1.9
 - react-bootstrap-table2-paginator@2.0.6
 - react-bootstrap-table2-toolkit@1.4.1
 - react-bootstrap-table-next@3.1.1
2019-04-15 19:45:38 +08:00
Allen
fb724331d3 Merge pull request #900 from react-bootstrap-table/develop
20180408 release
2019-04-15 19:38:41 +08:00
AllenFang
160dede412 fix #898 2019-04-07 17:33:00 +08:00
AllenFang
363a43251f fix #891 2019-04-07 16:49:03 +08:00
AllenFang
8ad0e65679 Merge branch 'SmaranSingh-master' into develop 2019-04-07 14:32:41 +08:00
AllenFang
6d5cca0047 optimize selection option equals 2019-04-07 14:31:43 +08:00
AllenFang
204e75c9c2 Merge branch 'master' of https://github.com/SmaranSingh/react-bootstrap-table2 into SmaranSingh-master 2019-04-07 14:30:21 +08:00
AllenFang
fc27c56cbb Merge branch 'Jayboy75-develop' into develop 2019-04-07 14:05:40 +08:00
AllenFang
8436edba7e no inline-block on label element 2019-04-07 14:03:42 +08:00
SmaranSingh
3606fa3b7b Update select.js 2019-04-04 16:11:01 +05:30
SmaranSingh
73e8701bde Update select.js 2019-04-04 15:37:34 +05:30
Jay Staudt
6d2493d537 revert page-button title 2019-04-02 14:53:52 -04:00
Jay Staudt
1a1f6969cb adjustments via eslint 2019-04-02 14:42:47 -04:00
Jay Staudt
d47a3757b8 wrap search bar with label and hidden text 2019-04-02 14:10:14 -04:00
Jay Staudt
ba7512969e add non-redundant titles to page buttons 2019-04-02 14:09:53 -04:00
Jay Staudt
955ee17939 wrap filter components with labels and hidden text 2019-04-02 14:09:20 -04:00
AllenFang
497bf44192 Publish
- react-bootstrap-table2-editor@1.2.3
 - react-bootstrap-table2-example@1.0.23
 - react-bootstrap-table2-filter@1.1.8
 - react-bootstrap-table-next@3.1.0
2019-04-02 21:33:49 +08:00
Allen
fcefcf8c84 Merge pull request #886 from react-bootstrap-table/develop
20190331 release
2019-04-02 21:25:03 +08:00
Allen
04e3af0bbb Merge pull request #861 from react-bootstrap-table/feature/row-expand-animate
Feature/row expand animate
2019-03-31 16:02:13 +08:00
AllenFang
9f47fa009c lock version 2019-03-31 15:33:43 +08:00
Alek Lefebvre
0edf9c8891 fix #808 (#874) 2019-03-31 15:17:57 +08:00
AllenFang
df5024892c Publish
- react-bootstrap-table2-example@1.0.22
 - react-bootstrap-table2-paginator@2.0.5
 - react-bootstrap-table-next@3.0.3
2019-03-26 20:41:04 +08:00
Allen
4448c3f28c Merge pull request #875 from react-bootstrap-table/develop
20190326 release
2019-03-26 20:39:13 +08:00
AllenFang
196ae33295 fix #859 2019-03-25 23:07:22 +08:00
AllenFang
7f1b7a6c97 fix #838 2019-03-24 20:52:26 +08:00
AllenFang
a6ccafcc75 fix #866 2019-03-24 16:55:56 +08:00
AllenFang
06d87299a3 Publish
- react-bootstrap-table2-example@1.0.21
 - react-bootstrap-table2-filter@1.1.7
 - react-bootstrap-table2-toolkit@1.4.0
 - react-bootstrap-table-next@3.0.2
2019-03-18 00:31:24 +08:00
Allen
5891ec1b93 Merge pull request #862 from react-bootstrap-table/develop
20190317 release
2019-03-18 00:28:47 +08:00
AllenFang
c5d9e04c2c fix #817 2019-03-17 16:30:39 +08:00
AllenFang
dba3da28c1 Merge branch 'YassienW-animate-row-expansion' into feature/row-expand-animate 2019-03-17 15:24:07 +08:00
AllenFang
a0e09cd804 fix conflicts 2019-03-17 15:20:16 +08:00
AllenFang
d0e70f7246 fix #852 2019-03-17 14:43:39 +08:00
henning-kvinnesland
b93c683f17 Fix typo. (#855)
`to cusom the sort caret` => `to customize the sort-caret`
2019-03-17 13:30:35 +08:00
henning-kvinnesland
f80e1ea66c Fix typo. (#856)
`chagne` => `change`
2019-03-17 13:30:21 +08:00
AllenFang
7642bfa1a3 fix #849 2019-03-17 13:29:17 +08:00
Yassien
8f304a849f Animation now works with custom expand management 2019-03-16 19:03:22 +02:00
AllenFang
956f1cef4d Publish
- react-bootstrap-table2-example@1.0.20
 - react-bootstrap-table2-filter@1.1.6
 - react-bootstrap-table2-paginator@2.0.4
 - react-bootstrap-table2-toolkit@1.3.2
 - react-bootstrap-table-next@3.0.1
2019-03-10 13:38:21 +08:00
Allen
c45deee590 Merge pull request #846 from react-bootstrap-table/develop
20190310 release
2019-03-10 13:37:02 +08:00
AllenFang
2aab4301dd fix #835 2019-03-09 23:01:21 +08:00
AllenFang
43aa280761 Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2019-03-09 23:00:59 +08:00
AllenFang
3af30a0265 fix #840 2019-03-09 22:35:49 +08:00
AllenFang
4b8b8b261e update story 2019-03-09 22:31:24 +08:00
Norbert Nemeth
e44782f222 fix #863 - expanded row column span does not update (#837) 2019-03-09 18:46:04 +08:00
AllenFang
921e8c7ecc fix #830 2019-03-09 18:18:20 +08:00
AllenFang
a3b3ce0dc4 Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2019-03-09 15:52:37 +08:00
AllenFang
e9f08d278d fix #826 2019-03-09 15:52:10 +08:00
Sartaj Singh Baveja
b4973c826c Minor update to docs (#841) 2019-03-06 22:28:41 +08:00
Yassien
33c026c7e2 Added original padding without breaking the animation 2019-02-25 13:01:42 +02:00
Yassien
d84fd5c801 CSS Animation for row expansion 2019-02-23 22:40:05 +02:00
53 changed files with 1286 additions and 391 deletions

View File

@@ -33,6 +33,7 @@
* [pagination](#pagination) * [pagination](#pagination)
* [filter](#filter) * [filter](#filter)
* [onTableChange](#onTableChange) * [onTableChange](#onTableChange)
* [onDataSizeChange](#onDataSizeChange)
### <a name='keyField'>keyField(**required**) - [String]</a> ### <a name='keyField'>keyField(**required**) - [String]</a>
Tells `react-bootstrap-table2` which column is unique. Tells `react-bootstrap-table2` which column is unique.
@@ -318,3 +319,19 @@ Following is a shape of `newState`
} }
} }
``` ```
### <a name='onDataSizeChange'>onDataSizeChange - [Function]</a>
This callback function will be called only when data size change by search/filter etc. This function have one argument which is an object contains below props:
* `dataSize`: The new data size
```js
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
<BootstrapTable
onDataSizeChange={ handleDataChange }
....
/>
```

View File

@@ -69,7 +69,7 @@ const cellEdit: {
// omit... // omit...
beforeSaveCell(oldValue, newValue, row, column, done) { beforeSaveCell(oldValue, newValue, row, column, done) {
setTimeout(() => { setTimeout(() => {
if (confirm('Do you want to accep this change?')) { if (confirm('Do you want to accept this change?')) {
done(); // contine to save the changes done(); // contine to save the changes
} else { } else {
done(false); // reject the changes done(false); // reject the changes

View File

@@ -164,7 +164,7 @@ Enable the column sort via a `true` value given.
``` ```
## <a name='sortCaret'>column.sortCaret - [Function]</a> ## <a name='sortCaret'>column.sortCaret - [Function]</a>
Use`column.sortCaret` to custom the sort caret. This callback function accept two arguments: `order` and `column` Use`column.sortCaret` to customize the sort caret. This callback function accept two arguments: `order` and `column`
```js ```js
{ {

View File

@@ -2,7 +2,7 @@
# Row expand # Row expand
`react-bootstrap-table2` supports the row expand feature. By passing prop `expandRow` to enable this functionality. `react-bootstrap-table2` supports the row expand feature. By passing prop `expandRow` to enable this functionality.
> Default is click to expand/collapse a row. In addition, we don't support any way to chagne this mechanism! > Default is click to expand/collapse a row. In addition, we don't support any way to change this mechanism!
## Required ## Required
* [renderer (**required**)](#renderer) * [renderer (**required**)](#renderer)

View File

@@ -132,7 +132,7 @@ const columns = [
if (typeof cell !== 'object') { if (typeof cell !== 'object') {
dateObj = new Date(cell); dateObj = new Date(cell);
} }
return `${('0' + dateObj.getDate()).slice(-2)}/${('0' + (dateObj.getMonth() + 1)).slice(-2)}/${dateObj.getFullYear()}`; return `${('0' + dateObj.getUTCDate()).slice(-2)}/${('0' + (dateObj.getUTCMonth() + 1)).slice(-2)}/${dateObj.getUTCFullYear()}`;
}, },
editor: { editor: {
type: Type.DATE type: Type.DATE

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-editor", "name": "react-bootstrap-table2-editor",
"version": "1.2.2", "version": "1.2.3",
"description": "it's the editor addon for react-bootstrap-table2", "description": "it's the editor addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"scripts": { "scripts": {

View File

@@ -9,6 +9,7 @@ const sourceStylePath = path.join(__dirname, '../../react-bootstrap-table2/style
const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style'); const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style');
const filterStylePath = path.join(__dirname, '../../react-bootstrap-table2-filter/style'); const filterStylePath = path.join(__dirname, '../../react-bootstrap-table2-filter/style');
const toolkitSourcePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/index.js'); const toolkitSourcePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/index.js');
const toolkitStylePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/style');
const storyPath = path.join(__dirname, '../stories'); const storyPath = path.join(__dirname, '../stories');
const examplesPath = path.join(__dirname, '../examples'); const examplesPath = path.join(__dirname, '../examples');
const srcPath = path.join(__dirname, '../src'); const srcPath = path.join(__dirname, '../src');
@@ -43,7 +44,13 @@ const loaders = [{
}, { }, {
test: /\.scss$/, test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'], use: ['style-loader', 'css-loader', 'sass-loader'],
include: [storyPath, sourceStylePath, paginationStylePath, filterStylePath], include: [
storyPath,
sourceStylePath,
paginationStylePath,
filterStylePath,
toolkitStylePath
],
}, { }, {
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/, test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000', loader: 'url-loader?limit=100000',

View File

@@ -23,7 +23,7 @@ const columns = [{
if (typeof cell !== 'object') { if (typeof cell !== 'object') {
dateObj = new Date(cell); dateObj = new Date(cell);
} }
return `${('0' + dateObj.getDate()).slice(-2)}/${('0' + (dateObj.getMonth() + 1)).slice(-2)}/${dateObj.getFullYear()}`; return `${('0' + dateObj.getUTCDate()).slice(-2)}/${('0' + (dateObj.getUTCMonth() + 1)).slice(-2)}/${dateObj.getUTCFullYear()}`;
}, },
editor: { editor: {
type: Type.DATE type: Type.DATE
@@ -48,7 +48,7 @@ const columns = [{
if (typeof cell !== 'object') { if (typeof cell !== 'object') {
dateObj = new Date(cell); dateObj = new Date(cell);
} }
return \`$\{('0' + dateObj.getDate()).slice(-2)}/$\{('0' + (dateObj.getMonth() + 1)).slice(-2)}/$\{dateObj.getFullYear()}\`; return \`$\{('0' + dateObj.getUTCDate()).slice(-2)}/$\{('0' + (dateObj.getUTCMonth() + 1)).slice(-2)}/$\{dateObj.getUTCFullYear()}\`;
}, },
editor: { editor: {
type: Type.DATE type: Type.DATE

View File

@@ -11,141 +11,13 @@ const products = [
{ id: 14, name: 'Item 14', price: 14.5, inStock: true } { id: 14, name: 'Item 14', price: 14.5, inStock: true }
]; ];
const columns = [
{
dataField: 'id',
text: 'Product ID'
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
},
{
dataField: 'inStock',
text: 'In Stock',
formatter: (cellContent, row) => (
<div className="checkbox disabled">
<label>
<input type="checkbox" checked={ row.inStock } disabled />
</label>
</div>
)
},
{
dataField: 'df1',
isDummyField: true,
text: 'Action 1',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
},
{
dataField: 'df2',
isDummyField: true,
text: 'Action 2',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
}
];
const sourceCode = `\ const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next'; import BootstrapTable from 'react-bootstrap-table-next';
const columns = [
{
dataField: 'id',
text: 'Product ID'
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
},
{
dataField: 'inStock',
text: 'In Stock',
formatter: (cellContent, row) => (
<div className="checkbox disabled">
<label>
<input type="checkbox" checked={ row.inStock } disabled />
</label>
</div>
)
},
{
dataField: 'df1',
isDummyField: true,
text: 'Action 1',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
},
{
dataField: 'df2',
isDummyField: true,
text: 'Action 2',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
}
];
class ProductList extends React.Component { class ProductList extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { products }; this.state = { products, count: 0 };
} }
toggleInStock = () => { toggleInStock = () => {
@@ -163,17 +35,96 @@ class ProductList extends React.Component {
}; };
render() { render() {
const columns = [
{
dataField: 'id',
text: 'Product ID',
formatter: (cell, row, rowIndex, extraData) => (
<div>
<span>ID: {row.id}</span>
<br />
<span>state: {extraData}</span>
</div>
),
formatExtraData: this.state.count
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
},
{
dataField: 'inStock',
text: 'In Stock',
formatter: (cellContent, row) => (
<div className="checkbox disabled">
<label>
<input type="checkbox" checked={ row.inStock } disabled />
</label>
</div>
)
},
{
dataField: 'df1',
isDummyField: true,
text: 'Action 1',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
},
{
dataField: 'df2',
isDummyField: true,
text: 'Action 2',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
}
];
return ( return (
<div> <div>
<h1 className="h2">Products</h1> <h3>Action 1 and Action 2 are dummy column</h3>
<button onClick={ this.toggleInStock } className="btn btn-primary">
Toggle item 13 stock status
</button>
<button
className="btn btn-success"
onClick={ () => this.setState(() => ({ count: this.state.count + 1 })) }
>
Click me to Increase counter
</button>
<BootstrapTable <BootstrapTable
keyField="id" keyField="id"
data={ this.state.products } data={ this.state.products }
columns={ columns } columns={ columns }
/> />
<button onClick={ this.toggleInStock } className="btn btn-primary"> <Code>{ sourceCode }</Code>
Toggle item 13 stock status
</button>
</div> </div>
); );
} }
@@ -183,7 +134,7 @@ class ProductList extends React.Component {
class ProductList extends React.Component { class ProductList extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { products }; this.state = { products, count: 0 };
} }
toggleInStock = () => { toggleInStock = () => {
@@ -200,13 +151,95 @@ class ProductList extends React.Component {
this.setState(curr => ({ ...curr, products: newProducts })); this.setState(curr => ({ ...curr, products: newProducts }));
}; };
counter = () => {
this.setState(curr => ({ ...curr, count: this.state.count + 1 }));
}
render() { render() {
const columns = [
{
dataField: 'id',
text: 'Product ID',
formatter: (cell, row, rowIndex, extraData) => (
<div>
<span>ID: {row.id}</span>
<br />
<span>Counter: {extraData}</span>
</div>
),
formatExtraData: this.state.count
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
},
{
dataField: 'inStock',
text: 'In Stock',
formatter: (cellContent, row) => (
<div className="checkbox disabled">
<label>
<input type="checkbox" checked={ row.inStock } disabled />
</label>
</div>
)
},
{
dataField: 'df1',
isDummyField: true,
text: 'Action 1',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
},
{
dataField: 'df2',
isDummyField: true,
text: 'Action 2',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
}
];
return ( return (
<div> <div>
<h3>Action 1 and Action 2 are dummy column</h3> <h3>Action 1 and Action 2 are dummy column</h3>
<button onClick={ this.toggleInStock } className="btn btn-primary"> <button onClick={ this.toggleInStock } className="btn btn-primary">
Toggle item 13 stock status Toggle item 13 stock status
</button> </button>
<button
className="btn btn-success"
onClick={ this.counter }
>
Click me to Increase counter
</button>
<BootstrapTable <BootstrapTable
keyField="id" keyField="id"
data={ this.state.products } data={ this.state.products }

View File

@@ -0,0 +1,102 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { CSVExport, Search } from 'react-bootstrap-table2-toolkit';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import paginationFactory from 'react-bootstrap-table2-paginator';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { SearchBar } = Search;
const { ExportCSVButton } = CSVExport;
const products = productsGenerator(150);
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { CSVExport, Search } from 'react-bootstrap-table2-toolkit';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
const { SearchBar } = Search;
const { ExportCSVButton } = CSVExport;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
clickToSelect: true
};
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV={ { onlyExportFiltered: true, exportAll: false } }
search
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<SearchBar { ...props.searchProps } />
<BootstrapTable
{ ...props.baseProps }
pagination={ paginationFactory() }
filter={ filterFactory() }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<h3>Export all the filtered/searched rows</h3>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV={ { onlyExportFiltered: true, exportAll: false } }
search
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<SearchBar { ...props.searchProps } />
<BootstrapTable
{ ...props.baseProps }
pagination={ paginationFactory() }
filter={ filterFactory() }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,151 @@
/* eslint react/no-multi-comp: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import paginationFactory from 'react-bootstrap-table2-paginator';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter()
}];
const sourceCode1 = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
class Case1 extends React.Component {
constructor(props) {
super(props);
this.state = { rowCount: products.length };
}
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
render() {
return (
<div>
<h5>Row Count:<span className="badge">{ this.state.rowCount }</span></h5>
<BootstrapTable
onDataSizeChange={ this.handleDataChange }
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);
}
`;
const sourceCode2 = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import paginationFactory from 'react-bootstrap-table2-paginator';
class Case2 extends React.Component {
constructor(props) {
super(props);
this.state = { rowCount: products.length };
}
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
render() {
return (
<div>
<h5>Row Count:<span className="badge">{ this.state.rowCount }</span></h5>
<BootstrapTable
onDataSizeChange={ this.handleDataChange }
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
pagination={ paginationFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);
}
`;
const products1 = productsGenerator(8);
class WithoutPaginationCase extends React.Component {
constructor(props) {
super(props);
this.state = { rowCount: products1.length };
}
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
render() {
return (
<div>
<h3>Without Pagination Case</h3>
<h5>Row Count:<span className="badge">{ this.state.rowCount }</span></h5>
<BootstrapTable
onDataSizeChange={ this.handleDataChange }
keyField="id"
data={ products1 }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode2 }</Code>
</div>
);
}
}
const products2 = productsGenerator(88);
class WithPaginationCase extends React.Component {
constructor(props) {
super(props);
this.state = { rowCount: products2.length };
}
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
render() {
return (
<div>
<h3>Without Pagination Case</h3>
<h5>Row Count:<span className="badge">{ this.state.rowCount }</span></h5>
<BootstrapTable
onDataSizeChange={ this.handleDataChange }
keyField="id"
data={ products2 }
columns={ columns }
filter={ filterFactory() }
pagination={ paginationFactory() }
/>
<Code>{ sourceCode1 }</Code>
</div>
);
}
}
export default () => (
<div>
<WithoutPaginationCase />
<WithPaginationCase />
</div>
);

View File

@@ -7,7 +7,7 @@ import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block'; import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common'; import { productsGenerator } from 'utils/common';
const products = productsGenerator(21); const products = productsGenerator(40);
const { SearchBar } = Search; const { SearchBar } = Search;
const columns = [{ const columns = [{
@@ -24,6 +24,12 @@ import paginationFactory, { PaginationProvider, PaginationListStandalone } from
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
class Table extends React.Component { class Table extends React.Component {
state = { products }
loadData = () => {
this.setState({ products: productsGenerator(17) });
}
render() { render() {
const options = { const options = {
custom: true, custom: true,
@@ -38,70 +44,84 @@ class Table extends React.Component {
firstPageTitle: 'Next page', firstPageTitle: 'Next page',
lastPageTitle: 'Last page', lastPageTitle: 'Last page',
showTotal: true, showTotal: true,
totalSize: products.length totalSize: this.state.products.length
};
const contentTable = ({ paginationProps, paginationTableProps }) => (
<div>
<PaginationListStandalone { ...paginationProps } />
<div>
<div>
<BootstrapTable
striped
hover
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
cellEdit={ cellEditFactory() }
{ ...paginationTableProps }
/>
</div>
</div>
<PaginationListStandalone { ...paginationProps } />
</div>
);
return (
<div>
<h2>PaginationProvider will care the data size change. You dont do anything</h2>
<PaginationProvider
pagination={
paginationFactory(options)
}
>
{ contentTable }
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div >
);
}
}
`;
export default class Table extends React.Component {
render() {
const options = {
custom: true,
paginationSize: 4,
pageStartIndex: 1,
firstPageText: 'First',
prePageText: 'Back',
nextPageText: 'Next',
lastPageText: 'Last',
nextPageTitle: 'First page',
prePageTitle: 'Pre page',
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
showTotal: true,
totalSize: products.length
}; };
const contentTable = ({ paginationProps, paginationTableProps }) => ( const contentTable = ({ paginationProps, paginationTableProps }) => (
<div> <div>
<button className="btn btn-default" onClick={ this.loadData }>Load Another Data</button>
<PaginationListStandalone { ...paginationProps } /> <PaginationListStandalone { ...paginationProps } />
<ToolkitProvider <ToolkitProvider
keyField="id" keyField="id"
columns={ columns } columns={ columns }
data={ products } data={ this.state.products }
search
>
{
toolkitprops => (
<div>
<SearchBar { ...toolkitprops.searchProps } />
<BootstrapTable
striped
hover
{ ...toolkitprops.baseProps }
{ ...paginationTableProps }
/>
</div>
)
}
</ToolkitProvider>
<PaginationListStandalone { ...paginationProps } />
</div>
);
return (
<div>
<h2>PaginationProvider will care the data size change. You dont do anything</h2>
<PaginationProvider
pagination={
paginationFactory(options)
}
>
{ contentTable }
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div >
);
}
}
`;
export default class Table extends React.Component {
state = { products }
loadData = () => {
this.setState({ products: productsGenerator(17) });
}
render() {
const options = {
custom: true,
paginationSize: 4,
pageStartIndex: 1,
firstPageText: 'First',
prePageText: 'Back',
nextPageText: 'Next',
lastPageText: 'Last',
nextPageTitle: 'First page',
prePageTitle: 'Pre page',
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
showTotal: true,
totalSize: this.state.products.length
};
const contentTable = ({ paginationProps, paginationTableProps }) => (
<div>
<button className="btn btn-default" onClick={ this.loadData }>Load Another Data</button>
<PaginationListStandalone { ...paginationProps } />
<ToolkitProvider
keyField="id"
columns={ columns }
data={ this.state.products }
search search
> >
{ {

View File

@@ -3,28 +3,70 @@ import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next'; import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator'; import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; import filterFactory, { textFilter, selectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block'; import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common'; import { productsQualityGenerator } from 'utils/common';
const products = productsGenerator(21); const products = productsQualityGenerator(21);
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{ const columns = [{
dataField: 'id', dataField: 'id',
text: 'Product ID', text: 'Product ID'
filter: textFilter({})
}, { }, {
dataField: 'name', dataField: 'name',
text: 'Product Name', text: 'Product Name',
filter: textFilter() filter: textFilter()
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: selectFilter({
options: selectOptions,
defaultValue: 0
})
}]; }];
const sourceCode = `\ const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next'; import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator'; import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; import filterFactory, { textFilter, selectFilter } from 'react-bootstrap-table2-filter';
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: selectFilter({
options: selectOptions,
defaultValue: 0
})
}];
class Table extends React.Component { class Table extends React.Component {
state = { products }
loadData = () => {
this.setState({ products: productsQualityGenerator(40, 7) });
}
render() { render() {
const options = { const options = {
custom: true, custom: true,
@@ -39,10 +81,11 @@ class Table extends React.Component {
firstPageTitle: 'Next page', firstPageTitle: 'Next page',
lastPageTitle: 'Last page', lastPageTitle: 'Last page',
showTotal: true, showTotal: true,
totalSize: products.length totalSize: this.state.products.length
}; };
const contentTable = ({ paginationProps, paginationTableProps }) => ( const contentTable = ({ paginationProps, paginationTableProps }) => (
<div> <div>
<button className="btn btn-default" onClick={ this.loadData }>Load Another Data</button>
<PaginationListStandalone { ...paginationProps } /> <PaginationListStandalone { ...paginationProps } />
<div> <div>
<div> <div>
@@ -50,10 +93,9 @@ class Table extends React.Component {
striped striped
hover hover
keyField="id" keyField="id"
data={ products } data={ this.state.products }
columns={ columns } columns={ columns }
filter={ filterFactory() } filter={ filterFactory() }
cellEdit={ cellEditFactory() }
{ ...paginationTableProps } { ...paginationTableProps }
/> />
</div> </div>
@@ -72,7 +114,6 @@ class Table extends React.Component {
> >
{ contentTable } { contentTable }
</PaginationProvider> </PaginationProvider>
<Code>{ sourceCode }</Code>
</div > </div >
); );
} }
@@ -80,6 +121,12 @@ class Table extends React.Component {
`; `;
export default class Table extends React.Component { export default class Table extends React.Component {
state = { products }
loadData = () => {
this.setState({ products: productsQualityGenerator(40, 7) });
}
render() { render() {
const options = { const options = {
custom: true, custom: true,
@@ -94,10 +141,11 @@ export default class Table extends React.Component {
firstPageTitle: 'Next page', firstPageTitle: 'Next page',
lastPageTitle: 'Last page', lastPageTitle: 'Last page',
showTotal: true, showTotal: true,
totalSize: products.length totalSize: this.state.products.length
}; };
const contentTable = ({ paginationProps, paginationTableProps }) => ( const contentTable = ({ paginationProps, paginationTableProps }) => (
<div> <div>
<button className="btn btn-default" onClick={ this.loadData }>Load Another Data</button>
<PaginationListStandalone { ...paginationProps } /> <PaginationListStandalone { ...paginationProps } />
<div> <div>
<div> <div>
@@ -105,7 +153,7 @@ export default class Table extends React.Component {
striped striped
hover hover
keyField="id" keyField="id"
data={ products } data={ this.state.products }
columns={ columns } columns={ columns }
filter={ filterFactory() } filter={ filterFactory() }
{ ...paginationTableProps } { ...paginationTableProps }

View File

@@ -0,0 +1,150 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
import Code from 'components/common/code-block';
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
class BookList extends React.Component {
state = {
books: [
{ id: '1', name: 'Book 1' },
{ id: '2', name: 'Book 2' },
{ id: '3', name: 'Book 3' },
{ id: '4', name: 'Book 4' },
{ id: '5', name: 'Book 5' },
{ id: '6', name: 'Book 6' }
]
};
deleteBookWithId = () => {
const lastOneId = this.state.books.length;
const updatedBooks = this.state.books.filter(m => m.id !== lastOneId.toString());
this.setState({ books: updatedBooks });
};
addBook = () => {
const lastOneId = this.state.books.length + 1;
this.setState({ books: [...this.state.books, {
id: \`$\{lastOneId}\`, name: \`Book $\{lastOneId}\`
}] });
}
render() {
const options = {
// pageStartIndex: 0,
sizePerPage: 5,
hideSizePerPage: true,
hidePageListOnlyOnePage: true
};
const columns = [
{
dataField: 'id',
text: 'Product ID',
Cell: row => (
<div>
<span title={ row.value }>{ row.value }</span>
</div>
)
},
{
dataField: 'name',
text: 'Product Name'
}
];
return (
<React.Fragment>
<BootstrapTable
keyField="id"
data={ this.state.books }
columns={ columns }
pagination={ paginationFactory(options) }
/>
<button className="btn btn-default" onClick={ () => this.deleteBookWithId() }>
delete last one book
</button>
<button className="btn btn-default" onClick={ () => this.addBook() }>
Add a book to the end
</button>
<Code>{ sourceCode }</Code>
</React.Fragment>
);
}
`;
export default class BookList extends React.Component {
state = {
books: [
{ id: '1', name: 'Book 1' },
{ id: '2', name: 'Book 2' },
{ id: '3', name: 'Book 3' },
{ id: '4', name: 'Book 4' },
{ id: '5', name: 'Book 5' },
{ id: '6', name: 'Book 6' },
{ id: '7', name: 'Book 6' },
{ id: '8', name: 'Book 6' },
{ id: '9', name: 'Book 6' },
{ id: '10', name: 'Book 6' },
{ id: '11', name: 'Book 6' }
]
};
deleteBookWithId = () => {
const lastOneId = this.state.books.length;
const updatedBooks = this.state.books.filter(m => m.id !== lastOneId.toString());
this.setState({ books: updatedBooks });
};
addBook = () => {
const lastOneId = this.state.books.length + 1;
this.setState({ books: [...this.state.books, {
id: `${lastOneId}`, name: `Book ${lastOneId}`
}] });
}
render() {
const options = {
// pageStartIndex: 0,
sizePerPage: 5,
hideSizePerPage: true,
hidePageListOnlyOnePage: true
};
const columns = [
{
dataField: 'id',
text: 'Product ID',
Cell: row => (
<div>
<span title={ row.value }>{ row.value }</span>
</div>
)
},
{
dataField: 'name',
text: 'Product Name'
}
];
return (
<React.Fragment>
<BootstrapTable
keyField="id"
data={ this.state.books }
columns={ columns }
pagination={ paginationFactory(options) }
/>
<button className="btn btn-default" onClick={ () => this.deleteBookWithId() }>
delete last one book
</button>
<button className="btn btn-default" onClick={ () => this.addBook() }>
Add a book to the end
</button>
<Code>{ sourceCode }</Code>
</React.Fragment>
);
}
}

View File

@@ -84,6 +84,7 @@ export default class StandaloneSizePerPage extends React.Component {
<div> <div>
<SizePerPageDropdownStandalone <SizePerPageDropdownStandalone
{ ...paginationProps } { ...paginationProps }
btnContextual="btn btn-primary"
/> />
<BootstrapTable <BootstrapTable
keyField="id" keyField="id"

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-example", "name": "react-bootstrap-table2-example",
"version": "1.0.19", "version": "1.0.24",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"private": true, "private": true,

View File

@@ -29,10 +29,10 @@ export const withOnSale = rows => rows.map((row) => {
return row; return row;
}); });
export const productsQualityGenerator = (quantity = 5) => export const productsQualityGenerator = (quantity = 5, factor = 0) =>
Array.from({ length: quantity }, (value, index) => ({ Array.from({ length: quantity }, (value, index) => ({
id: index, id: index + factor,
name: `Item name ${index}`, name: `Item name ${index + factor}`,
quality: index % 3 quality: index % 3
})); }));

View File

@@ -163,6 +163,7 @@ import ExpandHooks from 'examples/row-expand/expand-hooks';
// pagination // pagination
import PaginationTable from 'examples/pagination'; import PaginationTable from 'examples/pagination';
import PaginationHooksTable from 'examples/pagination/pagination-hooks'; import PaginationHooksTable from 'examples/pagination/pagination-hooks';
import PaginationWithDynamicData from 'examples/pagination/pagination-with-dynamic-data';
import CustomPaginationTable from 'examples/pagination/custom-pagination'; import CustomPaginationTable from 'examples/pagination/custom-pagination';
import CustomPageButtonTable from 'examples/pagination/custom-page-button'; import CustomPageButtonTable from 'examples/pagination/custom-page-button';
import CustomSizePerPageOptionTable from 'examples/pagination/custom-size-per-page-option'; import CustomSizePerPageOptionTable from 'examples/pagination/custom-size-per-page-option';
@@ -191,6 +192,7 @@ import CSVFormatter from 'examples/csv/csv-column-formatter';
import CustomCSVHeader from 'examples/csv/custom-csv-header'; import CustomCSVHeader from 'examples/csv/custom-csv-header';
import HideCSVColumn from 'examples/csv/hide-column'; import HideCSVColumn from 'examples/csv/hide-column';
import ExportOnlySelected from 'examples/csv/export-only-selected'; import ExportOnlySelected from 'examples/csv/export-only-selected';
import ExportOnlyFiltered from 'examples/csv/export-only-filtered';
import CSVColumnType from 'examples/csv/csv-column-type'; import CSVColumnType from 'examples/csv/csv-column-type';
import CustomCSVButton from 'examples/csv/custom-csv-button'; import CustomCSVButton from 'examples/csv/custom-csv-button';
import ExportCustomData from 'examples/csv/export-custom-data'; import ExportCustomData from 'examples/csv/export-custom-data';
@@ -215,6 +217,7 @@ import RemoteCellEdit from 'examples/remote/remote-celledit';
import RemoteAll from 'examples/remote/remote-all'; import RemoteAll from 'examples/remote/remote-all';
// data // data
import DataChangeListener from 'examples/data/data-change-listener';
import LoadDataWithFilter from 'examples/data/load-data-on-the-fly-with-filter'; import LoadDataWithFilter from 'examples/data/load-data-on-the-fly-with-filter';
import LoadDataWithDefaultFilter from 'examples/data/load-data-on-the-fly-with-default-filter'; import LoadDataWithDefaultFilter from 'examples/data/load-data-on-the-fly-with-default-filter';
import LoadDataWithSearch from 'examples/data/load-data-on-the-fly-with-search'; import LoadDataWithSearch from 'examples/data/load-data-on-the-fly-with-search';
@@ -227,6 +230,7 @@ import 'stories/stylesheet/storybook.scss';
import '../../react-bootstrap-table2/style/react-bootstrap-table2.scss'; import '../../react-bootstrap-table2/style/react-bootstrap-table2.scss';
import '../../react-bootstrap-table2-paginator/style/react-bootstrap-table2-paginator.scss'; import '../../react-bootstrap-table2-paginator/style/react-bootstrap-table2-paginator.scss';
import '../../react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss'; import '../../react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss';
import '../../react-bootstrap-table2-toolkit/style/react-bootstrap-table2-toolkit.scss';
// import bootstrap style by given version // import bootstrap style by given version
import bootstrapStyle, { BOOTSTRAP_VERSION } from './bootstrap-style'; import bootstrapStyle, { BOOTSTRAP_VERSION } from './bootstrap-style';
@@ -404,6 +408,7 @@ storiesOf('Pagination', module)
.addDecorator(bootstrapStyle()) .addDecorator(bootstrapStyle())
.add('Basic Pagination Table', () => <PaginationTable />) .add('Basic Pagination Table', () => <PaginationTable />)
.add('Pagination Hooks', () => <PaginationHooksTable />) .add('Pagination Hooks', () => <PaginationHooksTable />)
.add('Pagination with Dynamic Data', () => <PaginationWithDynamicData />)
.add('Custom Pagination', () => <CustomPaginationTable />) .add('Custom Pagination', () => <CustomPaginationTable />)
.add('Custom Page Button', () => <CustomPageButtonTable />) .add('Custom Page Button', () => <CustomPageButtonTable />)
.add('Custom Page List', () => <CustomPageListTable />) .add('Custom Page List', () => <CustomPageListTable />)
@@ -441,6 +446,7 @@ storiesOf('Export CSV', module)
.add('Custom CSV Header', () => <CustomCSVHeader />) .add('Custom CSV Header', () => <CustomCSVHeader />)
.add('Hide CSV Column', () => <HideCSVColumn />) .add('Hide CSV Column', () => <HideCSVColumn />)
.add('Only Export Selected Rows', () => <ExportOnlySelected />) .add('Only Export Selected Rows', () => <ExportOnlySelected />)
.add('Only Export Filtered/Searched Rows', () => <ExportOnlyFiltered />)
.add('CSV Column Type', () => <CSVColumnType />) .add('CSV Column Type', () => <CSVColumnType />)
.add('Custom CSV Button', () => <CustomCSVButton />) .add('Custom CSV Button', () => <CustomCSVButton />)
.add('Export Custom Data', () => <ExportCustomData />) .add('Export Custom Data', () => <ExportCustomData />)
@@ -462,6 +468,7 @@ storiesOf('Remote', module)
storiesOf('Data', module) storiesOf('Data', module)
.addDecorator(bootstrapStyle()) .addDecorator(bootstrapStyle())
.add('Data Change Listener', () => <DataChangeListener />)
.add('Load data with Filter', () => <LoadDataWithFilter />) .add('Load data with Filter', () => <LoadDataWithFilter />)
.add('Load data with Default Filter', () => <LoadDataWithDefaultFilter />) .add('Load data with Default Filter', () => <LoadDataWithDefaultFilter />)
.add('Load data with Search', () => <LoadDataWithSearch />) .add('Load data with Search', () => <LoadDataWithSearch />)

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-filter", "name": "react-bootstrap-table2-filter",
"version": "1.1.5", "version": "1.1.9",
"description": "it's a column filter addon for react-bootstrap-table2", "description": "it's a column filter addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -18,7 +18,7 @@ const legalComparators = [
]; ];
function dateParser(d) { function dateParser(d) {
return `${d.getFullYear()}-${('0' + (d.getMonth() + 1)).slice(-2)}-${('0' + d.getDate()).slice(-2)}`; return `${d.getUTCFullYear()}-${('0' + (d.getUTCMonth() + 1)).slice(-2)}-${('0' + d.getUTCDate()).slice(-2)}`;
} }
class DateFilter extends Component { class DateFilter extends Component {
@@ -132,8 +132,14 @@ class DateFilter extends Component {
className={ `filter date-filter ${className}` } className={ `filter date-filter ${className}` }
style={ style } style={ style }
> >
<label
className="filter-label"
htmlFor={ `date-filter-comparator-${text}` }
>
<span className="sr-only">Filter comparator</span>
<select <select
ref={ n => this.dateFilterComparator = n } ref={ n => this.dateFilterComparator = n }
id={ `date-filter-comparator-${text}` }
style={ comparatorStyle } style={ comparatorStyle }
className={ `date-filter-comparator form-control ${comparatorClassName}` } className={ `date-filter-comparator form-control ${comparatorClassName}` }
onChange={ this.onChangeComparator } onChange={ this.onChangeComparator }
@@ -141,8 +147,12 @@ class DateFilter extends Component {
> >
{ this.getComparatorOptions() } { this.getComparatorOptions() }
</select> </select>
</label>
<label htmlFor={ `date-filter-column-${text}` }>
<span className="sr-only">Enter ${ text }</span>
<input <input
ref={ n => this.inputDate = n } ref={ n => this.inputDate = n }
id={ `date-filter-column-${text}` }
className={ `filter date-filter-input form-control ${dateClassName}` } className={ `filter date-filter-input form-control ${dateClassName}` }
style={ dateStyle } style={ dateStyle }
type="date" type="date"
@@ -150,6 +160,7 @@ class DateFilter extends Component {
placeholder={ placeholder || `Enter ${text}...` } placeholder={ placeholder || `Enter ${text}...` }
defaultValue={ this.getDefaultDate() } defaultValue={ this.getDefaultDate() }
/> />
</label>
</div> </div>
); );
} }

View File

@@ -111,9 +111,15 @@ class MultiSelectFilter extends Component {
`filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`; `filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`;
return ( return (
<label
className="filter-label"
htmlFor={ `multiselect-filter-column-${column.text}` }
>
<span className="sr-only">Filter by {column.text}</span>
<select <select
{ ...rest } { ...rest }
ref={ n => this.selectInput = n } ref={ n => this.selectInput = n }
id={ `multiselect-filter-column-${column.text}` }
style={ style } style={ style }
multiple multiple
className={ selectClass } className={ selectClass }
@@ -123,6 +129,7 @@ class MultiSelectFilter extends Component {
> >
{ this.getOptions() } { this.getOptions() }
</select> </select>
</label>
); );
} }
} }

View File

@@ -173,28 +173,45 @@ class NumberFilter extends Component {
className={ `filter number-filter ${className}` } className={ `filter number-filter ${className}` }
style={ style } style={ style }
> >
<label
className="filter-label"
htmlFor={ `number-filter-comparator-${column.text}` }
>
<span className="sr-only">Filter comparator</span>
<select <select
ref={ n => this.numberFilterComparator = n } ref={ n => this.numberFilterComparator = n }
style={ comparatorStyle } style={ comparatorStyle }
id={ `number-filter-comparator-${column.text}` }
className={ `number-filter-comparator form-control ${comparatorClassName}` } className={ `number-filter-comparator form-control ${comparatorClassName}` }
onChange={ this.onChangeComparator } onChange={ this.onChangeComparator }
defaultValue={ defaultValue ? defaultValue.comparator : '' } defaultValue={ defaultValue ? defaultValue.comparator : '' }
> >
{ this.getComparatorOptions() } { this.getComparatorOptions() }
</select> </select>
</label>
{ {
options ? options ?
<label
className="filter-label"
htmlFor={ `number-filter-column-${column.text}` }
>
<span className="sr-only">{`Select ${column.text}`}</span>
<select <select
ref={ n => this.numberFilter = n } ref={ n => this.numberFilter = n }
id={ `number-filter-column-${column.text}` }
style={ numberStyle } style={ numberStyle }
className={ selectClass } className={ selectClass }
onChange={ this.onChangeNumberSet } onChange={ this.onChangeNumberSet }
defaultValue={ defaultValue ? defaultValue.number : '' } defaultValue={ defaultValue ? defaultValue.number : '' }
> >
{ this.getNumberOptions() } { this.getNumberOptions() }
</select> : </select>
</label> :
<label htmlFor={ `number-filter-column-${column.text}` }>
<span className="sr-only">{`Enter ${column.text}`}</span>
<input <input
ref={ n => this.numberFilter = n } ref={ n => this.numberFilter = n }
id={ `number-filter-column-${column.text}` }
type="number" type="number"
style={ numberStyle } style={ numberStyle }
className={ `number-filter-input form-control ${numberClassName}` } className={ `number-filter-input form-control ${numberClassName}` }
@@ -202,6 +219,7 @@ class NumberFilter extends Component {
onChange={ this.onChangeNumber } onChange={ this.onChangeNumber }
defaultValue={ defaultValue ? defaultValue.number : '' } defaultValue={ defaultValue ? defaultValue.number : '' }
/> />
</label>
} }
</div> </div>
); );

View File

@@ -8,6 +8,7 @@ import { FILTER_TYPE } from '../const';
function optionsEquals(currOpts, prevOpts) { function optionsEquals(currOpts, prevOpts) {
if (Array.isArray(currOpts)) { if (Array.isArray(currOpts)) {
if (currOpts.length === prevOpts.length) {
for (let i = 0; i < currOpts.length; i += 1) { for (let i = 0; i < currOpts.length; i += 1) {
if ( if (
currOpts[i].value !== prevOpts[i].value || currOpts[i].value !== prevOpts[i].value ||
@@ -16,7 +17,9 @@ function optionsEquals(currOpts, prevOpts) {
return false; return false;
} }
} }
return currOpts.length === prevOpts.length; return true;
}
return false;
} }
const keys = Object.keys(currOpts); const keys = Object.keys(currOpts);
for (let i = 0; i < keys.length; i += 1) { for (let i = 0; i < keys.length; i += 1) {
@@ -136,9 +139,15 @@ class SelectFilter extends Component {
`filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`; `filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`;
return ( return (
<label
className="filter-label"
htmlFor={ `select-filter-column-${column.text}` }
>
<span className="sr-only">Filter by { column.text }</span>
<select <select
{ ...rest } { ...rest }
ref={ n => this.selectInput = n } ref={ n => this.selectInput = n }
id={ `select-filter-column-${column.text}` }
style={ style } style={ style }
className={ selectClass } className={ selectClass }
onChange={ this.filter } onChange={ this.filter }
@@ -147,6 +156,7 @@ class SelectFilter extends Component {
> >
{ this.getOptions() } { this.getOptions() }
</select> </select>
</label>
); );
} }
} }

View File

@@ -94,10 +94,16 @@ class TextFilter extends Component {
// stopPropagation for onClick event is try to prevent sort was triggered. // stopPropagation for onClick event is try to prevent sort was triggered.
return ( return (
<label
className="filter-label"
htmlFor={ `text-filter-column-${text}` }
>
<span className="sr-only">Filter by {text}</span>
<input <input
{ ...rest } { ...rest }
ref={ n => this.input = n } ref={ n => this.input = n }
type="text" type="text"
id={ `text-filter-column-${text}` }
className={ `filter text-filter form-control ${className}` } className={ `filter text-filter form-control ${className}` }
style={ style } style={ style }
onChange={ this.filter } onChange={ this.filter }
@@ -105,6 +111,7 @@ class TextFilter extends Component {
placeholder={ placeholder || `Enter ${text}...` } placeholder={ placeholder || `Enter ${text}...` }
value={ this.state.value } value={ this.state.value }
/> />
</label>
); );
} }
} }

View File

@@ -27,9 +27,8 @@ export default (
this.onFilter = this.onFilter.bind(this); this.onFilter = this.onFilter.bind(this);
this.doFilter = this.doFilter.bind(this); this.doFilter = this.doFilter.bind(this);
this.onExternalFilter = this.onExternalFilter.bind(this); this.onExternalFilter = this.onExternalFilter.bind(this);
this.state = { this.data = props.data;
data: props.data this.isEmitDataChange = false;
};
} }
componentDidMount() { componentDidMount() {
@@ -39,13 +38,12 @@ export default (
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
let nextData = nextProps.data; // let nextData = nextProps.data;
if (!isRemoteFiltering() && !_.isEqual(nextProps.data, this.state.data)) { if (!isRemoteFiltering() && !_.isEqual(nextProps.data, this.data)) {
nextData = this.doFilter(nextProps); this.doFilter(nextProps, undefined, this.isEmitDataChange);
} else {
this.data = nextProps.data;
} }
this.setState({
data: nextData
});
} }
onFilter(column, filterType, initialize = false) { onFilter(column, filterType, initialize = false) {
@@ -83,9 +81,7 @@ export default (
if (filter.props.onFilter) { if (filter.props.onFilter) {
result = filter.props.onFilter(filterVal); result = filter.props.onFilter(filterVal);
} }
this.doFilter(this.props, result);
result = this.doFilter(this.props, result);
this.setState({ data: result });
}; };
} }
@@ -95,21 +91,29 @@ export default (
}; };
} }
doFilter(props, customResult) { getFiltered() {
return this.data;
}
doFilter(props, customResult, ignoreEmitDataChange = false) {
let result = customResult; let result = customResult;
const { dataChangeListener, data, columns } = props; const { dataChangeListener, data, columns } = props;
result = result || filters(data, columns, _)(this.currFilters); result = result || filters(data, columns, _)(this.currFilters);
if (dataChangeListener) { this.data = result;
if (dataChangeListener && !ignoreEmitDataChange) {
this.isEmitDataChange = true;
dataChangeListener.emit('filterChanged', result.length); dataChangeListener.emit('filterChanged', result.length);
} else {
this.isEmitDataChange = false;
this.forceUpdate();
} }
return result;
} }
render() { render() {
return ( return (
<FilterContext.Provider value={ { <FilterContext.Provider value={ {
data: this.state.data, data: this.data,
onFilter: this.onFilter, onFilter: this.onFilter,
onExternalFilter: this.onExternalFilter onExternalFilter: this.onExternalFilter
} } } }

View File

@@ -98,9 +98,9 @@ export const filterByDate = _ => (
customFilterValue customFilterValue
) => { ) => {
if (!date || !comparator) return data; if (!date || !comparator) return data;
const filterDate = date.getDate(); const filterDate = date.getUTCDate();
const filterMonth = date.getMonth(); const filterMonth = date.getUTCMonth();
const filterYear = date.getFullYear(); const filterYear = date.getUTCFullYear();
return data.filter((row) => { return data.filter((row) => {
let valid = true; let valid = true;
@@ -114,9 +114,9 @@ export const filterByDate = _ => (
cell = new Date(cell); cell = new Date(cell);
} }
const targetDate = cell.getDate(); const targetDate = cell.getUTCDate();
const targetMonth = cell.getMonth(); const targetMonth = cell.getUTCMonth();
const targetYear = cell.getFullYear(); const targetYear = cell.getUTCFullYear();
switch (comparator) { switch (comparator) {

View File

@@ -1,3 +1,7 @@
.react-bootstrap-table > table > thead > tr > th .filter-label {
display: block !important;
}
.react-bootstrap-table > table > thead > tr > th .filter { .react-bootstrap-table > table > thead > tr > th .filter {
font-weight: normal; font-weight: normal;
} }

View File

@@ -283,9 +283,9 @@ describe('FilterContext', () => {
expect(onFilter).toHaveBeenCalledWith(filterVal); expect(onFilter).toHaveBeenCalledWith(filterVal);
}); });
it('should set state.data correctly', () => { it('should set data correctly', () => {
instance.onFilter(customColumns[1], FILTER_TYPE.TEXT)(filterVal); instance.onFilter(customColumns[1], FILTER_TYPE.TEXT)(filterVal);
expect(instance.state.data).toEqual(mockReturn); expect(instance.data).toEqual(mockReturn);
}); });
}); });

View File

@@ -190,6 +190,19 @@ import paginationFactory, {
That's it!! The benifit for using standalone is you can much easier to render the standalone component in any posistion. In the future, we will implement more featue like applying `style`, `className` etc on standalone components. That's it!! The benifit for using standalone is you can much easier to render the standalone component in any posistion. In the future, we will implement more featue like applying `style`, `className` etc on standalone components.
##### Customizable props for `PaginationListStandalone`
* N/A
##### Customizable props for `SizePerPageDropdownStandalone`
* `open`: `true` to make dropdown show.
* `hidden`: `true` to hide the size per page dropdown.
* `btnContextual`: Set the button contextual
* `variation`: Variation for dropdown, available value is `dropdown` and `dropup`.
* `className`: Custom the class on size per page dropdown
##### Customizable props for `SizePerPageDropdownStandalone`
* N/A
#### 4.2 Customization Everything #### 4.2 Customization Everything
If you choose to custom the pagination component by yourself, the `paginationProps` will be important for you. Becasue you have to know for example how to change page or what's the current page etc. Hence, following is all the props in `paginationProps` object: If you choose to custom the pagination component by yourself, the `paginationProps` will be important for you. Becasue you have to know for example how to change page or what's the current page etc. Hence, following is all the props in `paginationProps` object:

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-paginator", "name": "react-bootstrap-table2-paginator",
"version": "2.0.3", "version": "2.0.6",
"description": "it's the pagination addon for react-bootstrap-table2", "description": "it's the pagination addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -32,7 +32,12 @@ class PaginationDataProvider extends Provider {
// user should align the page when the page is not fit to the data size when remote enable // user should align the page when the page is not fit to the data size when remote enable
if (!this.isRemotePagination() && !custom) { if (!this.isRemotePagination() && !custom) {
const newPage = alignPage( const newPage = alignPage(
nextProps.data.length, this.currPage, currSizePerPage, pageStartIndex); nextProps.data.length,
this.props.data.length,
this.currPage,
currSizePerPage,
pageStartIndex
);
if (this.currPage !== newPage) { if (this.currPage !== newPage) {
if (onPageChange) { if (onPageChange) {
@@ -41,6 +46,9 @@ class PaginationDataProvider extends Provider {
this.currPage = newPage; this.currPage = newPage;
} }
} }
if (nextProps.onDataSizeChange && nextProps.data.length !== this.props.data.length) {
nextProps.onDataSizeChange({ dataSize: nextProps.data.length });
}
} }
isRemotePagination = () => this.props.isRemotePagination(); isRemotePagination = () => this.props.isRemotePagination();

View File

@@ -56,7 +56,7 @@ export default ExtendBase =>
alwaysShowAllBtns alwaysShowAllBtns
} = this.props; } = this.props;
let pages; let pages = [];
let endPage = totalPages; let endPage = totalPages;
if (endPage <= 0) return []; if (endPage <= 0) return [];
@@ -68,24 +68,42 @@ export default ExtendBase =>
startPage = endPage - paginationSize + 1; startPage = endPage - paginationSize + 1;
} }
if (startPage !== pageStartIndex && totalPages > paginationSize && withFirstAndLast) { if (alwaysShowAllBtns) {
if (withFirstAndLast) {
pages = [firstPageText, prePageText]; pages = [firstPageText, prePageText];
} else if (totalPages > 1 || alwaysShowAllBtns) {
pages = [prePageText];
} else { } else {
pages = []; pages = [prePageText];
}
}
if (startPage !== pageStartIndex &&
totalPages > paginationSize &&
withFirstAndLast &&
pages.length === 0
) {
pages = [firstPageText, prePageText];
} else if (totalPages > 1 && pages.length === 0) {
pages = [prePageText];
} }
for (let i = startPage; i <= endPage; i += 1) { for (let i = startPage; i <= endPage; i += 1) {
if (i >= pageStartIndex) pages.push(i); if (i >= pageStartIndex) pages.push(i);
} }
if (endPage <= lastPage && pages.length > 1) { if (alwaysShowAllBtns || (endPage <= lastPage && pages.length > 1)) {
pages.push(nextPageText); pages.push(nextPageText);
} }
if (endPage !== lastPage && withFirstAndLast) { if ((endPage !== lastPage && withFirstAndLast) || (withFirstAndLast && alwaysShowAllBtns)) {
pages.push(lastPageText); pages.push(lastPageText);
} }
// if ((endPage <= lastPage && pages.length > 1) || alwaysShowAllBtns) {
// pages.push(nextPageText);
// }
// if (endPage !== lastPage && withFirstAndLast) {
// pages.push(lastPageText);
// }
return pages; return pages;
} }

View File

@@ -1,3 +1,5 @@
import Const from './const';
const getNormalizedPage = ( const getNormalizedPage = (
page, page,
pageStartIndex pageStartIndex
@@ -19,12 +21,20 @@ const startIndex = (
export const alignPage = ( export const alignPage = (
dataSize, dataSize,
prevDataSize,
page, page,
sizePerPage, sizePerPage,
pageStartIndex pageStartIndex
) => { ) => {
if (page < pageStartIndex || page > (Math.floor(dataSize / sizePerPage) + pageStartIndex)) { if (prevDataSize < dataSize) return page;
return pageStartIndex; if (page < pageStartIndex) return pageStartIndex;
if (dataSize <= 0) return pageStartIndex;
if ((page >= (Math.floor(dataSize / sizePerPage) + pageStartIndex)) && pageStartIndex === 1) {
return Math.ceil(dataSize / sizePerPage);
}
if (page >= Math.floor(dataSize / sizePerPage) && pageStartIndex === 0) {
const newPage = Math.ceil(dataSize / sizePerPage);
return newPage - Math.abs((Const.PAGE_START_INDEX - pageStartIndex));
} }
return page; return page;
}; };

View File

@@ -48,6 +48,7 @@ const sizePerPageDropdownAdapter = WrappedComponent =>
} }
return ( return (
<WrappedComponent <WrappedComponent
{ ...this.props }
currSizePerPage={ `${currSizePerPage}` } currSizePerPage={ `${currSizePerPage}` }
options={ this.calculateSizePerPageStatus() } options={ this.calculateSizePerPageStatus() }
optionRenderer={ sizePerPageOptionRenderer } optionRenderer={ sizePerPageOptionRenderer }

View File

@@ -50,11 +50,17 @@ class StateProvider extends React.Component {
// user should align the page when the page is not fit to the data size when remote enable // user should align the page when the page is not fit to the data size when remote enable
if (this.isRemotePagination() || custom) { if (this.isRemotePagination() || custom) {
if (typeof nextProps.pagination.options.page !== 'undefined') {
this.currPage = nextProps.pagination.options.page; this.currPage = nextProps.pagination.options.page;
}
if (typeof nextProps.pagination.options.sizePerPage !== 'undefined') {
this.currSizePerPage = nextProps.pagination.options.sizePerPage; this.currSizePerPage = nextProps.pagination.options.sizePerPage;
}
if (typeof nextProps.pagination.options.totalSize !== 'undefined') {
this.dataSize = nextProps.pagination.options.totalSize; this.dataSize = nextProps.pagination.options.totalSize;
} }
} }
}
getPaginationProps = () => { getPaginationProps = () => {
const { pagination: { options }, bootstrap4 } = this.props; const { pagination: { options }, bootstrap4 } = this.props;
@@ -117,13 +123,14 @@ class StateProvider extends React.Component {
const { pagination: { options } } = this.props; const { pagination: { options } } = this.props;
const pageStartIndex = typeof options.pageStartIndex === 'undefined' ? const pageStartIndex = typeof options.pageStartIndex === 'undefined' ?
Const.PAGE_START_INDEX : options.pageStartIndex; Const.PAGE_START_INDEX : options.pageStartIndex;
this.dataSize = newDataSize;
this.currPage = alignPage( this.currPage = alignPage(
newDataSize, newDataSize,
this.dataSize,
this.currPage, this.currPage,
this.currSizePerPage, this.currSizePerPage,
pageStartIndex pageStartIndex
); );
this.dataSize = newDataSize;
this.forceUpdate(); this.forceUpdate();
} }

View File

@@ -43,18 +43,132 @@ describe('Page Functions', () => {
}); });
describe('alignPage', () => { describe('alignPage', () => {
const pageStartIndex = 1; let newDataSize;
const sizePerPage = 10; let prevDataSize;
const page = 3; let currPage;
describe('if the page does not fit the pages which calculated from the length of data', () => { let pageStartIndex;
it('should return pageStartIndex argument', () => { let sizePerPage;
expect(alignPage(15, page, sizePerPage, pageStartIndex)).toEqual(pageStartIndex);
describe('if prevDataSize < newDataSize', () => {
beforeEach(() => {
newDataSize = 10;
prevDataSize = 6;
currPage = 2;
pageStartIndex = 1;
sizePerPage = 5;
});
it('should return same page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(currPage);
}); });
}); });
describe('if the length of store.data is large than the end page index', () => { describe('if currPage < newDataSize', () => {
it('should return current page', () => { beforeEach(() => {
expect(alignPage(30, page, sizePerPage, pageStartIndex)).toEqual(page); newDataSize = 10;
prevDataSize = 12;
currPage = 0;
pageStartIndex = 1;
sizePerPage = 5;
});
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(pageStartIndex);
});
});
describe('if partStartIndex is default 1', () => {
describe('and currPage is bigger than newest last page', () => {
beforeEach(() => {
newDataSize = 9;
prevDataSize = 12;
currPage = 3;
pageStartIndex = 1;
sizePerPage = 5;
});
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(2);
});
});
describe('and currPage is short than newest last page', () => {
beforeEach(() => {
newDataSize = 11;
prevDataSize = 12;
currPage = 3;
pageStartIndex = 1;
sizePerPage = 5;
});
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(currPage);
});
});
});
describe('if partStartIndex is default 0', () => {
describe('and currPage is bigger than newest last page', () => {
beforeEach(() => {
newDataSize = 8;
prevDataSize = 11;
currPage = 2;
pageStartIndex = 0;
sizePerPage = 5;
});
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(1);
});
});
describe('and currPage is short than newest last page', () => {
beforeEach(() => {
newDataSize = 11;
prevDataSize = 12;
currPage = 2;
pageStartIndex = 0;
sizePerPage = 5;
});
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(currPage);
});
}); });
}); });
}); });

View File

@@ -67,6 +67,19 @@ const { SearchBar } = Search;
3. You should render `SearchBar` with `searchProps` as well. The position of `SearchBar` is depends on you. 3. You should render `SearchBar` with `searchProps` as well. The position of `SearchBar` is depends on you.
### `SearchBar` Props
#### className - [string]
Custom the class on input element.
#### placeholder - [string]
Custom the placeholder on input element.
#### style - [object]
Custom the style on input element.
#### delay = [number]
milionsecond for debounce user input.
### Search Options ### Search Options
#### defaultSearch - [string] #### defaultSearch - [string]
@@ -127,6 +140,8 @@ const { SearchBar, ClearSearchButton } = Search;
</ToolkitProvider> </ToolkitProvider>
``` ```
-----
## Export CSV ## Export CSV
There are two steps to enable the export CSV functionality: There are two steps to enable the export CSV functionality:
@@ -176,6 +191,13 @@ Default is `true`. `false` will only export current data which display on table.
#### onlyExportSelection - [bool] #### onlyExportSelection - [bool]
Default is `false`. `true` will only export the data which is selected. Default is `false`. `true` will only export the data which is selected.
#### onlyExportFiltered - [bool]
Default is `false`. `true` will only export the data which is filtered/searched.
>> When you configure this prop as true, you must turn off `exportAll`.
-----
## Column Toggle ## Column Toggle
Let's see how to render the column toggle in your react component: Let's see how to render the column toggle in your react component:

View File

@@ -30,6 +30,7 @@ class ToolkitProvider extends statelessDecorator(React.Component) {
ignoreHeader: PropTypes.bool, ignoreHeader: PropTypes.bool,
noAutoBOM: PropTypes.bool, noAutoBOM: PropTypes.bool,
exportAll: PropTypes.bool, exportAll: PropTypes.bool,
onlyExportFiltered: PropTypes.bool,
onlyExportSelection: PropTypes.bool onlyExportSelection: PropTypes.bool
}) })
]) ])

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-toolkit", "name": "react-bootstrap-table2-toolkit",
"version": "1.3.1", "version": "1.4.1",
"description": "The toolkit for react-bootstrap-table2", "description": "The toolkit for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -27,19 +27,24 @@ export default Base =>
data = source; data = source;
} else if (options.exportAll) { } else if (options.exportAll) {
data = this.props.data; data = this.props.data;
} else if (options.onlyExportFiltered) {
const payload = {};
this.tableExposedAPIEmitter.emit('get.filtered.rows', payload);
data = payload.result;
} else { } else {
const payload = {}; const payload = {};
this.tableExposedAPIEmitter.emit('get.table.data', payload); this.tableExposedAPIEmitter.emit('get.table.data', payload);
data = payload.result; data = payload.result;
} }
// filter data // filter data by row selection
if (options.onlyExportSelection) { if (options.onlyExportSelection) {
const payload = {}; const payload = {};
this.tableExposedAPIEmitter.emit('get.selected.rows', payload); this.tableExposedAPIEmitter.emit('get.selected.rows', payload);
const selections = payload.result; const selections = payload.result;
data = data.filter(row => !!selections.find(sel => row[keyField] === sel)); data = data.filter(row => !!selections.find(sel => row[keyField] === sel));
} }
const content = transform(data, meta, this._.get, options); const content = transform(data, meta, this._.get, options);
save(content, options); save(content, options);
} }

View File

@@ -54,12 +54,19 @@ class SearchBar extends React.Component {
const { const {
className, className,
style, style,
placeholder placeholder,
tableId
} = this.props; } = this.props;
return ( return (
<label
htmlFor={ `search-bar-${tableId}` }
className="search-label"
>
<span className="sr-only">Search this table</span>
<input <input
ref={ n => this.input = n } ref={ n => this.input = n }
id={ `search-bar-${tableId}` }
type="text" type="text"
style={ style } style={ style }
onKeyUp={ () => this.onKeyup() } onKeyUp={ () => this.onKeyup() }
@@ -68,6 +75,7 @@ class SearchBar extends React.Component {
value={ this.state.value } value={ this.state.value }
placeholder={ placeholder || SearchBar.defaultProps.placeholder } placeholder={ placeholder || SearchBar.defaultProps.placeholder }
/> />
</label>
); );
} }
} }
@@ -78,7 +86,8 @@ SearchBar.propTypes = {
placeholder: PropTypes.string, placeholder: PropTypes.string,
style: PropTypes.object, style: PropTypes.object,
delay: PropTypes.number, delay: PropTypes.number,
searchText: PropTypes.string searchText: PropTypes.string,
tableId: PropTypes.string
}; };
SearchBar.defaultProps = { SearchBar.defaultProps = {
@@ -86,7 +95,8 @@ SearchBar.defaultProps = {
style: {}, style: {},
placeholder: 'Search', placeholder: 'Search',
delay: 250, delay: 250,
searchText: '' searchText: '',
tableId: 0
}; };
export default SearchBar; export default SearchBar;

View File

@@ -59,6 +59,10 @@ export default (options = {
} }
} }
getSearched() {
return this.state.data;
}
triggerListener(result) { triggerListener(result) {
if (this.props.dataChangeListener) { if (this.props.dataChangeListener) {
this.props.dataChangeListener.emit('filterChanged', result.length); this.props.dataChangeListener.emit('filterChanged', result.length);

View File

@@ -0,0 +1,3 @@
.search-label {
display: block !important;
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table-next", "name": "react-bootstrap-table-next",
"version": "3.0.0", "version": "3.1.1",
"description": "Next generation of react-bootstrap-table", "description": "Next generation of react-bootstrap-table",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {
@@ -37,6 +37,7 @@
], ],
"dependencies": { "dependencies": {
"classnames": "2.2.5", "classnames": "2.2.5",
"react-transition-group": "2.5.3",
"underscore": "1.9.1" "underscore": "1.9.1"
}, },
"peerDependencies": { "peerDependencies": {

View File

@@ -17,7 +17,6 @@ class Body extends React.Component {
super(props); super(props);
const { const {
keyField, keyField,
visibleColumnSize,
cellEdit, cellEdit,
selectRow, selectRow,
expandRow expandRow
@@ -34,7 +33,7 @@ class Body extends React.Component {
const expandRowEnabled = !!expandRow.renderer; const expandRowEnabled = !!expandRow.renderer;
if (expandRowEnabled) { if (expandRowEnabled) {
RowComponent = withRowExpansion(RowAggregator, visibleColumnSize); RowComponent = withRowExpansion(RowAggregator);
} }
if (selectRowEnabled) { if (selectRowEnabled) {

View File

@@ -18,6 +18,14 @@ class BootstrapTable extends PropsBaseResolver(Component) {
this.validateProps(); this.validateProps();
} }
componentWillReceiveProps(nextProps) {
if (nextProps.onDataSizeChange && !nextProps.pagination) {
if (nextProps.data.length !== this.props.data.length) {
nextProps.onDataSizeChange({ dataSize: nextProps.data.length });
}
}
}
// Exposed APIs // Exposed APIs
getData = () => { getData = () => {
return this.visibleRows(); return this.visibleRows();
@@ -192,6 +200,7 @@ BootstrapTable.propTypes = {
onSort: PropTypes.func, onSort: PropTypes.func,
onFilter: PropTypes.func, onFilter: PropTypes.func,
onExternalFilter: PropTypes.func, onExternalFilter: PropTypes.func,
onDataSizeChange: PropTypes.func,
// Inject from toolkit // Inject from toolkit
search: PropTypes.shape({ search: PropTypes.shape({
searchText: PropTypes.string, searchText: PropTypes.string,

View File

@@ -26,7 +26,7 @@ class Cell extends eventDelegater(Component) {
// if (nextProps.formatter) // if (nextProps.formatter)
shouldUpdate = shouldUpdate =
nextProps.column.formatter ? !_.isEqual(this.props.row, nextProps.row) : false || (nextProps.column.formatter ? !_.isEqual(this.props.row, nextProps.row) : false) ||
this.props.column.hidden !== nextProps.column.hidden || this.props.column.hidden !== nextProps.column.hidden ||
this.props.rowIndex !== nextProps.rowIndex || this.props.rowIndex !== nextProps.rowIndex ||
this.props.columnIndex !== nextProps.columnIndex || this.props.columnIndex !== nextProps.columnIndex ||

View File

@@ -23,6 +23,15 @@ const withContext = Base =>
const exposedAPIEmitter = new EventEmitter(); const exposedAPIEmitter = new EventEmitter();
exposedAPIEmitter.on('get.table.data', payload => payload.result = this.table.getData()); exposedAPIEmitter.on('get.table.data', payload => payload.result = this.table.getData());
exposedAPIEmitter.on('get.selected.rows', payload => payload.result = this.selectionContext.getSelected()); exposedAPIEmitter.on('get.selected.rows', payload => payload.result = this.selectionContext.getSelected());
exposedAPIEmitter.on('get.filtered.rows', (payload) => {
if (this.searchContext) {
payload.result = this.searchContext.getSearched();
} else if (this.filterContext) {
payload.result = this.filterContext.getFiltered();
} else {
payload.result = this.table.getData();
}
});
props.registerExposedAPI(exposedAPIEmitter); props.registerExposedAPI(exposedAPIEmitter);
} }
@@ -202,6 +211,7 @@ const withContext = Base =>
bootstrap4={ this.props.bootstrap4 } bootstrap4={ this.props.bootstrap4 }
isRemotePagination={ this.isRemotePagination } isRemotePagination={ this.isRemotePagination }
remoteEmitter={ this.remoteEmitter } remoteEmitter={ this.remoteEmitter }
onDataSizeChange={ this.props.onDataSizeChange }
> >
<this.PaginationContext.Consumer> <this.PaginationContext.Consumer>
{ {

View File

@@ -10,18 +10,35 @@ class RowExpandProvider extends React.Component {
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
data: PropTypes.array.isRequired, data: PropTypes.array.isRequired,
keyField: PropTypes.string.isRequired keyField: PropTypes.string.isRequired
} };
state = { expanded: this.props.expandRow.expanded || [] }; state = { expanded: this.props.expandRow.expanded || [],
isClosing: this.props.expandRow.isClosing || [] };
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (nextProps.expandRow) { if (nextProps.expandRow) {
const nextExpanded = nextProps.expandRow.expanded || this.state.expanded;
const isClosing = this.state.expanded.reduce((acc, cur) => {
if (!nextExpanded.includes(cur)) {
acc.push(cur);
}
return acc;
}, []);
this.setState(() => ({ this.setState(() => ({
expanded: nextProps.expandRow.expanded || this.state.expanded expanded: nextExpanded,
isClosing
}));
} else {
this.setState(() => ({
expanded: this.state.expanded
})); }));
} }
} }
onClosed = (closedRow) => {
this.setState({ isClosing: this.state.isClosing.filter(value => value !== closedRow) });
};
handleRowExpand = (rowKey, expanded, rowIndex, e) => { handleRowExpand = (rowKey, expanded, rowIndex, e) => {
const { data, keyField, expandRow: { onExpand, onlyOneExpanding, nonExpandable } } = this.props; const { data, keyField, expandRow: { onExpand, onlyOneExpanding, nonExpandable } } = this.props;
if (nonExpandable && nonExpandable.includes(rowKey)) { if (nonExpandable && nonExpandable.includes(rowKey)) {
@@ -29,11 +46,15 @@ class RowExpandProvider extends React.Component {
} }
let currExpanded = [...this.state.expanded]; let currExpanded = [...this.state.expanded];
let isClosing = [...this.state.isClosing];
if (expanded) { if (expanded) {
if (onlyOneExpanding) currExpanded = [rowKey]; if (onlyOneExpanding) {
else currExpanded.push(rowKey); isClosing = isClosing.concat(currExpanded);
currExpanded = [rowKey];
} else currExpanded.push(rowKey);
} else { } else {
isClosing.push(rowKey);
currExpanded = currExpanded.filter(value => value !== rowKey); currExpanded = currExpanded.filter(value => value !== rowKey);
} }
@@ -41,8 +62,8 @@ class RowExpandProvider extends React.Component {
const row = dataOperator.getRowByRowId(data, keyField, rowKey); const row = dataOperator.getRowByRowId(data, keyField, rowKey);
onExpand(row, expanded, rowIndex, e); onExpand(row, expanded, rowIndex, e);
} }
this.setState(() => ({ expanded: currExpanded })); this.setState(() => ({ expanded: currExpanded, isClosing }));
} };
handleAllRowExpand = (e, expandAll) => { handleAllRowExpand = (e, expandAll) => {
const { const {
@@ -68,7 +89,7 @@ class RowExpandProvider extends React.Component {
} }
this.setState(() => ({ expanded: currExpanded })); this.setState(() => ({ expanded: currExpanded }));
} };
render() { render() {
const { data, keyField } = this.props; const { data, keyField } = this.props;
@@ -78,6 +99,8 @@ class RowExpandProvider extends React.Component {
...this.props.expandRow, ...this.props.expandRow,
nonExpandable: this.props.expandRow.nonExpandable, nonExpandable: this.props.expandRow.nonExpandable,
expanded: this.state.expanded, expanded: this.state.expanded,
isClosing: this.state.isClosing,
onClosed: this.onClosed,
isAnyExpands: dataOperator.isAnyExpands(data, keyField, this.state.expanded), isAnyExpands: dataOperator.isAnyExpands(data, keyField, this.state.expanded),
onRowExpand: this.handleRowExpand, onRowExpand: this.handleRowExpand,
onAllRowExpand: this.handleAllRowExpand onAllRowExpand: this.handleAllRowExpand

View File

@@ -1,18 +1,37 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { CSSTransition } from 'react-transition-group';
const ExpandRow = ({ children, ...rest }) => ( const ExpandRow = ({ children, expanded, onClosed, ...rest }) => (
<tr className="expanding-row"> <tr>
<td { ...rest }>{ children }</td> <td className="reset-expansion-style" { ...rest }>
<CSSTransition
appear
in={ expanded }
timeout={ 400 }
classNames="row-expand-slide"
onExited={ onClosed }
>
<div>
<div className="row-expansion-style">
{ children }
</div>
</div>
</CSSTransition>
</td>
</tr> </tr>
); );
ExpandRow.propTypes = { ExpandRow.propTypes = {
children: PropTypes.node children: PropTypes.node,
expanded: PropTypes.bool,
onClosed: PropTypes.func
}; };
ExpandRow.defaultProps = { ExpandRow.defaultProps = {
children: null children: null,
expanded: false,
onClosed: null
}; };
export default ExpandRow; export default ExpandRow;

View File

@@ -3,13 +3,13 @@ import React from 'react';
import ExpandRow from './expand-row'; import ExpandRow from './expand-row';
import ExpansionContext from '../contexts/row-expand-context'; import ExpansionContext from '../contexts/row-expand-context';
export default (Component, visibleColumnSize) => { export default (Component) => {
const renderWithExpansion = (props, expandRow) => { const renderWithExpansion = (props, expandRow) => {
const key = props.value; const key = props.value;
const expanded = expandRow.expanded.includes(key); const expanded = expandRow.expanded.includes(key);
const isClosing = expandRow.isClosing.includes(key);
const expandable = !expandRow.nonExpandable || !expandRow.nonExpandable.includes(key); const expandable = !expandRow.nonExpandable || !expandRow.nonExpandable.includes(key);
return [ return [
<Component <Component
{ ...props } { ...props }
@@ -18,9 +18,11 @@ export default (Component, visibleColumnSize) => {
expandable={ expandable } expandable={ expandable }
expandRow={ { ...expandRow } } expandRow={ { ...expandRow } }
/>, />,
expanded ? <ExpandRow expanded || isClosing ? <ExpandRow
key={ `${key}-expanding` } key={ `${key}-expanding` }
colSpan={ visibleColumnSize } colSpan={ props.visibleColumnSize }
expanded={ expanded }
onClosed={ () => expandRow.onClosed(key) }
> >
{ expandRow.renderer(props.row) } { expandRow.renderer(props.row) }
</ExpandRow> : null </ExpandRow> : null

View File

@@ -8,7 +8,8 @@ export default ExtendBase =>
return ( return (
nextProps.editingRowIdx === nextProps.rowIndex || nextProps.editingRowIdx === nextProps.rowIndex ||
(this.props.editingRowIdx === nextProps.rowIndex && (this.props.editingRowIdx === nextProps.rowIndex &&
nextProps.editingRowIdx === null) nextProps.editingRowIdx === null) ||
this.props.editingRowIdx === nextProps.rowIndex
); );
} }
@@ -26,7 +27,7 @@ export default ExtendBase =>
return true; return true;
} }
for (let i = 0; i < this.props.columns.length; i += 1) { for (let i = 0; i < this.props.columns.length; i += 1) {
if (this.props.columns[i].hidden !== nextProps.columns[i].hidden) { if (!_.isEqual(this.props.columns[i], nextProps.columns[i])) {
return true; return true;
} }
} }

View File

@@ -68,10 +68,6 @@
text-align: center; text-align: center;
} }
tr.expanding-row {
padding: 5px;
}
td.react-bootstrap-table-editing-cell { td.react-bootstrap-table-editing-cell {
.animated { .animated {
animation-fill-mode: both; animation-fill-mode: both;
@@ -161,4 +157,26 @@
animation-name: bounceOut; animation-name: bounceOut;
} }
} }
.reset-expansion-style{
padding: 0;
}
.row-expansion-style{
padding: 8px;
}
.row-expand-slide-appear{
max-height: 0;
overflow: hidden;
}
.row-expand-slide-appear-active{
max-height: 1000px;
transition: max-height 3s linear;
}
.row-expand-slide-exit{
max-height: 1000px;
}
.row-expand-slide-exit-active{
max-height: 0;
overflow: hidden;
transition: max-height 400ms cubic-bezier(0, 0.95, 0, 0.95)
}
} }