63
app/routes/Tables/ExtendedTable/ExtendedTable.js
Executable file
63
app/routes/Tables/ExtendedTable/ExtendedTable.js
Executable file
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import { Container, Row, Col } from './../../../components';
|
||||
|
||||
import {
|
||||
AdvancedTableA,
|
||||
AdvancedTableB,
|
||||
BasicTable,
|
||||
BorderedTable,
|
||||
CellEdit,
|
||||
ClearSearch,
|
||||
LargeTable,
|
||||
SortTable
|
||||
} from './components';
|
||||
import { HeaderMain } from "../../components/HeaderMain";
|
||||
|
||||
export const ExtendedTable = () => (
|
||||
<Container>
|
||||
<HeaderMain
|
||||
title="Extended Tables"
|
||||
className="mb-5 mt-4"
|
||||
/>
|
||||
<Row className="mb-5">
|
||||
<Col>
|
||||
<AdvancedTableA />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mb-5">
|
||||
<Col>
|
||||
<AdvancedTableB />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<BasicTable />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<LargeTable />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<BorderedTable />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<SortTable />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ClearSearch />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<CellEdit />
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
309
app/routes/Tables/ExtendedTable/components/AdvancedTableA.js
Executable file
309
app/routes/Tables/ExtendedTable/components/AdvancedTableA.js
Executable file
@@ -0,0 +1,309 @@
|
||||
import React from 'react';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import paginationFactory from 'react-bootstrap-table2-paginator';
|
||||
import filterFactory, { Comparator, dateFilter } from 'react-bootstrap-table2-filter'
|
||||
import ToolkitProvider from 'react-bootstrap-table2-toolkit';
|
||||
import _ from 'lodash';
|
||||
import faker from 'faker/locale/en_US';
|
||||
import moment from 'moment';
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
CustomInput,
|
||||
StarRating,
|
||||
ButtonGroup
|
||||
} from './../../../../components';
|
||||
import { CustomExportCSV } from './CustomExportButton';
|
||||
import { CustomSearch } from './CustomSearch';
|
||||
import { CustomPaginationPanel } from './CustomPaginationPanel';
|
||||
import { CustomSizePerPageButton } from './CustomSizePerPageButton';
|
||||
import { CustomPaginationTotal } from './CustomPaginationTotal';
|
||||
import { randomArray } from './../../../../utilities';
|
||||
import {
|
||||
buildCustomTextFilter,
|
||||
buildCustomSelectFilter,
|
||||
buildCustomNumberFilter
|
||||
} from './../filters';
|
||||
|
||||
const INITIAL_PRODUCTS_COUNT = 500;
|
||||
|
||||
const ProductQuality = {
|
||||
Good: 'product-quality__good',
|
||||
Bad: 'product-quality__bad',
|
||||
Unknown: 'product-quality__unknown'
|
||||
};
|
||||
|
||||
const sortCaret = (order) => {
|
||||
if (!order)
|
||||
return <i className="fa fa-fw fa-sort text-muted"></i>;
|
||||
if (order)
|
||||
return <i className={`fa fa-fw text-muted fa-sort-${order}`}></i>
|
||||
}
|
||||
|
||||
const generateRow = (index) => ({
|
||||
id: index,
|
||||
name: faker.commerce.productName(),
|
||||
quality: randomArray([
|
||||
ProductQuality.Bad,
|
||||
ProductQuality.Good,
|
||||
ProductQuality.Unknown
|
||||
]),
|
||||
price: (1000 + Math.random() * 1000).toFixed(2),
|
||||
satisfaction: Math.round(Math.random() * 6),
|
||||
inStockDate: faker.date.past()
|
||||
});
|
||||
|
||||
export class AdvancedTableA extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
products: _.times(INITIAL_PRODUCTS_COUNT, generateRow),
|
||||
selected: []
|
||||
};
|
||||
|
||||
this.headerCheckboxRef = React.createRef();
|
||||
}
|
||||
|
||||
handleSelect(row, isSelected) {
|
||||
if (isSelected) {
|
||||
this.setState({ selected: [...this.state.selected, row.id] })
|
||||
} else {
|
||||
this.setState({
|
||||
selected: this.state.selected.filter(itemId => itemId !== row.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleSelectAll(isSelected, rows) {
|
||||
if (isSelected) {
|
||||
this.setState({ selected: _.map(rows, 'id') })
|
||||
} else {
|
||||
this.setState({ selected: [] });
|
||||
}
|
||||
}
|
||||
|
||||
handleAddRow() {
|
||||
const currentSize = this.state.products.length;
|
||||
|
||||
this.setState({
|
||||
products: [
|
||||
generateRow(currentSize + 1),
|
||||
...this.state.products,
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
handleDeleteRow() {
|
||||
this.setState({
|
||||
products: _.filter(this.state.products, product =>
|
||||
!_.includes(this.state.selected, product.id))
|
||||
})
|
||||
}
|
||||
|
||||
handleResetFilters() {
|
||||
this.nameFilter('');
|
||||
this.qualityFilter('');
|
||||
this.priceFilter('');
|
||||
this.satisfactionFilter('');
|
||||
}
|
||||
|
||||
createColumnDefinitions() {
|
||||
return [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID',
|
||||
headerFormatter: column => (
|
||||
<React.Fragment>
|
||||
<span className="text-nowrap">{ column.text }</span>
|
||||
<a
|
||||
href="javascript:;"
|
||||
className="d-block small text-decoration-none text-nowrap"
|
||||
onClick={ this.handleResetFilters.bind(this) }
|
||||
>
|
||||
Reset Filters <i className="fa fa-times fa-fw text-danger"></i>
|
||||
</a>
|
||||
</React.Fragment>
|
||||
)
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
sort: true,
|
||||
sortCaret,
|
||||
formatter: (cell) => (
|
||||
<span className="text-inverse">
|
||||
{ cell }
|
||||
</span>
|
||||
),
|
||||
...buildCustomTextFilter({
|
||||
placeholder: 'Enter product name...',
|
||||
getFilter: filter => { this.nameFilter = filter; }
|
||||
})
|
||||
}, {
|
||||
dataField: 'quality',
|
||||
text: 'Product Quality',
|
||||
formatter: (cell) => {
|
||||
let pqProps;
|
||||
switch (cell) {
|
||||
case ProductQuality.Good:
|
||||
pqProps = {
|
||||
color: 'success',
|
||||
text: 'Good'
|
||||
}
|
||||
break;
|
||||
case ProductQuality.Bad:
|
||||
pqProps = {
|
||||
color: 'danger',
|
||||
text: 'Bad'
|
||||
}
|
||||
break;
|
||||
case ProductQuality.Unknown:
|
||||
default:
|
||||
pqProps = {
|
||||
color: 'secondary',
|
||||
text: 'Unknown'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge color={pqProps.color}>
|
||||
{ pqProps.text }
|
||||
</Badge>
|
||||
)
|
||||
},
|
||||
sort: true,
|
||||
sortCaret,
|
||||
...buildCustomSelectFilter({
|
||||
placeholder: 'Select Quality',
|
||||
options: [
|
||||
{ value: ProductQuality.Good, label: 'Good' },
|
||||
{ value: ProductQuality.Bad, label: 'Bad' },
|
||||
{ value: ProductQuality.Unknown, label: 'Unknown' }
|
||||
],
|
||||
getFilter: filter => { this.qualityFilter = filter; }
|
||||
})
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price',
|
||||
sort: true,
|
||||
sortCaret,
|
||||
...buildCustomNumberFilter({
|
||||
comparators: [Comparator.EQ, Comparator.GT, Comparator.LT],
|
||||
getFilter: filter => { this.priceFilter = filter; }
|
||||
})
|
||||
}, {
|
||||
dataField: 'satisfaction',
|
||||
text: 'Buyer Satisfaction',
|
||||
sort: true,
|
||||
sortCaret,
|
||||
formatter: (cell) =>
|
||||
<StarRating at={ cell } max={ 6 } />,
|
||||
...buildCustomSelectFilter({
|
||||
placeholder: 'Select Satisfaction',
|
||||
options: _.times(6, (i) => ({ value: i + 1, label: i + 1 })),
|
||||
getFilter: filter => { this.satisfactionFilter = filter; }
|
||||
})
|
||||
}, {
|
||||
dataField: 'inStockDate',
|
||||
text: 'In Stock From',
|
||||
formatter: (cell) =>
|
||||
moment(cell).format('DD/MM/YYYY'),
|
||||
filter: dateFilter({
|
||||
className: 'd-flex align-items-center',
|
||||
comparatorClassName: 'd-none',
|
||||
dateClassName: 'form-control form-control-sm',
|
||||
comparator: Comparator.GT,
|
||||
getFilter: filter => { this.stockDateFilter = filter; }
|
||||
}),
|
||||
sort: true,
|
||||
sortCaret
|
||||
}];
|
||||
}
|
||||
|
||||
render() {
|
||||
const columnDefs = this.createColumnDefinitions();
|
||||
const paginationDef = paginationFactory({
|
||||
paginationSize: 5,
|
||||
showTotal: true,
|
||||
pageListRenderer: (props) => (
|
||||
<CustomPaginationPanel { ...props } size="sm" className="ml-md-auto mt-2 mt-md-0" />
|
||||
),
|
||||
sizePerPageRenderer: (props) => (
|
||||
<CustomSizePerPageButton { ...props } />
|
||||
),
|
||||
paginationTotalRenderer: (from, to, size) => (
|
||||
<CustomPaginationTotal { ...{ from, to, size } } />
|
||||
)
|
||||
});
|
||||
const selectRowConfig = {
|
||||
mode: 'checkbox',
|
||||
selected: this.state.selected,
|
||||
onSelect: this.handleSelect.bind(this),
|
||||
onSelectAll: this.handleSelectAll.bind(this),
|
||||
selectionRenderer: ({ mode, checked, disabled }) => (
|
||||
<CustomInput type={ mode } checked={ checked } disabled={ disabled } />
|
||||
),
|
||||
selectionHeaderRenderer: ({ mode, checked, indeterminate }) => (
|
||||
<CustomInput type={ mode } checked={ checked } innerRef={el => el && (el.indeterminate = indeterminate)} />
|
||||
)
|
||||
};
|
||||
|
||||
return (
|
||||
<ToolkitProvider
|
||||
keyField="id"
|
||||
data={ this.state.products }
|
||||
columns={ columnDefs }
|
||||
search
|
||||
exportCSV
|
||||
>
|
||||
{
|
||||
props => (
|
||||
<React.Fragment>
|
||||
<div className="d-flex justify-content-end align-items-center mb-2">
|
||||
<h6 className="my-0">
|
||||
AdvancedTable A
|
||||
</h6>
|
||||
<div className="d-flex ml-auto">
|
||||
<CustomSearch
|
||||
className="mr-2"
|
||||
{ ...props.searchProps }
|
||||
/>
|
||||
<ButtonGroup>
|
||||
<CustomExportCSV
|
||||
{ ...props.csvProps }
|
||||
>
|
||||
Export
|
||||
</CustomExportCSV>
|
||||
<Button
|
||||
size="sm"
|
||||
outline
|
||||
onClick={ this.handleDeleteRow.bind(this) }
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
outline
|
||||
onClick={ this.handleAddRow.bind(this) }
|
||||
>
|
||||
<i className="fa fa-fw fa-plus"></i>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
<BootstrapTable
|
||||
classes="table-responsive"
|
||||
pagination={ paginationDef }
|
||||
filter={ filterFactory() }
|
||||
selectRow={ selectRowConfig }
|
||||
bordered={ false }
|
||||
responsive
|
||||
{ ...props.baseProps }
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
</ToolkitProvider>
|
||||
);
|
||||
}
|
||||
}
|
234
app/routes/Tables/ExtendedTable/components/AdvancedTableB.js
Executable file
234
app/routes/Tables/ExtendedTable/components/AdvancedTableB.js
Executable file
@@ -0,0 +1,234 @@
|
||||
import React from 'react';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import ToolkitProvider from 'react-bootstrap-table2-toolkit';
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import faker from 'faker/locale/en_US';
|
||||
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Row,
|
||||
Col
|
||||
} from './../../../../components';
|
||||
import { CustomExportCSV } from './CustomExportButton';
|
||||
import { CustomSearch } from './CustomSearch';
|
||||
import { randomArray, randomAvatar } from './../../../../utilities';
|
||||
|
||||
const generateRow = (id) => ({
|
||||
id,
|
||||
photo: randomAvatar(),
|
||||
firstName: faker.name.firstName(),
|
||||
lastName: faker.name.lastName(),
|
||||
role: faker.name.jobType(),
|
||||
status: randomArray([
|
||||
'Active',
|
||||
'Suspended',
|
||||
'Waiting',
|
||||
'Unknown'
|
||||
]),
|
||||
region: randomArray(['North', 'South', 'East', 'West']),
|
||||
earnings: 500 + Math.random() * 1000,
|
||||
earningsCurrencyIcon: randomArray([
|
||||
<i className="fa fa-fw fa-euro text-muted" key="cur_eur"></i>,
|
||||
<i className="fa fa-fw fa-dollar text-muted" key="cur_usd"></i>
|
||||
]),
|
||||
lastLoginDate: faker.date.recent(),
|
||||
ipAddress: faker.internet.ip(),
|
||||
browser: 'Safari 9.1.1(11601.6.17)',
|
||||
os: 'OS X El Capitan',
|
||||
planSelected: randomArray(['Basic', 'Premium', 'Enterprise']),
|
||||
planEnd: faker.date.future()
|
||||
});
|
||||
|
||||
const sortCaret = (order) => {
|
||||
if (!order)
|
||||
return <i className="fa fa-fw fa-sort text-muted"></i>;
|
||||
if (order)
|
||||
return <i className={`fa fa-fw text-muted fa-sort-${order}`}></i>
|
||||
};
|
||||
|
||||
export class AdvancedTableB extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
users: _.times(10, generateRow)
|
||||
}
|
||||
}
|
||||
|
||||
handleAddRow() {
|
||||
const usersLength = this.state.users.length;
|
||||
|
||||
this.setState({
|
||||
users: [
|
||||
generateRow(usersLength + 1),
|
||||
...this.state.users
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
createColumnDefinitions() {
|
||||
return [
|
||||
{
|
||||
dataField: 'photo',
|
||||
text: 'Photo',
|
||||
formatter: (cell) => (
|
||||
<Avatar.Image src={ cell } />
|
||||
)
|
||||
}, {
|
||||
dataField: 'firstName',
|
||||
text: 'First Name',
|
||||
sort: true,
|
||||
sortCaret
|
||||
}, {
|
||||
dataField: 'lastName',
|
||||
text: 'Last Name',
|
||||
sort: true,
|
||||
sortCaret
|
||||
}, {
|
||||
dataField: 'role',
|
||||
text: 'Role',
|
||||
sort: true,
|
||||
sortCaret
|
||||
}, {
|
||||
dataField: 'status',
|
||||
text: 'Status',
|
||||
sort: true,
|
||||
sortCaret,
|
||||
formatter: (cell) => {
|
||||
const color = (status) => {
|
||||
const map = {
|
||||
'Active': 'success',
|
||||
'Suspended': 'danger',
|
||||
'Waiting': 'info',
|
||||
'Unknown': 'secondary'
|
||||
};
|
||||
return map[status];
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge color={ color(cell) }>
|
||||
{ cell }
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
}, {
|
||||
dataField: 'region',
|
||||
text: 'Region',
|
||||
sort: true,
|
||||
sortCaret
|
||||
}, {
|
||||
dataField: 'earnings',
|
||||
text: 'Earnings',
|
||||
sort: true,
|
||||
sortCaret,
|
||||
formatter: (cell, row) => (
|
||||
<span>
|
||||
{ row.earningsCurrencyIcon }
|
||||
{ _.isNumber(cell) && cell.toFixed(2) }
|
||||
</span>
|
||||
)
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
const columnDefs = this.createColumnDefinitions();
|
||||
|
||||
const expandRow = {
|
||||
renderer: row => (
|
||||
<Row>
|
||||
<Col md={ 6 }>
|
||||
<dl className="row">
|
||||
<dt className="col-sm-6 text-right">Last Login</dt>
|
||||
<dd className="col-sm-6">{ moment(row.lastLoginDate).format('DD-MMM-YYYY') }</dd>
|
||||
|
||||
<dt className="col-sm-6 text-right">IP Address</dt>
|
||||
<dd className="col-sm-6">{ row.ipAddress }</dd>
|
||||
|
||||
<dt className="col-sm-6 text-right">Browser</dt>
|
||||
<dd className="col-sm-6">{ row.browser }</dd>
|
||||
</dl>
|
||||
</Col>
|
||||
<Col md={ 6 }>
|
||||
<dl className="row">
|
||||
<dt className="col-sm-6 text-right">Operating System</dt>
|
||||
<dd className="col-sm-6">{ row.os }</dd>
|
||||
|
||||
<dt className="col-sm-6 text-right">Selected Plan</dt>
|
||||
<dd className="col-sm-6">{ row.planSelected }</dd>
|
||||
|
||||
<dt className="col-sm-6 text-right">Plan Expiriation</dt>
|
||||
<dd className="col-sm-6">{ moment(row.planEnd).format('DD-MMM-YYYY') }</dd>
|
||||
</dl>
|
||||
</Col>
|
||||
</Row>
|
||||
),
|
||||
showExpandColumn: true,
|
||||
expandHeaderColumnRenderer: ({ isAnyExpands }) => isAnyExpands ? (
|
||||
<i className="fa fa-angle-down fa-fw fa-lg text-muted"></i>
|
||||
) : (
|
||||
<i className="fa fa-angle-right fa-fw fa-lg text-muted"></i>
|
||||
),
|
||||
expandColumnRenderer: ({ expanded }) =>
|
||||
expanded ? (
|
||||
<i className="fa fa-angle-down fa-fw fa-lg text-muted"></i>
|
||||
) : (
|
||||
<i className="fa fa-angle-right fa-fw fa-lg text-muted"></i>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ToolkitProvider
|
||||
keyField="id"
|
||||
data={ this.state.users }
|
||||
columns={ columnDefs }
|
||||
search
|
||||
exportCSV
|
||||
>
|
||||
{
|
||||
props => (
|
||||
<React.Fragment>
|
||||
<div className="d-flex justify-content-end align-items-center mb-2">
|
||||
<h6 className="my-0">
|
||||
AdvancedTable B
|
||||
</h6>
|
||||
<div className="d-flex ml-auto">
|
||||
<CustomSearch
|
||||
className="mr-2"
|
||||
{ ...props.searchProps }
|
||||
/>
|
||||
<ButtonGroup>
|
||||
<CustomExportCSV
|
||||
{ ...props.csvProps }
|
||||
>
|
||||
Export
|
||||
</CustomExportCSV>
|
||||
<Button
|
||||
size="sm"
|
||||
outline
|
||||
onClick={ this.handleAddRow.bind(this) }
|
||||
>
|
||||
Add <i className="fa fa-fw fa-plus"></i>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
<BootstrapTable
|
||||
classes="table-responsive-lg"
|
||||
bordered={ false }
|
||||
expandRow={ expandRow }
|
||||
responsive
|
||||
hover
|
||||
{ ...props.baseProps }
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
</ToolkitProvider>
|
||||
);
|
||||
}
|
||||
}
|
38
app/routes/Tables/ExtendedTable/components/BasicTable.js
Executable file
38
app/routes/Tables/ExtendedTable/components/BasicTable.js
Executable file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import faker from 'faker/locale/en_US';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}
|
||||
];
|
||||
|
||||
const data = _.times(5, (index) => ({
|
||||
id: index,
|
||||
name: faker.commerce.productName(),
|
||||
price: Math.round(2000 + Math.random() * 500)
|
||||
}));
|
||||
|
||||
export const BasicTable = () => (
|
||||
<React.Fragment>
|
||||
<h6 className="mt-0">
|
||||
Basic Table
|
||||
</h6>
|
||||
<BootstrapTable
|
||||
classes="table-responsive-sm"
|
||||
keyField='id'
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
bordered={ false }
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
38
app/routes/Tables/ExtendedTable/components/BorderedTable.js
Executable file
38
app/routes/Tables/ExtendedTable/components/BorderedTable.js
Executable file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import faker from 'faker/locale/en_US';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}
|
||||
];
|
||||
|
||||
const data = _.times(5, (index) => ({
|
||||
id: index,
|
||||
name: faker.commerce.productName(),
|
||||
price: Math.round(2000 + Math.random() * 500)
|
||||
}));
|
||||
|
||||
export const BorderedTable = () => (
|
||||
<React.Fragment>
|
||||
<h6 className="mt-0">
|
||||
Bordered Table
|
||||
</h6>
|
||||
<BootstrapTable
|
||||
classes='table-responsive-sm'
|
||||
keyField='id'
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
bordered
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
89
app/routes/Tables/ExtendedTable/components/CellEdit.js
Executable file
89
app/routes/Tables/ExtendedTable/components/CellEdit.js
Executable file
@@ -0,0 +1,89 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
|
||||
import faker from 'faker/locale/en_US';
|
||||
|
||||
import { randomArray } from './../../../../utilities';
|
||||
|
||||
const regions = [
|
||||
{ value: 'Europe', label: 'Europe' },
|
||||
{ value: 'North America', label: 'North America' },
|
||||
{ value: 'Asia', label: 'Asia' },
|
||||
{ value: 'Australia', label: 'Australia' },
|
||||
];
|
||||
|
||||
const columns = [
|
||||
{
|
||||
dataField: 'id',
|
||||
text: 'Product ID',
|
||||
headerClasses: 'text-nowrap'
|
||||
}, {
|
||||
dataField: 'available',
|
||||
text: 'Available',
|
||||
editor: {
|
||||
type: Type.CHECKBOX,
|
||||
value: 'Y:N'
|
||||
},
|
||||
formatter: function AvailableFormatter(cell) {
|
||||
return cell === 'Y' ?
|
||||
<i className="fa fa-fw fa-check"></i> :
|
||||
<i className="fa fa-fw fa-times"></i>;
|
||||
},
|
||||
headerClasses: 'text-nowrap'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
editor: {
|
||||
type: Type.TEXT
|
||||
},
|
||||
headerClasses: 'text-nowrap'
|
||||
}, {
|
||||
dataField: 'description',
|
||||
text: 'Product Description',
|
||||
editor: {
|
||||
type: Type.TEXTAREA
|
||||
},
|
||||
style: {
|
||||
width: '40%'
|
||||
},
|
||||
headerClasses: 'text-nowrap'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price',
|
||||
headerClasses: 'text-nowrap'
|
||||
}, {
|
||||
dataField: 'region',
|
||||
text: 'Region',
|
||||
headerClasses: 'text-nowrap',
|
||||
editor: {
|
||||
type: Type.SELECT,
|
||||
options: regions
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const data = _.times(5, (index) => ({
|
||||
id: index,
|
||||
available: !Math.round(Math.random()) ? 'Y' : 'N',
|
||||
name: faker.commerce.productName(),
|
||||
description: faker.lorem.paragraph(),
|
||||
price: Math.round(2000 + Math.random() * 500),
|
||||
region: randomArray(_.map(regions, 'value'))
|
||||
}))
|
||||
|
||||
export const CellEdit = () => (
|
||||
<React.Fragment>
|
||||
<h6 className="mt-0">
|
||||
Cell Edit
|
||||
</h6>
|
||||
<BootstrapTable
|
||||
classes="table-responsive"
|
||||
keyField='id'
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
bordered={ false }
|
||||
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
59
app/routes/Tables/ExtendedTable/components/ClearSearch.js
Executable file
59
app/routes/Tables/ExtendedTable/components/ClearSearch.js
Executable file
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import ToolkitProvider from 'react-bootstrap-table2-toolkit';
|
||||
import faker from 'faker/locale/en_US';
|
||||
|
||||
import { CustomSearch } from './CustomSearch';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}
|
||||
];
|
||||
|
||||
const data = _.times(5, (index) => ({
|
||||
id: index,
|
||||
name: faker.commerce.productName(),
|
||||
price: Math.round(2000 + Math.random() * 500)
|
||||
}));
|
||||
|
||||
export const ClearSearch = () => (
|
||||
<ToolkitProvider
|
||||
keyField="id"
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
search
|
||||
>
|
||||
{
|
||||
props => (
|
||||
<React.Fragment>
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
<h6 className="mt-0">
|
||||
Table Search with Clear
|
||||
</h6>
|
||||
|
||||
<div>
|
||||
<CustomSearch
|
||||
className="mb-2"
|
||||
{ ...props.searchProps }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<BootstrapTable
|
||||
classes="table-responsive-sm"
|
||||
bordered={ false }
|
||||
{ ...props.baseProps }
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
</ToolkitProvider>
|
||||
);
|
28
app/routes/Tables/ExtendedTable/components/CustomExportButton.js
Executable file
28
app/routes/Tables/ExtendedTable/components/CustomExportButton.js
Executable file
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Button
|
||||
} from './../../../../components';
|
||||
|
||||
export const CustomExportCSV = ({children, onExport, ...props}) => {
|
||||
return (
|
||||
<Button
|
||||
{ ...props }
|
||||
onClick={() => { onExport() }}
|
||||
>
|
||||
{ children }
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
CustomExportCSV.propTypes = {
|
||||
size: PropTypes.string,
|
||||
outline: PropTypes.bool,
|
||||
onExport: PropTypes.func,
|
||||
children: PropTypes.node
|
||||
}
|
||||
|
||||
CustomExportCSV.defaultProps = {
|
||||
size: 'sm',
|
||||
outline: true
|
||||
}
|
31
app/routes/Tables/ExtendedTable/components/CustomPaginationPanel.js
Executable file
31
app/routes/Tables/ExtendedTable/components/CustomPaginationPanel.js
Executable file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { map, isInteger } from 'lodash';
|
||||
import { Pagination, PaginationItem, PaginationLink, Col } from './../../../../components';
|
||||
|
||||
const mapToFa = {
|
||||
'<': <i className="fa fa-angle-left" />,
|
||||
'<<': <i className="fa fa-angle-double-left" />,
|
||||
'>': <i className="fa fa-angle-right" />,
|
||||
'>>': <i className="fa fa-angle-double-right" />
|
||||
}
|
||||
|
||||
export const CustomPaginationPanel = ({ onPageChange, pages, ...otherProps }) => (
|
||||
<Col md={ 6 } className="d-flex">
|
||||
<Pagination aria-label="Page navigation example" { ...otherProps } listClassName="my-0">
|
||||
{
|
||||
map(pages, page => (
|
||||
<PaginationItem active={ page.active } disabled={ page.disabled }>
|
||||
<PaginationLink onClick={() => onPageChange(page.page)}>
|
||||
{ isInteger(page.page) ? page.page : mapToFa[page.page] }
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
))
|
||||
}
|
||||
</Pagination>
|
||||
</Col>
|
||||
);
|
||||
CustomPaginationPanel.propTypes = {
|
||||
pages: PropTypes.array,
|
||||
onPageChange: PropTypes.func
|
||||
};
|
13
app/routes/Tables/ExtendedTable/components/CustomPaginationTotal.js
Executable file
13
app/routes/Tables/ExtendedTable/components/CustomPaginationTotal.js
Executable file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export const CustomPaginationTotal = ({ from, to, size }) => (
|
||||
<span className="small ml-2">
|
||||
Showing { from } to { to } of { size } Results
|
||||
</span>
|
||||
);
|
||||
CustomPaginationTotal.propTypes = {
|
||||
from: PropTypes.number,
|
||||
to: PropTypes.number,
|
||||
size: PropTypes.number,
|
||||
};
|
57
app/routes/Tables/ExtendedTable/components/CustomSearch.js
Executable file
57
app/routes/Tables/ExtendedTable/components/CustomSearch.js
Executable file
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Input,
|
||||
InputGroup,
|
||||
Button,
|
||||
InputGroupAddon
|
||||
} from './../../../../components';
|
||||
|
||||
export class CustomSearch extends React.Component {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
onSearch: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
value: ''
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState.value !== this.state.value) {
|
||||
this.props.onSearch(this.state.value);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<InputGroup className={ this.props.className } size="sm">
|
||||
<InputGroupAddon addonType="prepend">
|
||||
<i className="fa fa-search fa-fw"></i>
|
||||
</InputGroupAddon>
|
||||
<Input
|
||||
onChange={(e) => { this.setState({ value: e.target.value }) }}
|
||||
value={ this.state.value }
|
||||
className="bg-white"
|
||||
placeholder="Type to search..."
|
||||
/>
|
||||
{
|
||||
this.state.value && (
|
||||
<InputGroupAddon addonType="append">
|
||||
<Button
|
||||
outline
|
||||
onClick={() => { this.setState({value: ''}) }}
|
||||
>
|
||||
<i className="fa fa-fw fa-times"></i>
|
||||
</Button>
|
||||
</InputGroupAddon>
|
||||
)
|
||||
}
|
||||
</InputGroup>
|
||||
)
|
||||
}
|
||||
}
|
40
app/routes/Tables/ExtendedTable/components/CustomSizePerPageButton.js
Executable file
40
app/routes/Tables/ExtendedTable/components/CustomSizePerPageButton.js
Executable file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { map } from 'lodash';
|
||||
import {
|
||||
UncontrolledButtonDropdown,
|
||||
DropdownToggle,
|
||||
DropdownMenu,
|
||||
DropdownItem
|
||||
} from './../../../../components';
|
||||
|
||||
export const CustomSizePerPageButton = ({
|
||||
options,
|
||||
currSizePerPage,
|
||||
onSizePerPageChange,
|
||||
...ddProps,
|
||||
}) => (
|
||||
<UncontrolledButtonDropdown { ...ddProps }>
|
||||
<DropdownToggle size="sm" color="link" className="text-decoration-none">
|
||||
{ currSizePerPage }<i className="fa fa-angle-down ml-2" />
|
||||
</DropdownToggle>
|
||||
<DropdownMenu>
|
||||
<DropdownItem header>Page Size</DropdownItem>
|
||||
{
|
||||
map(options, option => (
|
||||
<DropdownItem
|
||||
onClick={() => onSizePerPageChange(option.page)}
|
||||
active={option.page === currSizePerPage}
|
||||
>
|
||||
{ option.text }
|
||||
</DropdownItem>
|
||||
))
|
||||
}
|
||||
</DropdownMenu>
|
||||
</UncontrolledButtonDropdown>
|
||||
);
|
||||
CustomSizePerPageButton.propTypes = {
|
||||
options: PropTypes.object,
|
||||
currSizePerPage: PropTypes.number,
|
||||
onSizePerPageChange: PropTypes.func
|
||||
}
|
71
app/routes/Tables/ExtendedTable/components/LargeTable.js
Executable file
71
app/routes/Tables/ExtendedTable/components/LargeTable.js
Executable file
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import faker from 'faker/locale/en_US';
|
||||
|
||||
import classes from './LargeTable.scss';
|
||||
import { Card, CardHeader } from './../../../../components';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}
|
||||
];
|
||||
|
||||
const data = _.times(500, (index) => ({
|
||||
id: index,
|
||||
name: faker.commerce.productName(),
|
||||
price: Math.round(2000 + Math.random() * 500)
|
||||
}));
|
||||
|
||||
const expandRow = {
|
||||
showExpandColumn: true,
|
||||
renderer: function ExtendedRowRender(row) {
|
||||
return (
|
||||
<div>
|
||||
<p>{ `This Expand row is belong to rowKey ${row.id}` }</p>
|
||||
<p>You can render anything here, also you can add additional data on every row object</p>
|
||||
<p>expandRow.renderer callback will pass the origin row object to you</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
expandHeaderColumnRenderer: ({ isAnyExpands }) => isAnyExpands ? (
|
||||
<i className="fa fa-angle-down fa-fw fa-lg text-muted"></i>
|
||||
) : (
|
||||
<i className="fa fa-angle-right fa-fw fa-lg text-muted"></i>
|
||||
),
|
||||
expandColumnRenderer: ({ expanded }) =>
|
||||
expanded ? (
|
||||
<i className="fa fa-angle-down fa-fw fa-lg text-muted"></i>
|
||||
) : (
|
||||
<i className="fa fa-angle-right fa-fw fa-lg text-muted"></i>
|
||||
)
|
||||
};
|
||||
|
||||
export const LargeTable = () => (
|
||||
<React.Fragment>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
Large Table
|
||||
</CardHeader>
|
||||
<div className={ classes['table-scroll-wrap'] }>
|
||||
<BootstrapTable
|
||||
classes="table-responsive-sm"
|
||||
keyField='id'
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
bordered={ false }
|
||||
selectRow={ { mode: 'checkbox', clickToSelect: true } }
|
||||
expandRow={ expandRow }
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</React.Fragment>
|
||||
);
|
4
app/routes/Tables/ExtendedTable/components/LargeTable.scss
Executable file
4
app/routes/Tables/ExtendedTable/components/LargeTable.scss
Executable file
@@ -0,0 +1,4 @@
|
||||
.table-scroll-wrap {
|
||||
max-height: 380px;
|
||||
overflow-y: auto;
|
||||
}
|
44
app/routes/Tables/ExtendedTable/components/SelectAll.js
Executable file
44
app/routes/Tables/ExtendedTable/components/SelectAll.js
Executable file
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import faker from 'faker/locale/en_US';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price',
|
||||
}
|
||||
];
|
||||
|
||||
const data = _.times(5, (index) => ({
|
||||
id: index,
|
||||
name: faker.commerce.productName(),
|
||||
price: Math.round(2000 + Math.random() * 500)
|
||||
}));
|
||||
|
||||
const selectRow = {
|
||||
mode: 'checkbox',
|
||||
clickToSelect: true
|
||||
};
|
||||
|
||||
export const SelectAll = () => (
|
||||
<React.Fragment>
|
||||
<h6 className="mt-0">
|
||||
Select All
|
||||
</h6>
|
||||
<BootstrapTable
|
||||
classes="table-responsive"
|
||||
keyField='id'
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
bordered={ false }
|
||||
selectRow={ selectRow }
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
49
app/routes/Tables/ExtendedTable/components/SortTable.js
Executable file
49
app/routes/Tables/ExtendedTable/components/SortTable.js
Executable file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import faker from 'faker/locale/en_US';
|
||||
|
||||
const sortCaret = (order) => {
|
||||
if (!order)
|
||||
return <i className="fa fa-fw fa-sort text-muted"></i>;
|
||||
if (order)
|
||||
return <i className={`fa fa-fw text-muted fa-sort-${order}`}></i>
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
sort: true,
|
||||
sortCaret
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price',
|
||||
sort: true,
|
||||
sortCaret
|
||||
}
|
||||
];
|
||||
|
||||
const data = _.times(10, (index) => ({
|
||||
id: index,
|
||||
name: faker.commerce.productName(),
|
||||
price: Math.round(2000 + Math.random() * 500)
|
||||
}));
|
||||
|
||||
export const SortTable = () => (
|
||||
<React.Fragment>
|
||||
<h6 className="mt-0">
|
||||
Sort Table
|
||||
</h6>
|
||||
<BootstrapTable
|
||||
classes="table-responsive-sm"
|
||||
keyField='id'
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
bordered={ false }
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
11
app/routes/Tables/ExtendedTable/components/index.js
Executable file
11
app/routes/Tables/ExtendedTable/components/index.js
Executable file
@@ -0,0 +1,11 @@
|
||||
export * from './AdvancedTableA';
|
||||
export * from './AdvancedTableB';
|
||||
export * from './BasicTable';
|
||||
export * from './BorderedTable';
|
||||
export * from './CellEdit';
|
||||
export * from './ClearSearch';
|
||||
export * from './CustomExportButton';
|
||||
export * from './CustomSearch';
|
||||
export * from './LargeTable';
|
||||
export * from './SelectAll';
|
||||
export * from './SortTable';
|
3
app/routes/Tables/ExtendedTable/filters/index.js
Executable file
3
app/routes/Tables/ExtendedTable/filters/index.js
Executable file
@@ -0,0 +1,3 @@
|
||||
export * from './textFilter';
|
||||
export * from './selectFilter';
|
||||
export * from './numberFilter';
|
143
app/routes/Tables/ExtendedTable/filters/numberFilter.js
Executable file
143
app/routes/Tables/ExtendedTable/filters/numberFilter.js
Executable file
@@ -0,0 +1,143 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from 'lodash';
|
||||
import uid from 'uuid/v4';
|
||||
import { customFilter, Comparator, FILTER_TYPES } from 'react-bootstrap-table2-filter';
|
||||
|
||||
import { CustomInput, Input } from './../../../../components';
|
||||
|
||||
const comparatorSign = (comp) => {
|
||||
switch (comp) {
|
||||
case Comparator.EQ:
|
||||
return '=';
|
||||
case Comparator.GT:
|
||||
return '>';
|
||||
case Comparator.LT:
|
||||
return '<';
|
||||
}
|
||||
}
|
||||
|
||||
class NumberFilter extends React.Component {
|
||||
static propTypes = {
|
||||
column: PropTypes.object.isRequired,
|
||||
onFilter: PropTypes.func.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
getFilter: PropTypes.func,
|
||||
comparators: PropTypes.array,
|
||||
comparator: PropTypes.string,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
comparators: [Comparator.EQ],
|
||||
comparator: Comparator.EQ
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
value: '',
|
||||
comparator: props.comparator
|
||||
}
|
||||
|
||||
this.comparatorInputId = uid();
|
||||
this.valueInputId = uid();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (_.isFunction(this.props.getFilter)) {
|
||||
this.props.getFilter((value) => {
|
||||
this.setState({ value });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (
|
||||
prevState.value !== this.state.value ||
|
||||
prevState.comparator !== this.state.comparator
|
||||
) {
|
||||
this.props.onFilter({
|
||||
number: this.state.value,
|
||||
comparator: this.state.comparator
|
||||
});
|
||||
}
|
||||
if (prevProps.comparator !== this.props.comparator) {
|
||||
this.setState({ comparator: this.props.comparator });
|
||||
}
|
||||
}
|
||||
|
||||
handleClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { placeholder, comparators } = this.props;
|
||||
const { comparator } = this.state;
|
||||
return (
|
||||
<div className='d-flex'>
|
||||
{
|
||||
(!_.isEmpty(comparators) && comparators.length > 0) && (
|
||||
<CustomInput
|
||||
type="select"
|
||||
bsSize="sm"
|
||||
onChange={(e) => { this.setState({ comparator: e.target.value }) }}
|
||||
onClick={this.handleClick}
|
||||
value={ comparator }
|
||||
className="d-block bg-white mr-1"
|
||||
id={this.comparatorInputId}
|
||||
>
|
||||
<option value=""></option>
|
||||
|
||||
{
|
||||
_.map(comparators, (comparator, index) => (
|
||||
<option
|
||||
value={ comparator }
|
||||
key={ index }
|
||||
>
|
||||
{ comparatorSign(comparator) }
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</CustomInput>
|
||||
)
|
||||
}
|
||||
<Input
|
||||
type="number"
|
||||
className="bg-white"
|
||||
bsSize="sm"
|
||||
onChange={(e) => { this.setState({ value: e.target.value }) }}
|
||||
onClick={this.handleClick}
|
||||
value={ this.state.value }
|
||||
placeholder={ placeholder }
|
||||
id={this.valueInputId}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const buildCustomNumberFilter = ({ placeholder, getFilter, comparators, ...other } = {}) => ({
|
||||
filter: customFilter({
|
||||
type: FILTER_TYPES.NUMBER,
|
||||
...other
|
||||
}),
|
||||
filterRenderer: function NumberFilterWrap(onFilter, column) {
|
||||
return (
|
||||
<NumberFilter
|
||||
{...{
|
||||
onFilter,
|
||||
column,
|
||||
placeholder,
|
||||
getFilter,
|
||||
comparators
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
});
|
90
app/routes/Tables/ExtendedTable/filters/selectFilter.js
Executable file
90
app/routes/Tables/ExtendedTable/filters/selectFilter.js
Executable file
@@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from 'lodash';
|
||||
import { customFilter } from 'react-bootstrap-table2-filter';
|
||||
import uid from 'uuid/v4';
|
||||
|
||||
import { CustomInput } from './../../../../components';
|
||||
|
||||
class SelectFilter extends React.Component {
|
||||
static propTypes = {
|
||||
column: PropTypes.object.isRequired,
|
||||
onFilter: PropTypes.func.isRequired,
|
||||
options: PropTypes.array.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
getFilter: PropTypes.func,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
value: ''
|
||||
}
|
||||
this.inputId = uid();
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (_.isFunction(this.props.getFilter)) {
|
||||
this.props.getFilter((value) => {
|
||||
this.setState({ value });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState.value !== this.state.value) {
|
||||
this.props.onFilter(this.state.value);
|
||||
}
|
||||
}
|
||||
|
||||
handleClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { placeholder, options } = this.props;
|
||||
return (
|
||||
<CustomInput
|
||||
type="select"
|
||||
bsSize="sm"
|
||||
onChange={(e) => { this.setState({ value: e.target.value }) }}
|
||||
onClick={ this.handleClick }
|
||||
value={ this.state.value }
|
||||
className="d-block bg-white"
|
||||
id={this.inputId}
|
||||
>
|
||||
<option value="">{ placeholder }</option>
|
||||
|
||||
{
|
||||
_.map(options, ({ value, label }, index) => (
|
||||
<option value={value} key={ index }>{ label }</option>
|
||||
))
|
||||
}
|
||||
</CustomInput>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const buildCustomSelectFilter = ({ placeholder, options, getFilter, ...other } = {}) => ({
|
||||
filter: customFilter(other),
|
||||
filterRenderer: function TextFilterWrap(onFilter, column) {
|
||||
return (
|
||||
<SelectFilter
|
||||
{...{
|
||||
onFilter,
|
||||
column,
|
||||
placeholder,
|
||||
options,
|
||||
getFilter
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
});
|
89
app/routes/Tables/ExtendedTable/filters/textFilter.js
Executable file
89
app/routes/Tables/ExtendedTable/filters/textFilter.js
Executable file
@@ -0,0 +1,89 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from 'lodash';
|
||||
import { customFilter } from 'react-bootstrap-table2-filter';
|
||||
|
||||
import {
|
||||
Form,
|
||||
Input
|
||||
} from './../../../../components';
|
||||
|
||||
class TextFilter extends React.Component {
|
||||
static propTypes = {
|
||||
column: PropTypes.object.isRequired,
|
||||
onFilter: PropTypes.func.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
getFilter: PropTypes.func,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
value: ''
|
||||
}
|
||||
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (_.isFunction(this.props.getFilter)) {
|
||||
this.props.getFilter((value) => {
|
||||
this.setState({ value });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState.value !== this.state.value) {
|
||||
this.props.onFilter(this.state.value);
|
||||
}
|
||||
}
|
||||
|
||||
handleClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onFilter, placeholder } = this.props;
|
||||
return (
|
||||
<Form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
onFilter(this.state.value)
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
bsSize="sm"
|
||||
className="bg-white"
|
||||
onChange={(e) => this.setState({value: e.target.value})}
|
||||
onClick={ this.handleClick }
|
||||
value={ this.state.value }
|
||||
placeholder={ placeholder }
|
||||
/>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const buildCustomTextFilter = ({ placeholder, getFilter, ...other } = {}) => ({
|
||||
filter: customFilter(other),
|
||||
filterRenderer: function TextFilterWrap(onFilter, column) {
|
||||
return (
|
||||
<TextFilter
|
||||
{...{
|
||||
onFilter,
|
||||
column,
|
||||
placeholder,
|
||||
getFilter
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
});
|
3
app/routes/Tables/ExtendedTable/index.js
Executable file
3
app/routes/Tables/ExtendedTable/index.js
Executable file
@@ -0,0 +1,3 @@
|
||||
import { ExtendedTable } from './ExtendedTable';
|
||||
|
||||
export default ExtendedTable;
|
Reference in New Issue
Block a user