mirror of
https://github.com/gosticks/react-table.git
synced 2026-08-19 16:30:21 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3afddd9fe3 | ||
|
|
d43d316eed | ||
|
|
590afb3e61 |
@@ -24,6 +24,7 @@ import FunctionalRendering from '../stories/FunctionalRendering.js'
|
|||||||
import CustomExpanderPosition from '../stories/CustomExpanderPosition.js'
|
import CustomExpanderPosition from '../stories/CustomExpanderPosition.js'
|
||||||
import NoDataText from '../stories/NoDataText.js'
|
import NoDataText from '../stories/NoDataText.js'
|
||||||
import Footers from '../stories/Footers.js'
|
import Footers from '../stories/Footers.js'
|
||||||
|
import Filtering from '../stories/Filtering.js'
|
||||||
//
|
//
|
||||||
configure(() => {
|
configure(() => {
|
||||||
storiesOf('1. Docs')
|
storiesOf('1. Docs')
|
||||||
@@ -53,4 +54,5 @@ configure(() => {
|
|||||||
.add('Custom Expander Position', CustomExpanderPosition)
|
.add('Custom Expander Position', CustomExpanderPosition)
|
||||||
.add('Custom "No Data" Text', NoDataText)
|
.add('Custom "No Data" Text', NoDataText)
|
||||||
.add('Footers', Footers)
|
.add('Footers', Footers)
|
||||||
|
.add('Custom Filtering', Filtering)
|
||||||
}, module)
|
}, module)
|
||||||
|
|||||||
@@ -11,3 +11,7 @@ h1 {
|
|||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
@@ -143,6 +143,9 @@ These are all of the available props (and their default values) for the main `<R
|
|||||||
collapseOnDataChange: true,
|
collapseOnDataChange: true,
|
||||||
freezeWhenExpanded: false,
|
freezeWhenExpanded: false,
|
||||||
defaultSorting: [],
|
defaultSorting: [],
|
||||||
|
showFilters: false,
|
||||||
|
defaultFiltering: [],
|
||||||
|
defaultFilterMethod: (filter, row) => (row[filter.id] == filter.value),
|
||||||
|
|
||||||
// Controlled State Overrides (see Fully Controlled Component section)
|
// Controlled State Overrides (see Fully Controlled Component section)
|
||||||
page: undefined,
|
page: undefined,
|
||||||
@@ -154,6 +157,7 @@ These are all of the available props (and their default values) for the main `<R
|
|||||||
onPageChange: undefined,
|
onPageChange: undefined,
|
||||||
onPageSizeChange: undefined,
|
onPageSizeChange: undefined,
|
||||||
onSortingChange: undefined,
|
onSortingChange: undefined,
|
||||||
|
onFilteringChange: undefined,
|
||||||
|
|
||||||
// Pivoting
|
// Pivoting
|
||||||
pivotBy: undefined,
|
pivotBy: undefined,
|
||||||
@@ -184,6 +188,9 @@ These are all of the available props (and their default values) for the main `<R
|
|||||||
getTheadProps: () => ({}),
|
getTheadProps: () => ({}),
|
||||||
getTheadTrProps: () => ({}),
|
getTheadTrProps: () => ({}),
|
||||||
getTheadThProps: () => ({}),
|
getTheadThProps: () => ({}),
|
||||||
|
getTheadFilterProps: () => ({}),
|
||||||
|
getTheadFilterTrProps: () => ({}),
|
||||||
|
getTheadFilterThProps: () => ({}),
|
||||||
getTbodyProps: () => ({}),
|
getTbodyProps: () => ({}),
|
||||||
getTrGroupProps: () => ({}),
|
getTrGroupProps: () => ({}),
|
||||||
getTrProps: () => ({}),
|
getTrProps: () => ({}),
|
||||||
@@ -215,7 +222,8 @@ These are all of the available props (and their default values) for the main `<R
|
|||||||
footer: undefined,
|
footer: undefined,
|
||||||
footerClassName: '',
|
footerClassName: '',
|
||||||
footerStyle: {},
|
footerStyle: {},
|
||||||
getFooterProps: () => ({})
|
getFooterProps: () => ({}),
|
||||||
|
filterMethod: undefined
|
||||||
},
|
},
|
||||||
|
|
||||||
// Text
|
// Text
|
||||||
@@ -292,8 +300,13 @@ Or just define them as props
|
|||||||
footer: 'Header Name' or JSX eg. ({data, column}) => <div>Header Name</div>,
|
footer: 'Header Name' or JSX eg. ({data, column}) => <div>Header Name</div>,
|
||||||
footerClassName: '', // Set the classname of the `td` element of the column's footer
|
footerClassName: '', // Set the classname of the `td` element of the column's footer
|
||||||
footerStyle: {}, // Set the style of the `td` element of the column's footer
|
footerStyle: {}, // Set the style of the `td` element of the column's footer
|
||||||
getFooterProps: (state, rowInfo, column, instance) => ({}) // a function that returns props to decorate the `td` element of the column's footer
|
getFooterProps: (state, rowInfo, column, instance) => ({}) // A function that returns props to decorate the `td` element of the column's footer
|
||||||
|
|
||||||
|
// Filtering
|
||||||
|
filterMethod: (filter, row, column) => {return true} // A function returning a boolean that specifies the filtering logic for the column
|
||||||
|
// filter == an object specifying which filter is being applied. Format: {id: [the filter column's id], value: [the value the user typed in the filter field]}
|
||||||
|
// row == the row of data supplied to the table
|
||||||
|
// column == the column that the filter is on
|
||||||
}]
|
}]
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -532,7 +545,7 @@ By adding a `SubComponent` props, you can easily add an expansion level to all r
|
|||||||
|
|
||||||
|
|
||||||
## Server-side Data
|
## Server-side Data
|
||||||
If you want to handle pagination, and sorting on the server, `react-table` makes it easy on you.
|
If you want to handle pagination, sorting, and filtering on the server, `react-table` makes it easy on you.
|
||||||
|
|
||||||
1. Feed React Table `data` from somewhere dynamic. eg. `state`, a redux store, etc...
|
1. Feed React Table `data` from somewhere dynamic. eg. `state`, a redux store, etc...
|
||||||
1. Add `manual` as a prop. This informs React Table that you'll be handling sorting and pagination server-side
|
1. Add `manual` as a prop. This informs React Table that you'll be handling sorting and pagination server-side
|
||||||
@@ -555,7 +568,8 @@ If you want to handle pagination, and sorting on the server, `react-table` makes
|
|||||||
Axios.post('mysite.com/data', {
|
Axios.post('mysite.com/data', {
|
||||||
page: state.page,
|
page: state.page,
|
||||||
pageSize: state.pageSize,
|
pageSize: state.pageSize,
|
||||||
sorting: state.sorting
|
sorting: state.sorting,
|
||||||
|
filtering: state.filtering
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
// Update react-table
|
// Update react-table
|
||||||
@@ -601,6 +615,7 @@ Here are the props and their corresponding callbacks that control the state of t
|
|||||||
onPageSizeChange={(pageSize, pageIndex) => {...}} // Called when the pageSize is changed by the user. The resolve page is also sent to maintain approximate position in the data
|
onPageSizeChange={(pageSize, pageIndex) => {...}} // Called when the pageSize is changed by the user. The resolve page is also sent to maintain approximate position in the data
|
||||||
onSortingChange={(column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. If the column is a pivoted column, `column` will be an array of columns
|
onSortingChange={(column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. If the column is a pivoted column, `column` will be an array of columns
|
||||||
onExpandRow={(index, event) => {...}} // Called when an expander is clicked. Use this to manage `expandedRows`
|
onExpandRow={(index, event) => {...}} // Called when an expander is clicked. Use this to manage `expandedRows`
|
||||||
|
onFilteringChange={(column, event) => {...}} // Called when a user enters a value into a filter input field. The event is the onChange event of the input field.
|
||||||
/>
|
/>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<div id="error-display"></div>
|
<div id="error-display"></div>
|
||||||
<script src="static/preview.742092f25ec9b6802477.bundle.js"></script>
|
<script src="static/preview.ea1bdf726a82703c8f6f.bundle.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
+1
-1
@@ -38,7 +38,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body style="margin: 0;">
|
<body style="margin: 0;">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script src="static/manager.1e44a83b81ae02b691ef.bundle.js"></script>
|
<script src="static/manager.064c8fe6b78907f0a142.bundle.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
+26
-26
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"static/manager.064c8fe6b78907f0a142.bundle.js","sources":["webpack:///static/manager.064c8fe6b78907f0a142.bundle.js"],"mappings":"AAAA;AAkuDA;AA84DA;AAy8DA;AA00DA;AAsyEA;AA89CA;AA+rDA;AAsiDA;AAg6DA;AA2nDA;AA++CA;AAkvDA;AAsnEA;AA2oDA;AAivCA;AA+nDA;AAkpDA;AA6lEA;AAs4DA;AAquDA;AA+pDA;AAqxDA;AAsrDA;AA4yDA;AA88GA;AAj6CA;AAopGA;AAsuFA;AA+zEA;AAtMA;AAizEA","sourceRoot":""}
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"static/manager.1e44a83b81ae02b691ef.bundle.js","sources":["webpack:///static/manager.1e44a83b81ae02b691ef.bundle.js"],"mappings":"AAAA;AAkuDA;AA84DA;AA28DA;AA00DA;AAsyEA;AA89CA;AA+rDA;AAsiDA;AAg6DA;AA2nDA;AA++CA;AAkvDA;AAsnEA;AA2oDA;AAivCA;AA+nDA;AAkpDA;AA6lEA;AAs4DA;AAquDA;AA+pDA;AAqxDA;AAsrDA;AA2yDA;AA88GA;AAj6CA;AAopGA;AAsuFA;AA+zEA;AAtMA;AAizEA","sourceRoot":""}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"static/preview.742092f25ec9b6802477.bundle.js","sources":["webpack:///static/preview.742092f25ec9b6802477.bundle.js"],"mappings":"AAAA;AAkuDA;AAo8CA;AA8uGA;AA++EA;AAi9NA;AAo5GA;AAmvDA;AAmgEA;AA4xDA;AAigEA;AAmjDA;AA4sDA;AAk8CA;AAu/DA;AA8lDA;AA09CA;AAqoDA;AAywEA;AAokDA;AAsvCA;AA2mDA;AA4tDA;AAqjEA;AAs2DA;AAmyDA;AAsnDA;AAk1EA;AAmjGA;AAiuDA;AA+2CA;AAq5BA;AA4zDA;AA26BA;AAuOA;AAw5EA","sourceRoot":""}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"static/preview.ea1bdf726a82703c8f6f.bundle.js","sources":["webpack:///static/preview.ea1bdf726a82703c8f6f.bundle.js"],"mappings":"AAAA;AAkuDA;AAo/DA;AAgrFA;AAmmFA;AA07OA;AAuwFA;AAwtDA;AA+jEA;AA4uDA;AAi8DA;AA+lDA;AA4yDA;AA28CA;AAw7DA;AAooDA;AA67CA;AAiqDA;AAynEA;AAwxDA;AA+rCA;AA+mDA;AA2lDA;AAkqEA;AAs2DA;AA2zDA;AAo4CA;AAksFA;AAmjGA;AAksDA;AA02CA;AAgoCA;AAm/CA;AA+4CA;AAsCA;AA+jFA","sourceRoot":""}
|
||||||
+9
-1
@@ -21,6 +21,9 @@ export default {
|
|||||||
collapseOnDataChange: true,
|
collapseOnDataChange: true,
|
||||||
freezeWhenExpanded: false,
|
freezeWhenExpanded: false,
|
||||||
defaultSorting: [],
|
defaultSorting: [],
|
||||||
|
showFilters: false,
|
||||||
|
defaultFiltering: [],
|
||||||
|
defaultFilterMethod: (filter, row, column) => (row[filter.id] === filter.value),
|
||||||
|
|
||||||
// Controlled State Overrides
|
// Controlled State Overrides
|
||||||
// page: undefined,
|
// page: undefined,
|
||||||
@@ -32,6 +35,7 @@ export default {
|
|||||||
onPageChange: undefined,
|
onPageChange: undefined,
|
||||||
onPageSizeChange: undefined,
|
onPageSizeChange: undefined,
|
||||||
onSortingChange: undefined,
|
onSortingChange: undefined,
|
||||||
|
onFilteringChange: undefined,
|
||||||
|
|
||||||
// Pivoting
|
// Pivoting
|
||||||
pivotBy: undefined,
|
pivotBy: undefined,
|
||||||
@@ -62,6 +66,9 @@ export default {
|
|||||||
getTheadProps: emptyObj,
|
getTheadProps: emptyObj,
|
||||||
getTheadTrProps: emptyObj,
|
getTheadTrProps: emptyObj,
|
||||||
getTheadThProps: emptyObj,
|
getTheadThProps: emptyObj,
|
||||||
|
getTheadFilterProps: emptyObj,
|
||||||
|
getTheadFilterTrProps: emptyObj,
|
||||||
|
getTheadFilterThProps: emptyObj,
|
||||||
getTbodyProps: emptyObj,
|
getTbodyProps: emptyObj,
|
||||||
getTrGroupProps: emptyObj,
|
getTrGroupProps: emptyObj,
|
||||||
getTrProps: emptyObj,
|
getTrProps: emptyObj,
|
||||||
@@ -92,7 +99,8 @@ export default {
|
|||||||
footer: undefined,
|
footer: undefined,
|
||||||
footerClassName: '',
|
footerClassName: '',
|
||||||
footerStyle: {},
|
footerStyle: {},
|
||||||
getFooterProps: emptyObj
|
getFooterProps: emptyObj,
|
||||||
|
filterMethod: undefined
|
||||||
},
|
},
|
||||||
|
|
||||||
// Text
|
// Text
|
||||||
|
|||||||
+152
-25
@@ -26,6 +26,9 @@ export default React.createClass({
|
|||||||
getTheadProps,
|
getTheadProps,
|
||||||
getTheadTrProps,
|
getTheadTrProps,
|
||||||
getTheadThProps,
|
getTheadThProps,
|
||||||
|
getTheadFilterProps,
|
||||||
|
getTheadFilterTrProps,
|
||||||
|
getTheadFilterThProps,
|
||||||
getTbodyProps,
|
getTbodyProps,
|
||||||
getTrGroupProps,
|
getTrGroupProps,
|
||||||
getTrProps,
|
getTrProps,
|
||||||
@@ -41,11 +44,13 @@ export default React.createClass({
|
|||||||
manual,
|
manual,
|
||||||
loadingText,
|
loadingText,
|
||||||
noDataText,
|
noDataText,
|
||||||
|
showFilters,
|
||||||
// State
|
// State
|
||||||
loading,
|
loading,
|
||||||
pageSize,
|
pageSize,
|
||||||
page,
|
page,
|
||||||
sorting,
|
sorting,
|
||||||
|
filtering,
|
||||||
pages,
|
pages,
|
||||||
// Pivoting State
|
// Pivoting State
|
||||||
pivotValKey,
|
pivotValKey,
|
||||||
@@ -82,7 +87,7 @@ export default React.createClass({
|
|||||||
const minRows = this.getMinRows()
|
const minRows = this.getMinRows()
|
||||||
const padRows = pages > 1 ? _.range(pageSize - pageRows.length)
|
const padRows = pages > 1 ? _.range(pageSize - pageRows.length)
|
||||||
: minRows ? _.range(Math.max(minRows - pageRows.length, 0))
|
: minRows ? _.range(Math.max(minRows - pageRows.length, 0))
|
||||||
: []
|
: []
|
||||||
|
|
||||||
const hasColumnFooter = allVisibleColumns.some(d => d.footer)
|
const hasColumnFooter = allVisibleColumns.some(d => d.footer)
|
||||||
|
|
||||||
@@ -360,6 +365,142 @@ export default React.createClass({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const makeFilters = () => {
|
||||||
|
const theadFilterProps = _.splitProps(getTheadFilterProps(finalState, undefined, undefined, this))
|
||||||
|
const theadFilterTrProps = _.splitProps(getTheadFilterTrProps(finalState, undefined, undefined, this))
|
||||||
|
return (
|
||||||
|
<TheadComponent
|
||||||
|
className={classnames('-filters', theadFilterProps.className)}
|
||||||
|
style={{
|
||||||
|
...theadFilterProps.style,
|
||||||
|
minWidth: `${rowMinWidth}px`
|
||||||
|
}}
|
||||||
|
{...theadFilterProps.rest}
|
||||||
|
>
|
||||||
|
<TrComponent
|
||||||
|
className={theadFilterTrProps.className}
|
||||||
|
style={theadFilterTrProps.style}
|
||||||
|
{...theadFilterTrProps.rest}
|
||||||
|
>
|
||||||
|
{allVisibleColumns.map(makeFilter)}
|
||||||
|
</TrComponent>
|
||||||
|
</TheadComponent>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeFilter = (column, i) => {
|
||||||
|
const width = _.getFirstDefined(column.width, column.minWidth)
|
||||||
|
const maxWidth = _.getFirstDefined(column.width, column.maxWidth)
|
||||||
|
const theadFilterThProps = _.splitProps(getTheadFilterThProps(finalState, undefined, column, this))
|
||||||
|
const columnHeaderProps = _.splitProps(column.getHeaderProps(finalState, undefined, column, this))
|
||||||
|
|
||||||
|
const classes = [
|
||||||
|
column.headerClassName,
|
||||||
|
theadFilterThProps.className,
|
||||||
|
columnHeaderProps.className
|
||||||
|
]
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
...column.headerStyle,
|
||||||
|
...theadFilterThProps.style,
|
||||||
|
...columnHeaderProps.style
|
||||||
|
}
|
||||||
|
|
||||||
|
const rest = {
|
||||||
|
...theadFilterThProps.rest,
|
||||||
|
...columnHeaderProps.rest
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.expander) {
|
||||||
|
if (column.pivotColumns) {
|
||||||
|
const pivotCols = []
|
||||||
|
for (let i = 0; i < column.pivotColumns.length; i++) {
|
||||||
|
const col = column.pivotColumns[i]
|
||||||
|
const filter = filtering.find(filter => filter.id === col.id)
|
||||||
|
pivotCols.push(
|
||||||
|
<span key={col.id}
|
||||||
|
style={{display: 'flex', alignContent: 'flex-end', flex: 1}}>
|
||||||
|
<input type='text'
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
width: 20,
|
||||||
|
backgroundColor: i > 0 ? '#f3f3f3' : '#fff'
|
||||||
|
}}
|
||||||
|
value={filter ? filter.value : ''}
|
||||||
|
disabled={i > 0}
|
||||||
|
onChange={(event) => this.filterColumn(col, event)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
if (i < column.pivotColumns.length - 1) {
|
||||||
|
pivotCols.push(<ExpanderComponent key={col.id + '-' + i} />)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ThComponent
|
||||||
|
key={i}
|
||||||
|
className={classnames(
|
||||||
|
'rt-pivot-header',
|
||||||
|
column.sortable && '-cursor-pointer',
|
||||||
|
classes
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
...styles,
|
||||||
|
flex: `${width} 0 auto`,
|
||||||
|
width: `${width}px`,
|
||||||
|
maxWidth: `${maxWidth}px`,
|
||||||
|
display: 'flex'
|
||||||
|
}}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{pivotCols}
|
||||||
|
</ThComponent>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ThComponent
|
||||||
|
key={i}
|
||||||
|
className={classnames(
|
||||||
|
'rt-expander-header',
|
||||||
|
classes
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
...styles,
|
||||||
|
flex: `0 0 auto`,
|
||||||
|
width: `${expanderColumnWidth}px`
|
||||||
|
}}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filter = filtering.find(filter => filter.id === column.id)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThComponent
|
||||||
|
key={i}
|
||||||
|
className={classnames(
|
||||||
|
classes
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
...styles,
|
||||||
|
flex: `${width} 0 auto`,
|
||||||
|
width: `${width}px`,
|
||||||
|
maxWidth: `${maxWidth}px`
|
||||||
|
}}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
<input type='text'
|
||||||
|
style={{
|
||||||
|
width: `100%`
|
||||||
|
}}
|
||||||
|
value={filter ? filter.value : ''}
|
||||||
|
onChange={(event) => this.filterColumn(column, event)}
|
||||||
|
/>
|
||||||
|
</ThComponent>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const makePageRow = (row, i, path = []) => {
|
const makePageRow = (row, i, path = []) => {
|
||||||
const rowInfo = {
|
const rowInfo = {
|
||||||
row: row.__original,
|
row: row.__original,
|
||||||
@@ -452,15 +593,15 @@ export default React.createClass({
|
|||||||
{...rowInfo}
|
{...rowInfo}
|
||||||
value={rowInfo.rowValues[pivotValKey]}
|
value={rowInfo.rowValues[pivotValKey]}
|
||||||
/>
|
/>
|
||||||
) : <span>{row[pivotValKey]} ({rowInfo.subRows.length})</span>}
|
) : <span>{row[pivotValKey]} ({rowInfo.subRows.length})</span>}
|
||||||
</span>
|
</span>
|
||||||
) : SubComponent ? (
|
) : SubComponent ? (
|
||||||
<span>
|
<span>
|
||||||
<ExpanderComponent
|
<ExpanderComponent
|
||||||
isExpanded={isExpanded}
|
isExpanded={isExpanded}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</TdComponent>
|
</TdComponent>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -526,7 +667,6 @@ export default React.createClass({
|
|||||||
const makePadRow = (row, i) => {
|
const makePadRow = (row, i) => {
|
||||||
const trGroupProps = getTrGroupProps(finalState, undefined, undefined, this)
|
const trGroupProps = getTrGroupProps(finalState, undefined, undefined, this)
|
||||||
const trProps = _.splitProps(getTrProps(finalState, undefined, undefined, this))
|
const trProps = _.splitProps(getTrProps(finalState, undefined, undefined, this))
|
||||||
const tdProps = _.splitProps(getTdProps(finalState, undefined, undefined, this))
|
|
||||||
return (
|
return (
|
||||||
<TrGroupComponent
|
<TrGroupComponent
|
||||||
key={i}
|
key={i}
|
||||||
@@ -539,20 +679,6 @@ export default React.createClass({
|
|||||||
)}
|
)}
|
||||||
style={trProps.style || {}}
|
style={trProps.style || {}}
|
||||||
>
|
>
|
||||||
{SubComponent && (
|
|
||||||
<ThComponent
|
|
||||||
className={classnames(
|
|
||||||
'rt-expander-header',
|
|
||||||
tdProps.className
|
|
||||||
)}
|
|
||||||
style={{
|
|
||||||
...tdProps.style,
|
|
||||||
flex: `0 0 auto`,
|
|
||||||
width: `${expanderColumnWidth}px`
|
|
||||||
}}
|
|
||||||
{...tdProps.rest}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{allVisibleColumns.map((column, i2) => {
|
{allVisibleColumns.map((column, i2) => {
|
||||||
const show = typeof column.show === 'function' ? column.show() : column.show
|
const show = typeof column.show === 'function' ? column.show() : column.show
|
||||||
const width = _.getFirstDefined(column.width, column.minWidth)
|
const width = _.getFirstDefined(column.width, column.minWidth)
|
||||||
@@ -735,6 +861,7 @@ export default React.createClass({
|
|||||||
>
|
>
|
||||||
{hasHeaderGroups ? makeHeaderGroups() : null}
|
{hasHeaderGroups ? makeHeaderGroups() : null}
|
||||||
{makeHeaders()}
|
{makeHeaders()}
|
||||||
|
{showFilters ? makeFilters() : null}
|
||||||
<TbodyComponent
|
<TbodyComponent
|
||||||
className={classnames(tBodyProps.className)}
|
className={classnames(tBodyProps.className)}
|
||||||
style={{
|
style={{
|
||||||
@@ -760,7 +887,7 @@ export default React.createClass({
|
|||||||
style={paginationProps.style}
|
style={paginationProps.style}
|
||||||
{...paginationProps.rest}
|
{...paginationProps.rest}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{!pageRows.length && (
|
{!pageRows.length && (
|
||||||
<NoDataComponent
|
<NoDataComponent
|
||||||
{...noDataProps}
|
{...noDataProps}
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ $expandSize = 7px
|
|||||||
background: alpha(black, .03)
|
background: alpha(black, .03)
|
||||||
border-bottom: 1px solid alpha(black, .05)
|
border-bottom: 1px solid alpha(black, .05)
|
||||||
|
|
||||||
|
&.-filters
|
||||||
|
border-bottom: 1px solid alpha(black, 0.05)
|
||||||
|
|
||||||
|
.rt-th
|
||||||
|
border-right: 1px solid alpha(black, 0.02)
|
||||||
|
|
||||||
&.-header
|
&.-header
|
||||||
box-shadow: 0 2px 15px 0px alpha(black, .15)
|
box-shadow: 0 2px 15px 0px alpha(black, .15)
|
||||||
|
|
||||||
|
|||||||
+17
-5
@@ -11,7 +11,8 @@ export default {
|
|||||||
page: 0,
|
page: 0,
|
||||||
pageSize: this.props.defaultPageSize || 10,
|
pageSize: this.props.defaultPageSize || 10,
|
||||||
sorting: this.props.defaultSorting,
|
sorting: this.props.defaultSorting,
|
||||||
expandedRows: {}
|
expandedRows: {},
|
||||||
|
filtering: this.props.defaultFiltering
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -41,12 +42,19 @@ export default {
|
|||||||
newState.sorting = newState.defaultSorting
|
newState.sorting = newState.defaultSorting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((oldState.showFilters !== newState.showFilters) ||
|
||||||
|
(oldState.showFilters !== newState.showFilters)) {
|
||||||
|
newState.filtering = newState.defaultFiltering
|
||||||
|
}
|
||||||
|
|
||||||
// Props that trigger a data update
|
// Props that trigger a data update
|
||||||
if (
|
if (
|
||||||
oldState.data !== newState.data ||
|
oldState.data !== newState.data ||
|
||||||
oldState.columns !== newState.columns ||
|
oldState.columns !== newState.columns ||
|
||||||
oldState.pivotBy !== newState.pivotBy ||
|
oldState.pivotBy !== newState.pivotBy ||
|
||||||
oldState.sorting !== newState.sorting
|
oldState.sorting !== newState.sorting ||
|
||||||
|
oldState.showFilters !== newState.showFilters ||
|
||||||
|
oldState.filtering !== newState.filtering
|
||||||
) {
|
) {
|
||||||
this.setStateWithData(this.getDataModel(newState))
|
this.setStateWithData(this.getDataModel(newState))
|
||||||
}
|
}
|
||||||
@@ -55,7 +63,7 @@ export default {
|
|||||||
setStateWithData (newState, cb) {
|
setStateWithData (newState, cb) {
|
||||||
const oldState = this.getResolvedState()
|
const oldState = this.getResolvedState()
|
||||||
const newResolvedState = this.getResolvedState({}, newState)
|
const newResolvedState = this.getResolvedState({}, newState)
|
||||||
const { freezeWhenExpanded } = newResolvedState
|
const {freezeWhenExpanded} = newResolvedState
|
||||||
|
|
||||||
// Default to unfrozen state
|
// Default to unfrozen state
|
||||||
newResolvedState.frozen = false
|
newResolvedState.frozen = false
|
||||||
@@ -77,11 +85,15 @@ export default {
|
|||||||
if (
|
if (
|
||||||
(oldState.frozen && !newResolvedState.frozen) ||
|
(oldState.frozen && !newResolvedState.frozen) ||
|
||||||
oldState.sorting !== newResolvedState.sorting ||
|
oldState.sorting !== newResolvedState.sorting ||
|
||||||
|
oldState.filtering !== newResolvedState.filtering ||
|
||||||
|
oldState.showFilters !== newResolvedState.showFilters ||
|
||||||
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData)
|
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData)
|
||||||
) {
|
) {
|
||||||
// Handle collapseOnSortingChange & collapseOnDataChange
|
// Handle collapseOnSortingChange & collapseOnDataChange
|
||||||
if (
|
if (
|
||||||
(oldState.sorting !== newResolvedState.sorting && this.props.collapseOnSortingChange) ||
|
(oldState.sorting !== newResolvedState.sorting && this.props.collapseOnSortingChange) ||
|
||||||
|
(oldState.filtering !== newResolvedState.filtering) ||
|
||||||
|
(oldState.showFilters !== newResolvedState.showFilters) ||
|
||||||
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData && this.props.collapseOnDataChange)
|
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData && this.props.collapseOnDataChange)
|
||||||
) {
|
) {
|
||||||
newResolvedState.expandedRows = {}
|
newResolvedState.expandedRows = {}
|
||||||
@@ -91,8 +103,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate pageSize all the time
|
// Calculate pageSize all the time
|
||||||
if (newResolvedState.resolvedData) {
|
if (newResolvedState.sortedData) {
|
||||||
newResolvedState.pages = newResolvedState.manual ? newResolvedState.pages : Math.ceil(newResolvedState.resolvedData.length / newResolvedState.pageSize)
|
newResolvedState.pages = newResolvedState.manual ? newResolvedState.pages : Math.ceil(newResolvedState.sortedData.length / newResolvedState.pageSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.setState(newResolvedState, cb)
|
return this.setState(newResolvedState, cb)
|
||||||
|
|||||||
+68
-23
@@ -192,15 +192,14 @@ export default {
|
|||||||
// Group the rows together for this level
|
// Group the rows together for this level
|
||||||
let groupedRows = Object.entries(
|
let groupedRows = Object.entries(
|
||||||
_.groupBy(rows, keys[i]))
|
_.groupBy(rows, keys[i]))
|
||||||
.map(([key, value]) => {
|
.map(([key, value]) => {
|
||||||
return {
|
return {
|
||||||
[pivotIDKey]: keys[i],
|
[pivotIDKey]: keys[i],
|
||||||
[pivotValKey]: key,
|
[pivotValKey]: key,
|
||||||
[keys[i]]: key,
|
[keys[i]]: key,
|
||||||
[subRowsKey]: value
|
[subRowsKey]: value
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
// Recurse into the subRows
|
// Recurse into the subRows
|
||||||
groupedRows = groupedRows.map(rowGroup => {
|
groupedRows = groupedRows.map(rowGroup => {
|
||||||
let subRows = groupRecursively(rowGroup[subRowsKey], keys, i + 1)
|
let subRows = groupRecursively(rowGroup[subRowsKey], keys, i + 1)
|
||||||
@@ -233,12 +232,16 @@ export default {
|
|||||||
const {
|
const {
|
||||||
manual,
|
manual,
|
||||||
sorting,
|
sorting,
|
||||||
resolvedData
|
filtering,
|
||||||
|
showFilters,
|
||||||
|
defaultFilterMethod,
|
||||||
|
resolvedData,
|
||||||
|
allVisibleColumns
|
||||||
} = resolvedState
|
} = resolvedState
|
||||||
|
|
||||||
// Resolve the data from either manual data or sorted data
|
// Resolve the data from either manual data or sorted data
|
||||||
return {
|
return {
|
||||||
sortedData: manual ? resolvedData : this.sortData(resolvedData, sorting)
|
sortedData: manual ? resolvedData : this.sortData(resolvedData, sorting, showFilters, filtering, defaultFilterMethod, allVisibleColumns)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -251,11 +254,28 @@ export default {
|
|||||||
getStateOrProp (key) {
|
getStateOrProp (key) {
|
||||||
return _.getFirstDefined(this.state[key], this.props[key])
|
return _.getFirstDefined(this.state[key], this.props[key])
|
||||||
},
|
},
|
||||||
sortData (data, sorting) {
|
sortData (data, sorting, showFilters, filtering, defaultFilterMethod, allVisibleColumns) {
|
||||||
if (!sorting.length) {
|
let filteredData = data
|
||||||
return data
|
|
||||||
|
if (showFilters && filtering.length) {
|
||||||
|
filteredData = filtering.reduce(
|
||||||
|
(filteredSoFar, nextFilter) => {
|
||||||
|
return filteredSoFar.filter(
|
||||||
|
(row) => {
|
||||||
|
const column = allVisibleColumns.find(x => x.id === nextFilter.id) || {}
|
||||||
|
const filterMethod = column.filterMethod || defaultFilterMethod
|
||||||
|
return filterMethod(nextFilter, row, column)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
, filteredData
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const sorted = _.orderBy(data, sorting.map(sort => {
|
|
||||||
|
if (!sorting.length) {
|
||||||
|
return filteredData
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted = _.orderBy(filteredData, sorting.map(sort => {
|
||||||
return row => {
|
return row => {
|
||||||
if (row[sort.id] === null || row[sort.id] === undefined) {
|
if (row[sort.id] === null || row[sort.id] === undefined) {
|
||||||
return -Infinity
|
return -Infinity
|
||||||
@@ -281,23 +301,23 @@ export default {
|
|||||||
|
|
||||||
// User actions
|
// User actions
|
||||||
onPageChange (page) {
|
onPageChange (page) {
|
||||||
const { onPageChange, collapseOnPageChange } = this.props
|
const {onPageChange, collapseOnPageChange} = this.props
|
||||||
if (onPageChange) {
|
if (onPageChange) {
|
||||||
return onPageChange(page)
|
return onPageChange(page)
|
||||||
}
|
}
|
||||||
const newState = { page }
|
const newState = {page}
|
||||||
if (collapseOnPageChange) {
|
if (collapseOnPageChange) {
|
||||||
newState.expandedRows = {}
|
newState.expandedRows = {}
|
||||||
}
|
}
|
||||||
this.setStateWithData(
|
this.setStateWithData(
|
||||||
newState
|
newState
|
||||||
, () => {
|
, () => {
|
||||||
this.fireOnChange()
|
this.fireOnChange()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onPageSizeChange (newPageSize) {
|
onPageSizeChange (newPageSize) {
|
||||||
const { onPageSizeChange } = this.props
|
const {onPageSizeChange} = this.props
|
||||||
const { pageSize, page } = this.getResolvedState()
|
const {pageSize, page} = this.getResolvedState()
|
||||||
|
|
||||||
// Normalize the page to display
|
// Normalize the page to display
|
||||||
const currentRow = pageSize * page
|
const currentRow = pageSize * page
|
||||||
@@ -315,8 +335,8 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
sortColumn (column, additive) {
|
sortColumn (column, additive) {
|
||||||
const { sorting } = this.getResolvedState()
|
const {sorting} = this.getResolvedState()
|
||||||
const { onSortingChange } = this.props
|
const {onSortingChange} = this.props
|
||||||
if (onSortingChange) {
|
if (onSortingChange) {
|
||||||
return onSortingChange(column, additive)
|
return onSortingChange(column, additive)
|
||||||
}
|
}
|
||||||
@@ -398,5 +418,30 @@ export default {
|
|||||||
}, () => {
|
}, () => {
|
||||||
this.fireOnChange()
|
this.fireOnChange()
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
filterColumn (column, event) {
|
||||||
|
const {filtering} = this.getResolvedState()
|
||||||
|
const {onFilteringChange} = this.props
|
||||||
|
|
||||||
|
if (onFilteringChange) {
|
||||||
|
return onFilteringChange(column, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove old filter first if it exists
|
||||||
|
const newFiltering = (filtering || []).filter(x => x.id !== column.id)
|
||||||
|
|
||||||
|
if (event.target.value !== '') {
|
||||||
|
newFiltering.push({
|
||||||
|
id: column.id,
|
||||||
|
value: event.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setStateWithData({
|
||||||
|
page: 0,
|
||||||
|
filtering: newFiltering
|
||||||
|
}, () => {
|
||||||
|
this.fireOnChange()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,206 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import namor from 'namor'
|
||||||
|
|
||||||
|
import CodeHighlight from './components/codeHighlight'
|
||||||
|
import ReactTable from '../src/index'
|
||||||
|
|
||||||
|
class Filtering extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
const data = _.map(_.range(5553), d => {
|
||||||
|
return {
|
||||||
|
firstName: namor.generate({words: 1, numLen: 0}),
|
||||||
|
lastName: namor.generate({words: 1, numLen: 0}),
|
||||||
|
age: Math.floor(Math.random() * 30)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
tableOptions: {
|
||||||
|
loading: false,
|
||||||
|
showPagination: true,
|
||||||
|
showPageSizeOptions: true,
|
||||||
|
showPageJump: true,
|
||||||
|
collapseOnSortingChange: true,
|
||||||
|
collapseOnPageChange: true,
|
||||||
|
collapseOnDataChange: true,
|
||||||
|
freezeWhenExpanded: false,
|
||||||
|
showFilters: true
|
||||||
|
},
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setTableOption = this.setTableOption.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const columns = [{
|
||||||
|
header: 'Name',
|
||||||
|
columns: [{
|
||||||
|
header: 'First Name',
|
||||||
|
accessor: 'firstName',
|
||||||
|
filterMethod: (filter, row) => (row[filter.id].startsWith(filter.value) && row[filter.id].endsWith(filter.value))
|
||||||
|
}, {
|
||||||
|
header: 'Last Name',
|
||||||
|
id: 'lastName',
|
||||||
|
accessor: d => d.lastName,
|
||||||
|
filterMethod: (filter, row) => (row[filter.id].includes(filter.value))
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
header: 'Info',
|
||||||
|
columns: [{
|
||||||
|
header: 'Age',
|
||||||
|
accessor: 'age'
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div style={{float: "left"}}>
|
||||||
|
<h1>Table Options</h1>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{
|
||||||
|
Object.keys(this.state.tableOptions).map(optionKey => {
|
||||||
|
const optionValue = this.state.tableOptions[optionKey];
|
||||||
|
return (
|
||||||
|
<tr key={optionKey}>
|
||||||
|
<td>{optionKey}</td>
|
||||||
|
<td style={{paddingLeft: 10, paddingTop: 5}}>
|
||||||
|
<input type="checkbox"
|
||||||
|
name={optionKey}
|
||||||
|
checked={optionValue}
|
||||||
|
onChange={this.setTableOption}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div className='table-wrap' style={{paddingLeft: 240}}>
|
||||||
|
<ReactTable
|
||||||
|
className='-striped -highlight'
|
||||||
|
data={this.state.data}
|
||||||
|
columns={columns}
|
||||||
|
defaultPageSize={10}
|
||||||
|
defaultFilterMethod={(filter, row) => ((row[filter.id] + "").startsWith(filter.value))}
|
||||||
|
{...this.state.tableOptions}
|
||||||
|
SubComponent={(row) => {
|
||||||
|
return (
|
||||||
|
<div style={{padding: '20px'}}>
|
||||||
|
<em>You can put any component you want here, even another React Table!</em>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<ReactTable
|
||||||
|
data={this.state.data}
|
||||||
|
columns={columns}
|
||||||
|
defaultPageSize={3}
|
||||||
|
showPagination={false}
|
||||||
|
SubComponent={(row) => {
|
||||||
|
return (
|
||||||
|
<div style={{padding: '20px'}}>
|
||||||
|
<em>It even has access to the row data: </em>
|
||||||
|
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{textAlign: 'center'}}>
|
||||||
|
<br />
|
||||||
|
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1>Custom Filters In This Example</h1>
|
||||||
|
<p>The default filter for all columns of a table if it is not specified in the configuration is set to be an exact match. Example: age == "23".</p>
|
||||||
|
<p>This example overrides the default filter behavior by setting the <strong>defaultFilterMethod</strong> table option to match on values that start with the filter text. Example: age.startsWith("2")</p>
|
||||||
|
<p>Each column can also be customized with the column <strong>filterMethod</strong> option:</p>
|
||||||
|
<p>In this example the firstName column filters on the value starting with and ending with the filter value.</p>
|
||||||
|
<p>In this example the lastName column filters on the value including the filter value anywhere in its text.</p>
|
||||||
|
</div>
|
||||||
|
<CodeHighlight>{() => this.getCode()}</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
setTableOption(event) {
|
||||||
|
const target = event.target;
|
||||||
|
const value = target.type === 'checkbox' ? target.checked : target.value;
|
||||||
|
const name = target.name;
|
||||||
|
this.setState({
|
||||||
|
tableOptions: {
|
||||||
|
...this.state.tableOptions,
|
||||||
|
[name]: value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getCode() {
|
||||||
|
return `
|
||||||
|
const columns = [{
|
||||||
|
header: 'Name',
|
||||||
|
columns: [{
|
||||||
|
header: 'First Name',
|
||||||
|
accessor: 'firstName',
|
||||||
|
filterMethod: (filter, row) => (row[filter.id].startsWith(filter.value) && row[filter.id].endsWith(filter.value))
|
||||||
|
}, {
|
||||||
|
header: 'Last Name',
|
||||||
|
id: 'lastName',
|
||||||
|
accessor: d => d.lastName,
|
||||||
|
filterMethod: (filter, row) => (row[filter.id].includes(filter.value))
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
header: 'Info',
|
||||||
|
columns: [{
|
||||||
|
header: 'Age',
|
||||||
|
accessor: 'age'
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
|
||||||
|
export default (
|
||||||
|
<ReactTable
|
||||||
|
data={data}
|
||||||
|
columns={columns}
|
||||||
|
defaultPageSize={10}
|
||||||
|
defaultFilterMethod={(filter, row) => ((row[filter.id] + \"\").startsWith(filter.value))}
|
||||||
|
{...otherOptions}
|
||||||
|
SubComponent={(row) => {
|
||||||
|
return (
|
||||||
|
<div style={{padding: '20px'}}>
|
||||||
|
<em>You can put any component you want here, even another React Table!</em>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<ReactTable
|
||||||
|
data={data}
|
||||||
|
columns={columns}
|
||||||
|
defaultPageSize={3}
|
||||||
|
showPagination={false}
|
||||||
|
SubComponent={(row) => {
|
||||||
|
return (
|
||||||
|
<div style={{padding: '20px'}}>
|
||||||
|
<em>It even has access to the row data: </em>
|
||||||
|
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default () => <Filtering/>
|
||||||
@@ -33,7 +33,8 @@ export default () => {
|
|||||||
aggregate: vals => _.round(_.mean(vals)),
|
aggregate: vals => _.round(_.mean(vals)),
|
||||||
render: row => {
|
render: row => {
|
||||||
return <span>{row.aggregated ? `${row.value} (avg)` : row.value}</span>
|
return <span>{row.aggregated ? `${row.value} (avg)` : row.value}</span>
|
||||||
}
|
},
|
||||||
|
filterMethod: (filter, row) => (filter.value == `${row[filter.id]} (avg)`)
|
||||||
}, {
|
}, {
|
||||||
header: 'Visits',
|
header: 'Visits',
|
||||||
accessor: 'visits',
|
accessor: 'visits',
|
||||||
@@ -50,6 +51,7 @@ export default () => {
|
|||||||
defaultPageSize={10}
|
defaultPageSize={10}
|
||||||
className='-striped -highlight'
|
className='-striped -highlight'
|
||||||
pivotBy={['firstName', 'lastName']}
|
pivotBy={['firstName', 'lastName']}
|
||||||
|
showFilters={true}
|
||||||
SubComponent={(row) => {
|
SubComponent={(row) => {
|
||||||
return (
|
return (
|
||||||
<div style={{padding: '20px'}}>
|
<div style={{padding: '20px'}}>
|
||||||
@@ -104,7 +106,8 @@ const columns = [{
|
|||||||
aggregate: vals => _.round(_.mean(vals)),
|
aggregate: vals => _.round(_.mean(vals)),
|
||||||
render: row => {
|
render: row => {
|
||||||
return <span>{row.aggregated ? \`\${row.value} (avg)\` : row.value}</span>
|
return <span>{row.aggregated ? \`\${row.value} (avg)\` : row.value}</span>
|
||||||
}
|
},
|
||||||
|
filterMethod: (filter, row) => (filter.value == \`\${row[filter.id]} (avg)\`)
|
||||||
}, {
|
}, {
|
||||||
header: 'Visits',
|
header: 'Visits',
|
||||||
accessor: 'visits',
|
accessor: 'visits',
|
||||||
@@ -119,6 +122,7 @@ return (
|
|||||||
defaultPageSize={10}
|
defaultPageSize={10}
|
||||||
className='-striped -highlight'
|
className='-striped -highlight'
|
||||||
pivotBy={['firstName', 'lastName']}
|
pivotBy={['firstName', 'lastName']}
|
||||||
|
showFilters={true}
|
||||||
SubComponent={(row) => {
|
SubComponent={(row) => {
|
||||||
return (
|
return (
|
||||||
<div style={{padding: '20px'}}>
|
<div style={{padding: '20px'}}>
|
||||||
|
|||||||
+20
-6
@@ -14,23 +14,34 @@ const rawData = _.map(_.range(3424), d => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Now let's mock the server. It's job is simple: use the table model to sort and return the page data
|
// Now let's mock the server. It's job is simple: use the table model to sort and return the page data
|
||||||
const requestData = (pageSize, page, sorting) => {
|
const requestData = (pageSize, page, sorting, filtering) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// On the server, you'll likely use SQL or noSQL or some other query language to do this.
|
// On the server, you'll likely use SQL or noSQL or some other query language to do this.
|
||||||
// For this mock, we'll just use lodash
|
// For this mock, we'll just use lodash
|
||||||
const sortedData = _.orderBy(rawData, sorting.map(sort => {
|
let filteredData = rawData
|
||||||
|
if (filtering.length) {
|
||||||
|
filteredData = filtering.reduce(
|
||||||
|
(filteredSoFar, nextFilter) => {
|
||||||
|
return filteredSoFar.filter(
|
||||||
|
(row) => {
|
||||||
|
return (row[nextFilter.id] + '').includes(nextFilter.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
, filteredData)
|
||||||
|
}
|
||||||
|
const sortedData = _.orderBy(filteredData, sorting.map(sort => {
|
||||||
return row => {
|
return row => {
|
||||||
if (row[sort.id] === null || row[sort.id] === undefined) {
|
if (row[sort.id] === null || row[sort.id] === undefined) {
|
||||||
return -Infinity
|
return -Infinity
|
||||||
}
|
}
|
||||||
return typeof row[sort.id] === 'string' ? row[sort.id].toLowerCase() : row[sort.id]
|
return typeof row[sort.id] === 'string' ? row[sort.id].toLowerCase() : row[sort.id]
|
||||||
}
|
}
|
||||||
}), sorting.map(d => d.asc ? 'asc' : 'desc'))
|
}), sorting.map(d => d.desc ? 'desc' : 'asc'))
|
||||||
|
|
||||||
// Be sure to send back the rows to be displayed and any other pertinent information, like how many pages there are total.
|
// Be sure to send back the rows to be displayed and any other pertinent information, like how many pages there are total.
|
||||||
const res = {
|
const res = {
|
||||||
rows: sortedData.slice(pageSize * page, (pageSize * page) + pageSize),
|
rows: sortedData.slice(pageSize * page, (pageSize * page) + pageSize),
|
||||||
pages: Math.ceil(rawData.length / pageSize)
|
pages: Math.ceil(filteredData.length / pageSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we'll simulate a server response with 500ms of delay.
|
// Here we'll simulate a server response with 500ms of delay.
|
||||||
@@ -52,7 +63,7 @@ const ServerSide = React.createClass({
|
|||||||
// You can set the `loading` prop of the table to true to use the built-in one or show you're own loading bar if you want.
|
// You can set the `loading` prop of the table to true to use the built-in one or show you're own loading bar if you want.
|
||||||
this.setState({loading: true})
|
this.setState({loading: true})
|
||||||
// Request the data however you want. Here, we'll use our mocked service we created earlier
|
// Request the data however you want. Here, we'll use our mocked service we created earlier
|
||||||
requestData(state.pageSize, state.page, state.sorting)
|
requestData(state.pageSize, state.page, state.sorting, state.filtering)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log(res.rows)
|
console.log(res.rows)
|
||||||
// Now just get the rows of data to your React Table (and update anything else like total pages or loading)
|
// Now just get the rows of data to your React Table (and update anything else like total pages or loading)
|
||||||
@@ -82,6 +93,7 @@ const ServerSide = React.createClass({
|
|||||||
}]}
|
}]}
|
||||||
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
|
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
|
||||||
defaultPageSize={10}
|
defaultPageSize={10}
|
||||||
|
showFilters
|
||||||
data={this.state.data} // Set the rows to be displayed
|
data={this.state.data} // Set the rows to be displayed
|
||||||
pages={this.state.pages} // Display the total number of pages
|
pages={this.state.pages} // Display the total number of pages
|
||||||
loading={this.state.loading} // Display the loading overlay when we need it
|
loading={this.state.loading} // Display the loading overlay when we need it
|
||||||
@@ -124,7 +136,8 @@ export default React.createClass({
|
|||||||
Axios.post('mysite.com/data', {
|
Axios.post('mysite.com/data', {
|
||||||
pageSize: state.pageSize,
|
pageSize: state.pageSize,
|
||||||
page: state.page,
|
page: state.page,
|
||||||
sorting: state.sorting
|
sorting: state.sorting,
|
||||||
|
filtering: state.filtering
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
// Now update your state!
|
// Now update your state!
|
||||||
@@ -151,6 +164,7 @@ export default React.createClass({
|
|||||||
}]}
|
}]}
|
||||||
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
|
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
|
||||||
defaultPageSize={10}
|
defaultPageSize={10}
|
||||||
|
showFilters={true}
|
||||||
data={this.state.data} // Set the rows to be displayed
|
data={this.state.data} // Set the rows to be displayed
|
||||||
pages={this.state.pages} // Display the total number of pages
|
pages={this.state.pages} // Display the total number of pages
|
||||||
loading={this.state.loading} // Display the loading overlay when we need it
|
loading={this.state.loading} // Display the loading overlay when we need it
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class SubComponents extends React.Component {
|
|||||||
collapseOnSortingChange: true,
|
collapseOnSortingChange: true,
|
||||||
collapseOnPageChange: true,
|
collapseOnPageChange: true,
|
||||||
collapseOnDataChange: true,
|
collapseOnDataChange: true,
|
||||||
freezeWhenExpanded: false
|
freezeWhenExpanded: false,
|
||||||
|
showFilters: false
|
||||||
},
|
},
|
||||||
data: data
|
data: data
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user