You can put any component you want here, even another React Table! You even have access to the row-level data if you need! Spark-charts, drill-throughs, infographics... the possibilities are endless!
@@ -226,7 +226,6 @@ By adding a `subComponent` props, you can easily add an expansion level to all r
```
-
## Server-side Data
If you want to handle pagination, and sorting on the server, `react-table` makes it easy on you.
@@ -267,38 +266,38 @@ If you want to handle pagination, and sorting on the server, `react-table` makes
For a detailed example, take a peek at our [async table mockup](https://github.com/tannerlinsley/react-table/blob/master/example/src/screens/async.js)
-
## Multi-Sort
When clicking on a column header, hold shift to multi-sort! You can toggle `ascending` `descending` and `none` for multi-sort columns. Clicking on a header without holding shift will clear the multi-sort and replace it with the single sort of that column. It's quite handy!
-
## Component Overrides
-Though we wouldn't suggest it, `react-table` has the ability to change the core componentry it used to render it's table. You can do so by assigning a react component to it's corresponding global prop, or on a one-off basis like so:
+Though we confidently stand by the markup and architecture behind it, `react-table` does offer the ability to change the core componentry it uses to render everything. You can extend or override these internal components by passing a react component to it's corresponding prop on either the global props or on a one-off basis like so:
```javascript
// Change the global default
import { ReactTableDefaults } from 'react-table'
Object.assign(ReactTableDefaults, {
- tableComponent: Component,
- theadComponent: Component,
- tbodyComponent: Component,
- trComponent: Component,
- thComponent: Component,
- tdComponent: Component,
- paginationComponent: Component,
- previousComponent: Component,
- nextComponent: Component,
- loadingComponent: Component
+ TableComponent: Component,
+ TheadComponent: Component,
+ TbodyComponent: Component,
+ TrGroupComponent: Component,
+ TrComponent: Component,
+ ThComponent: Component,
+ TdComponent: Component,
+ PaginationComponent: Component,
+ PreviousComponent: Component,
+ NextComponent: Component,
+ LoadingComponent: Component,
+ ExpanderComponent: Component
})
// Or change per instance
```
-If you choose to change the core components React-Table uses to render, you must make sure your components support and utilized all of the neeeded features for that component to work properly. For a broad reference on how to do this, investigate [the source](https://github.com/tannerlinsley/react-table/blob/master/src/index.js) for the component you wish to replace.
+If you choose to change the core components React-Table uses to render, you must make sure your replacement components consume and utilize all of the supplied and inherited props that are needed for that component to function properly. We would suggest investigating [the source](https://github.com/tannerlinsley/react-table/blob/master/src/index.js) for the component you wish to replace.
## Contributing
diff --git a/src/index.js b/src/index.js
index 96e9ae2..4a3ac23 100644
--- a/src/index.js
+++ b/src/index.js
@@ -6,25 +6,23 @@ import _ from './utils'
import Pagination from './pagination'
-const makeTemplateComponent = (compClass) => ({children, className, ...rest}) => (
-
- {children}
-
-)
-
export const ReactTableDefaults = {
// General
data: [],
loading: false,
- pageSize: 20,
showPagination: true,
showPageSizeOptions: true,
pageSizeOptions: [5, 10, 20, 25, 50, 100],
+ defaultPageSize: 20,
showPageJump: true,
expanderColumnWidth: 30,
+
+ // State Overrides (for controlled-component style)
+ // page
+ // pageSize
+ // sorting
+ // visibleSubComponents
+
// Callbacks
onChange: () => null,
onTrClick: () => null,
@@ -69,12 +67,12 @@ export const ReactTableDefaults = {
ofText: 'of',
rowsText: 'rows',
// Components
- tableComponent: makeTemplateComponent('rt-table'),
- theadComponent: makeTemplateComponent('rt-thead'),
- tbodyComponent: makeTemplateComponent('rt-tbody'),
- trGroupComponent: makeTemplateComponent('rt-tr-group'),
- trComponent: makeTemplateComponent('rt-tr'),
- thComponent: ({toggleSort, className, children, ...rest}) => {
+ TableComponent: _.makeTemplateComponent('rt-table'),
+ TheadComponent: _.makeTemplateComponent('rt-thead'),
+ TbodyComponent: _.makeTemplateComponent('rt-tbody'),
+ TrGroupComponent: _.makeTemplateComponent('rt-tr-group'),
+ TrComponent: _.makeTemplateComponent('rt-tr'),
+ ThComponent: ({toggleSort, className, children, ...rest}) => {
return (
)
},
- tdComponent: makeTemplateComponent('rt-td'),
- expanderComponent: ({isOpen, toggle, ...rest}) => {
+ TdComponent: _.makeTemplateComponent('rt-td'),
+ ExpanderComponent: ({isOpen, toggle, ...rest}) => {
return (
)
},
- paginationComponent: Pagination,
- previousComponent: null,
- nextComponent: null,
- loadingComponent: props => (
-
+ PaginationComponent: Pagination,
+ PreviousComponent: null,
+ NextComponent: null,
+ LoadingComponent: ({loading, loadingText}) => (
+
- {props.loadingText}
+ {loadingText}
)
@@ -112,91 +110,21 @@ export default React.createClass({
getDefaultProps () {
return ReactTableDefaults
},
+
getInitialState () {
return {
page: 0,
+ pageSize: this.props.defaultPageSize || 10,
sorting: false,
visibleSubComponents: {}
}
},
+
componentDidMount () {
this.fireOnChange()
},
- fireOnChange () {
- this.props.onChange({
- page: this.getPropOrState('page'),
- pageSize: this.getStateOrProp('pageSize'),
- pages: this.getPagesLength(),
- sorting: this.getSorting()
- }, this)
- },
- getPropOrState (key) {
- return _.getFirstDefined(this.props[key], this.state[key])
- },
- getStateOrProp (key) {
- return _.getFirstDefined(this.state[key], this.props[key])
- },
- getInitSorting (columns) {
- if (!columns) {
- return []
- }
- const initSorting = columns.filter(d => {
- return typeof d.sort !== 'undefined'
- }).map(d => {
- return {
- id: d.id,
- asc: d.sort === 'asc'
- }
- })
- return initSorting.length ? initSorting : [{
- id: columns[0].id,
- asc: true
- }]
- },
- sortData (data, sorting) {
- return _.orderBy(data, sorting.map(sort => {
- return row => {
- if (row[sort.id] === null || row[sort.id] === undefined) {
- return -Infinity
- }
- return typeof row[sort.id] === 'string' ? row[sort.id].toLowerCase() : row[sort.id]
- }
- }), sorting.map(d => d.asc ? 'asc' : 'desc'))
- },
- makeDecoratedColumn (column) {
- const dcol = Object.assign({}, this.props.column, column)
-
- if (typeof dcol.accessor === 'string') {
- dcol.id = dcol.id || dcol.accessor
- const accessorString = dcol.accessor
- dcol.accessor = row => _.get(row, accessorString)
- return dcol
- }
-
- if (dcol.accessor && !dcol.id) {
- console.warn(dcol)
- throw new Error('A column id is required if using a non-string accessor for column above.')
- }
-
- if (!dcol.accessor) {
- dcol.accessor = d => undefined
- }
-
- return dcol
- },
- getSorting (columns) {
- return this.props.sorting || (this.state.sorting && this.state.sorting.length ? this.state.sorting : this.getInitSorting(columns))
- },
- getPagesLength () {
- return this.props.manual ? this.props.pages
- : Math.ceil(this.props.data.length / this.getStateOrProp('pageSize'))
- },
- getMinRows () {
- return _.getFirstDefined(this.props.minRows, this.getStateOrProp('pageSize'))
- },
render () {
- const {visibleSubComponents} = this.state
const {
className,
style,
@@ -209,6 +137,7 @@ export default React.createClass({
thClassname,
thStyle,
data,
+ columns,
theadClassName,
tbodyClassName,
tbodyStyle,
@@ -226,8 +155,29 @@ export default React.createClass({
ofText,
rowsText,
paginationClassName,
- expanderColumnWidth
- } = this.props
+ expanderColumnWidth,
+ manual,
+ loadingText,
+ // State
+ visibleSubComponents,
+ loading,
+ pageSize,
+ page,
+ // Components
+ TableComponent,
+ TheadComponent,
+ TbodyComponent,
+ TrGroupComponent,
+ TrComponent,
+ ThComponent,
+ TdComponent,
+ ExpanderComponent,
+ PaginationComponent,
+ PreviousComponent,
+ NextComponent,
+ LoadingComponent,
+ SubComponent
+ } = this.getResolvedState()
// Build Columns
const decoratedColumns = []
@@ -236,8 +186,7 @@ export default React.createClass({
// Determine Header Groups
let hasHeaderGroups = false
- this.props.columns
- .forEach(column => {
+ columns.forEach(column => {
if (column.columns) {
hasHeaderGroups = true
}
@@ -252,7 +201,7 @@ export default React.createClass({
}
// Build the columns and headers
- const visibleColumns = this.props.columns.filter(d => _.getFirstDefined(d.show, true))
+ const visibleColumns = columns.filter(d => _.getFirstDefined(d.show, true))
visibleColumns.forEach((column, i) => {
if (column.columns) {
const nestedColumns = column.columns.filter(d => _.getFirstDefined(d.show, true))
@@ -278,7 +227,7 @@ export default React.createClass({
}
const sorting = this.getSorting(decoratedColumns)
- const accessedData = this.props.data.map((d, i) => {
+ const accessedData = data.map((d, i) => {
const row = {
__original: d,
__index: i
@@ -288,38 +237,22 @@ export default React.createClass({
})
return row
})
- const resolvedData = this.props.manual ? accessedData : this.sortData(accessedData, sorting)
+ const resolvedData = manual ? accessedData : this.sortData(accessedData, sorting)
// Normalize state
- const currentPage = this.getPropOrState('page')
- const pageSize = this.getStateOrProp('pageSize')
const pagesLength = this.getPagesLength()
// Pagination
- const startRow = pageSize * currentPage
+ const startRow = pageSize * page
const endRow = startRow + pageSize
- const pageRows = this.props.manual ? resolvedData : resolvedData.slice(startRow, endRow)
+ const pageRows = manual ? resolvedData : resolvedData.slice(startRow, endRow)
const minRows = this.getMinRows()
const padRows = pagesLength > 1 ? _.range(pageSize - pageRows.length)
: minRows ? _.range(Math.max(minRows - pageRows.length, 0))
: []
- const canPrevious = currentPage > 0
- const canNext = currentPage + 1 < pagesLength
-
- const TableComponent = this.props.tableComponent
- const TheadComponent = this.props.theadComponent
- const TbodyComponent = this.props.tbodyComponent
- const TrGroupComponent = this.props.trGroupComponent
- const TrComponent = this.props.trComponent
- const ThComponent = this.props.thComponent
- const TdComponent = this.props.tdComponent
- const ExpanderComponent = this.props.expanderComponent
- const PaginationComponent = this.props.paginationComponent
- const PreviousComponent = this.props.previousComponent
- const NextComponent = this.props.nextComponent
- const LoadingComponent = this.props.loadingComponent
- const SubComponent = this.props.subComponent
+ const canPrevious = page > 0
+ const canNext = page + 1 < pagesLength
const rowWidth = (SubComponent ? expanderColumnWidth : 0) + _.sum(decoratedColumns.map(d => d.minWidth))
@@ -535,7 +468,7 @@ export default React.createClass({
{showPagination && (
)}
-
+
)
},
+
+ // Helpers
+ getResolvedState () {
+ return {
+ ...this.state,
+ ...this.props,
+ pages: this.getPagesLength(),
+ sorting: this.getSorting()
+ }
+ },
+ fireOnChange () {
+ this.props.onChange(this.getResolvedState(), this)
+ },
+ getPropOrState (key) {
+ return _.getFirstDefined(this.props[key], this.state[key])
+ },
+ getStateOrProp (key) {
+ return _.getFirstDefined(this.state[key], this.props[key])
+ },
+ getInitSorting (columns) {
+ if (!columns) {
+ return []
+ }
+ const initSorting = columns.filter(d => {
+ return typeof d.sort !== 'undefined'
+ }).map(d => {
+ return {
+ id: d.id,
+ asc: d.sort === 'asc'
+ }
+ })
+
+ return initSorting.length ? initSorting : [{
+ id: columns[0].id,
+ asc: true
+ }]
+ },
+ sortData (data, sorting) {
+ return _.orderBy(data, sorting.map(sort => {
+ return row => {
+ if (row[sort.id] === null || row[sort.id] === undefined) {
+ return -Infinity
+ }
+ return typeof row[sort.id] === 'string' ? row[sort.id].toLowerCase() : row[sort.id]
+ }
+ }), sorting.map(d => d.asc ? 'asc' : 'desc'))
+ },
+ makeDecoratedColumn (column) {
+ const dcol = Object.assign({}, this.props.column, column)
+
+ if (typeof dcol.accessor === 'string') {
+ dcol.id = dcol.id || dcol.accessor
+ const accessorString = dcol.accessor
+ dcol.accessor = row => _.get(row, accessorString)
+ return dcol
+ }
+
+ if (dcol.accessor && !dcol.id) {
+ console.warn(dcol)
+ throw new Error('A column id is required if using a non-string accessor for column above.')
+ }
+
+ if (!dcol.accessor) {
+ dcol.accessor = d => undefined
+ }
+
+ return dcol
+ },
+ getSorting (columns) {
+ return this.props.sorting || (this.state.sorting && this.state.sorting.length ? this.state.sorting : this.getInitSorting(columns))
+ },
+ getPagesLength () {
+ return this.props.manual ? this.props.pages
+ : Math.ceil(this.props.data.length / this.getStateOrProp('pageSize'))
+ },
+ getMinRows () {
+ return _.getFirstDefined(this.props.minRows, this.getStateOrProp('pageSize'))
+ },
+
// User actions
setPage (page) {
this.setState({
@@ -570,14 +585,13 @@ export default React.createClass({
this.fireOnChange()
})
},
- setPageSize (pageSize) {
- const currentPageSize = this.getStateOrProp('pageSize')
- const currentPage = this.getPropOrState('page')
- const currentRow = currentPageSize * currentPage
- const page = Math.floor(currentRow / pageSize)
+ setPageSize (newPageSize) {
+ const { pageSize, page } = this.getResolvedState()
+ const currentRow = pageSize * page
+ const newPage = Math.floor(currentRow / pageSize)
this.setState({
- pageSize,
- page
+ pageSize: newPageSize,
+ page: newPage
}, () => {
this.fireOnChange()
})
diff --git a/src/pagination.js b/src/pagination.js
index 4502ae5..7f6d14c 100644
--- a/src/pagination.js
+++ b/src/pagination.js
@@ -10,11 +10,11 @@ const defaultButton = (props) => (
export default React.createClass({
getInitialState () {
return {
- page: this.props.currentPage
+ page: this.props.page
}
},
componentWillReceiveProps (nextProps) {
- this.setState({page: nextProps.currentPage})
+ this.setState({page: nextProps.page})
},
getSafePage (page) {
return Math.min(Math.max(page, 0), this.props.pagesLength - 1)
@@ -27,11 +27,11 @@ export default React.createClass({
applyPage (e) {
e && e.preventDefault()
const page = this.state.page
- this.changePage(page === '' ? this.props.currentPage : page)
+ this.changePage(page === '' ? this.props.page : page)
},
render () {
const {
- currentPage,
+ page,
pagesLength,
showPageSizeOptions,
pageSizeOptions,
@@ -55,7 +55,7 @@ export default React.createClass({
{
if (!canPrevious) return
- this.changePage(currentPage - 1)
+ this.changePage(page - 1)
}}
disabled={!canPrevious}
>
@@ -82,8 +82,8 @@ export default React.createClass({
onBlur={this.applyPage}
/>
- ) : (
- {currentPage + 1}
+ ) : (
+ {page + 1}
)} {this.props.ofText} {pagesLength}
{showPageSizeOptions && (
@@ -109,7 +109,7 @@ export default React.createClass({
{
if (!canNext) return
- this.changePage(currentPage + 1)
+ this.changePage(page + 1)
}}
disabled={!canNext}
>
diff --git a/src/utils.js b/src/utils.js
index 959df6a..d8eed54 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,3 +1,6 @@
+import React from 'react'
+import classnames from 'classnames'
+//
export default {
get,
takeRight,
@@ -7,7 +10,8 @@ export default {
clone,
remove,
getFirstDefined,
- sum
+ sum,
+ makeTemplateComponent
}
function remove (a, b) {
@@ -100,3 +104,14 @@ function sum (arr) {
return a + b
}, 0)
}
+
+function makeTemplateComponent (compClass) {
+ return ({children, className, ...rest}) => (
+
+ {children}
+
+ )
+}
diff --git a/stories/ServerSide.js b/stories/ServerSide.js
index ad8df0b..477a25d 100644
--- a/stories/ServerSide.js
+++ b/stories/ServerSide.js
@@ -85,7 +85,7 @@ const ServerSide = React.createClass({
}]
}]}
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
- pageSize={10}
+ defaultPageSize={10}
data={this.state.data} // Set the rows to be displayed
pages={this.state.pages} // Display the total number of pages
loading={this.state.loading} // Display the loading overlay when we need it
diff --git a/stories/Simple.js b/stories/Simple.js
index de4b945..9f45cee 100644
--- a/stories/Simple.js
+++ b/stories/Simple.js
@@ -38,7 +38,7 @@ export default () => {
@@ -78,7 +78,7 @@ return (
)
`
diff --git a/stories/SubComponents.js b/stories/SubComponents.js
index 1f90c9b..053f799 100644
--- a/stories/SubComponents.js
+++ b/stories/SubComponents.js
@@ -38,7 +38,7 @@ export default () => {
{
return (
@@ -47,7 +47,7 @@ export default () => {
{
return (
@@ -95,7 +95,7 @@ export default (
{
return (
{JSON.stringify(row, null, 2)}