Compare commits

...
9 Commits
Author SHA1 Message Date
Tanner Linsley e059ceec75 4.1.1 2017-01-13 09:42:16 -07:00
Tanner Linsley 09f425aeb3 Redux package size to 5kb 2017-01-13 09:42:07 -07:00
Tanner Linsley ca038e9a69 4.1.0 2017-01-12 11:06:20 -07:00
Tanner Linsley 75c31b9354 Fully controlled component props and callbacks + docs 2017-01-12 11:06:09 -07:00
Tanner Linsley 4b2fa4e47d ignore react-storybook in testing 2017-01-11 20:04:32 -07:00
Tanner Linsley 76e92de670 4.0.1 2017-01-11 19:59:03 -07:00
Tanner Linsley 3c5ba2843d Bug fixes and readme updates 2017-01-11 19:58:52 -07:00
Tanner Linsley 1cb280942d Updated Readme 2017-01-11 16:47:56 -07:00
Tanner Linsley c8c650c729 Readme Updates 2017-01-11 16:34:36 -07:00
9 changed files with 575 additions and 492 deletions
+68 -38
View File
@@ -21,25 +21,24 @@
## <a href="http://react-table.zabapps.com/?selectedKind=2.%20Demos&selectedStory=Client-side%20Data&full=0&down=1&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel" target="\_blank">Demo</a>
## Table of Contents
- [Installation](#nstallation)
- [Installation](#installation)
- [Example](#example)
- [Data](#data)
- [Default Props](#default-props)
- [Props](#props)
- [Columns](#columns)
- [Styles](#styles)
- [Header Groups](#header-groups)
- [Sub Tables & Components](#sub-tables--components)
- [Sub Tables & Components](#sub-tables-components)
- [Server-side Data](#server-side-data)
- [Fully Controlled Component](#fully-controlled-component)
- [Multi-sort](#multi-sort)
- [Component Overrides](#component-overrides)
<a name="installation"></a>
## Installation
```bash
$ npm install react-table
```
<a name="example"></a>
## Example
```javascript
import ReactTable from 'react-table'
@@ -76,19 +75,17 @@ const columns = [{
/>
```
<a name="data"></a>
## Data
Simply pass the `data` prop anything that resembles an array or object. Client-side filtering and pagination is built in, and your table will update gracefully as you change any props. [Server-side data](#server-side-data) is also supported!
Simply pass the `data` prop anything that resembles an array or object. Client-side sorting and pagination are built in, and your table will update gracefully as you change any props. [Server-side data](#server-side-data) is also supported!
<a name="default-props"></a>
## Default Props
These are the default props for the main react component `<ReactTable />`
## Props
These are all of the available props (and their default values) for the main `<ReactTable />` component.
```javascript
{
// General
loading: false, // Whether to show the loading overlay or not
pageSize: 20, // The default page size (this can be changed by the user if `showPageSizeOptions` is enabled)
defaultPageSize: 20, // The default page size (this can be changed by the user if `showPageSizeOptions` is enabled)
minRows: 0, // Ensure this many rows are always rendered, regardless of rows on page
showPagination: true, // Shows or hides the pagination component
showPageJump: true, // Shows or hides the pagination number input
@@ -115,6 +112,7 @@ These are the default props for the main react component `<ReactTable />`
trClassName: '', // ClassName for all `tr` elements
trClassCallback: row => null, // A call back to dynamically add classes (via the classnames module) to a row element
paginationClassName: '' // ClassName for `pagination` element
// Styles
style: {}, // Main style object for the component
tableStyle: {}, // style object for the `table` component
@@ -125,6 +123,16 @@ These are the default props for the main react component `<ReactTable />`
thStyle: {}, // style object for the `th` component
tdStyle: {}, // style object for the `td` component
paginationStyle: {}, // style object for the `paginination` component
// Controlled Props (see Using as a Fully Controlled Component below)
page: undefined,
pageSize: undefined,
sorting: undefined,
visibleSubComponents: undefined,
// Controlled Callbacks
onExpand: undefined,
onPageChange: undefined,
onPageSizeChange: undefined,
}
```
@@ -134,7 +142,7 @@ You can easily override the core defaults like so:
import { ReactTableDefaults } from 'react-table'
Object.assign(ReactTableDefaults, {
pageSize: 10,
defaultPageSize: 10,
minRows: 3,
// etc...
})
@@ -144,15 +152,14 @@ Or just define them on the component per-instance
```javascript
<ReactTable
pageSize={10}
defaultPageSize={10}
minRows={3}
// etc...
/>
```
<a name="columns"></a>
## Columns
`<ReactTable/>` requires a `columns` prop, which is an array of objects with the following properties
`<ReactTable/>` requires a `columns` prop, which is an array of objects containing the following properties
```javascript
[{
@@ -162,7 +169,7 @@ Or just define them on the component per-instance
sortable: true,
sort: 'asc' or 'desc', // used to determine the column sorting on init
show: true, // can be used to hide a column
minWidth: 100 // A minimum width for this column. If there is room, columns will flex to fill
minWidth: 100 // A minimum width for this column. If there is room, columns will flex to fill available space
// Cell Options
className: '', // Set the classname of the `td` element of the column
@@ -185,11 +192,9 @@ Or just define them on the component per-instance
}]
```
<a name="styles"></a>
## Styles
React-table is built to be dropped into existing applications or styled from the ground up, but if you'd like a decent starting point, you can optionally include our default theme `react-table.css`. We think it looks great, honestly :)
<a name="header-groups"></a>
## Header Groups
To group columns with another header column, just nest your columns in a header column like so:
```javascript
@@ -209,13 +214,13 @@ const columns = [{
```
## Sub Tables & Components
By adding a `subComponent` props, you can easily add an expansion level to all root-level rows:
By adding a `SubComponent` props, you can easily add an expansion level to all root-level rows:
```javascript
<ReactTable
data={data}
columns={columns}
pageSize={10}
subComponent={(row) => {
defaultPageSize={10}
SubComponent={(row) => {
return (
<div>
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 +231,6 @@ By adding a `subComponent` props, you can easily add an expansion level to all r
```
<a name="server-side-data"></a>
## Server-side Data
If you want to handle pagination, and sorting on the server, `react-table` makes it easy on you.
@@ -267,38 +271,64 @@ 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)
<a name="multi-sort"></a>
## Fully Controlled Component
React Table by default works fantastically out of the box, but you can achieve even more control and customization if you choose to maintain the state yourself. It is very easy to do, even if you only want to manage *parts* of the state.
Here are the props and their corresponding callbacks that control the state of the a table:
```javascript
<ReactTable
// Props
page={0} // the index of the page you wish to display
pageSize={20} // the number of rows per page to be displayed
sorting={[{
id: 'lastName',
asc: true
}, {
id: 'firstName',
asc: true
}]} // the sorting model for the table
visibleSubComponents={[1,4,5]} // The row indexes on the current page that should appear expanded
// Callbacks
onPageChange={(pageIndex) => {...}} // Called when the page index is changed by the user
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.
onExpand={(index, event) => {...}} // Called when an expander is clicked. Use this to manage `visibleSubComponents`
/>
```
## 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!
<a name="component-overrides"></a>
## 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
<ReactTable
tableComponent={Component},
theadComponent={Component},
TableComponent={Component},
TheadComponent={Component},
// etc...
/>
```
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
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "react-table",
"version": "4.0.0",
"version": "4.1.1",
"description": "A fast, lightweight, opinionated table and datagrid built on React",
"license": "MIT",
"homepage": "https://github.com/tannerlinsley/react-table#readme",
@@ -35,8 +35,7 @@
"deploy": "build-storybook && zab deploy"
},
"dependencies": {
"classnames": "^2.2.5",
"inline-style-prefixer": "^2.0.5"
"classnames": "^2.2.5"
},
"peerDependencies": {
"react": "^15.x.x"
@@ -73,7 +72,8 @@
"dist",
"lib",
"example",
"react-table.js"
"react-table.js",
"stories"
]
},
"babel": {
+434 -407
View File
@@ -1,31 +1,34 @@
import React from 'react'
import classnames from 'classnames'
import prefixAll from 'inline-style-prefixer/static'
//
import _ from './utils'
import Pagination from './pagination'
const makeTemplateComponent = (compClass) => ({children, className, ...rest}) => (
<div
className={classnames(compClass, className)}
{...rest}
>
{children}
</div>
)
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,
// Callbacks
// Controlled State Overrides
// page
// pageSize
// sorting
// visibleSubComponents
// Controlled State Callbacks
onExpand: undefined,
onPageChange: undefined,
onPageSizeChange: undefined,
onSortingChange: undefined,
// General Callbacks
onChange: () => null,
onTrClick: () => null,
// Classes
@@ -69,12 +72,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 (
<div
className={classnames(className, 'rt-th')}
@@ -87,22 +90,22 @@ export const ReactTableDefaults = {
</div>
)
},
tdComponent: makeTemplateComponent('rt-td'),
expanderComponent: ({isOpen, toggle, ...rest}) => {
TdComponent: _.makeTemplateComponent('rt-td'),
ExpanderComponent: ({isExpanded, toggle, ...rest}) => {
return (
<div
className={classnames('rt-expander', isOpen && '-open')}
className={classnames('rt-expander', isExpanded && '-open')}
{...rest}
/>
)
},
paginationComponent: Pagination,
previousComponent: null,
nextComponent: null,
loadingComponent: props => (
<div className={classnames('-loading', {'-active': props.loading})}>
PaginationComponent: Pagination,
PreviousComponent: undefined,
NextComponent: undefined,
LoadingComponent: ({loading, loadingText}) => (
<div className={classnames('-loading', {'-active': loading})}>
<div className='-loading-inner'>
{props.loadingText}
{loadingText}
</div>
</div>
)
@@ -112,23 +115,397 @@ export default React.createClass({
getDefaultProps () {
return ReactTableDefaults
},
getInitialState () {
return {
page: 0,
pageSize: this.props.defaultPageSize || 10,
sorting: false,
visibleSubComponents: {}
visibleSubComponents: []
}
},
componentDidMount () {
this.fireOnChange()
},
fireOnChange () {
this.props.onChange({
page: this.getPropOrState('page'),
pageSize: this.getStateOrProp('pageSize'),
render () {
const resolvedProps = this.getResolvedState()
const {
className,
style,
tableClassName,
tableStyle,
theadGroupClassName,
theadStyle,
trClassName,
trStyle,
thClassname,
thStyle,
data,
columns,
theadClassName,
tbodyClassName,
tbodyStyle,
onTrClick,
trClassCallback,
trStyleCallback,
tdStyle,
showPagination,
paginationClassName,
expanderColumnWidth,
manual,
loadingText,
onExpand,
// State
visibleSubComponents,
loading,
pageSize,
page,
// Components
TableComponent,
TheadComponent,
TbodyComponent,
TrGroupComponent,
TrComponent,
ThComponent,
TdComponent,
ExpanderComponent,
PaginationComponent,
LoadingComponent,
SubComponent
} = resolvedProps
// Build Columns
const decoratedColumns = []
const headerGroups = []
let currentSpan = []
// Determine Header Groups
let hasHeaderGroups = false
columns.forEach(column => {
if (column.columns) {
hasHeaderGroups = true
}
})
// A convenience function to add a header and reset the currentSpan
const addHeader = (columns, column = {}) => {
headerGroups.push(Object.assign({}, column, {
columns: columns
}))
currentSpan = []
}
// Build the columns and headers
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))
nestedColumns.forEach(nestedColumn => {
decoratedColumns.push(this.makeDecoratedColumn(nestedColumn))
})
if (hasHeaderGroups) {
if (currentSpan.length > 0) {
addHeader(currentSpan)
}
addHeader(_.takeRight(decoratedColumns, nestedColumns.length), column)
}
} else {
decoratedColumns.push(this.makeDecoratedColumn(column))
currentSpan.push(_.last(decoratedColumns))
}
})
const columnPercentage = 100 / decoratedColumns.length
if (hasHeaderGroups && currentSpan.length > 0) {
addHeader(currentSpan)
}
const sorting = this.getSorting(decoratedColumns)
const accessedData = data.map((d, i) => {
const row = {
__original: d,
__index: i
}
decoratedColumns.forEach(column => {
row[column.id] = column.accessor(d)
})
return row
})
const resolvedData = manual ? accessedData : this.sortData(accessedData, sorting)
// Normalize state
const pagesLength = this.getPagesLength()
// Pagination
const startRow = pageSize * page
const endRow = startRow + pageSize
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 = page > 0
const canNext = page + 1 < pagesLength
const rowWidth = (SubComponent ? expanderColumnWidth : 0) + _.sum(decoratedColumns.map(d => d.minWidth))
return (
<div
className={classnames(className, 'ReactTable')}
style={style}
>
<TableComponent
className={classnames(tableClassName)}
style={tableStyle}
>
{hasHeaderGroups && (
<TheadComponent
className={classnames(theadGroupClassName, '-headerGroups')}
style={Object.assign({}, theadStyle, {
minWidth: `${rowWidth}px`
})}
>
<TrComponent
className={trClassName}
style={trStyle}
>
{SubComponent && (
<ThComponent
className={classnames(thClassname, 'rt-expander-header')}
style={_.prefixAll({
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
})}
/>
)}
{headerGroups.map((column, i) => {
return (
<ThComponent
key={i}
className={classnames(thClassname, column.headerClassName)}
style={Object.assign({}, thStyle, column.headerStyle, _.prefixAll({
flex: `${column.columns.length * columnPercentage} 0 auto`,
width: `${_.sum(column.columns.map(d => d.minWidth))}px`
}))}
>
{typeof column.header === 'function' ? (
<column.header
data={resolvedData}
column={column}
/>
) : column.header}
</ThComponent>
)
})}
</TrComponent>
</TheadComponent>
)}
<TheadComponent
className={classnames(theadClassName, '-header')}
style={Object.assign({}, theadStyle, {
minWidth: `${rowWidth}px`
})}
>
<TrComponent
className={trClassName}
style={trStyle}
>
{SubComponent && (
<ThComponent
className={classnames(thClassname, 'rt-expander-header')}
style={_.prefixAll({
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
})}
/>
)}
{decoratedColumns.map((column, i) => {
const sort = sorting.find(d => d.id === column.id)
const show = typeof column.show === 'function' ? column.show() : column.show
return (
<ThComponent
key={i}
className={classnames(
thClassname,
column.headerClassName,
sort ? (sort.asc ? '-sort-asc' : '-sort-desc') : '',
{
'-cursor-pointer': column.sortable,
'-hidden': !show
}
)}
style={Object.assign({}, thStyle, column.headerStyle, _.prefixAll({
flex: `${columnPercentage} 0 auto`,
width: `${column.minWidth}px`
}))}
toggleSort={(e) => {
column.sortable && this.sortColumn(column, e.shiftKey)
}}
>
{typeof column.header === 'function' ? (
<column.header
data={resolvedData}
column={column}
/>
) : column.header}
</ThComponent>
)
})}
</TrComponent>
</TheadComponent>
<TbodyComponent
className={classnames(tbodyClassName)}
style={Object.assign({}, tbodyStyle, {
minWidth: `${rowWidth}px`
})}
>
{pageRows.map((row, i) => {
const rowInfo = {
row: row.__original,
rowValues: row,
index: row.__index,
viewIndex: i
}
const visibleSubComponentIndex = visibleSubComponents.indexOf(i)
const isExpanded = visibleSubComponentIndex > -1
return (
<TrGroupComponent key={i}>
<TrComponent
onClick={event => onTrClick(rowInfo.row, event)}
className={classnames(trClassName, trClassCallback(rowInfo))}
style={Object.assign({}, trStyle, trStyleCallback(rowInfo))}
>
{SubComponent && (
<ThComponent
className={classnames(thClassname, 'rt-expander-wrap')}
style={_.prefixAll({
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
})}
onClick={(e) => {
if (onExpand) {
return onExpand(i, e)
}
if (isExpanded) {
return this.setState({
visibleSubComponents: [
...visibleSubComponents.slice(0, visibleSubComponentIndex - 1),
...visibleSubComponents.slice(visibleSubComponentIndex + 1)
]
})
}
this.setState({
visibleSubComponents: [
...visibleSubComponents,
i
]
})
}}
>
<ExpanderComponent
isExpanded={isExpanded}
/>
</ThComponent>
)}
{decoratedColumns.map((column, i2) => {
const Cell = column.render
const show = typeof column.show === 'function' ? column.show() : column.show
return (
<TdComponent
key={i2}
className={classnames(column.className, {hidden: !show})}
style={Object.assign({}, tdStyle, column.style, _.prefixAll({
flex: `${columnPercentage} 0 auto`,
width: `${column.minWidth}px`
}))}
>
{typeof Cell === 'function' ? (
<Cell
{...rowInfo}
value={rowInfo.rowValues[column.id]}
/>
) : typeof Cell !== 'undefined' ? Cell
: rowInfo.rowValues[column.id]}
</TdComponent>
)
})}
</TrComponent>
{SubComponent && isExpanded ? (
SubComponent(rowInfo)
) : null}
</TrGroupComponent>
)
})}
{padRows.map((row, i) => {
return (
<TrComponent
key={i}
className={classnames(trClassName, '-padRow')}
style={trStyle}
>
{SubComponent && (
<ThComponent
className={classnames(thClassname, 'rt-expander-header')}
style={_.prefixAll({
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
})}
/>
)}
{decoratedColumns.map((column, i2) => {
const show = typeof column.show === 'function' ? column.show() : column.show
return (
<TdComponent
key={i2}
className={classnames(column.className, {hidden: !show})}
style={Object.assign({}, tdStyle, column.style, {
flex: `${columnPercentage} 0 auto`,
width: `${column.minWidth}px`
})}
>
&nbsp;
</TdComponent>
)
})}
</TrComponent>
)
})}
</TbodyComponent>
</TableComponent>
{showPagination && (
<PaginationComponent
{...resolvedProps}
pagesLength={pagesLength}
canPrevious={canPrevious}
canNext={canNext}
onPageChange={this.onPageChange}
onPageSizeChange={this.onPageSizeChange}
className={paginationClassName}
/>
)}
<LoadingComponent
loading={loading}
loadingText={loadingText}
/>
</div>
)
},
// Helpers
getResolvedState () {
return {
...this.state,
...this.props,
pages: this.getPagesLength(),
sorting: this.getSorting()
}, this)
}
},
fireOnChange () {
this.props.onChange(this.getResolvedState(), this)
},
getPropOrState (key) {
return _.getFirstDefined(this.props[key], this.state[key])
@@ -195,394 +572,44 @@ export default React.createClass({
getMinRows () {
return _.getFirstDefined(this.props.minRows, this.getStateOrProp('pageSize'))
},
render () {
const {visibleSubComponents} = this.state
const {
className,
style,
tableClassName,
tableStyle,
theadGroupClassName,
theadStyle,
trClassName,
trStyle,
thClassname,
thStyle,
data,
theadClassName,
tbodyClassName,
tbodyStyle,
onTrClick,
trClassCallback,
trStyleCallback,
tdStyle,
showPagination,
showPageSizeOptions,
pageSizeOptions,
showPageJump,
previousText,
nextText,
pageText,
ofText,
rowsText,
paginationClassName,
expanderColumnWidth
} = this.props
// Build Columns
const decoratedColumns = []
const headerGroups = []
let currentSpan = []
// Determine Header Groups
let hasHeaderGroups = false
this.props.columns
.forEach(column => {
if (column.columns) {
hasHeaderGroups = true
}
})
// A convenience function to add a header and reset the currentSpan
const addHeader = (columns, column = {}) => {
headerGroups.push(Object.assign({}, column, {
columns: columns
}))
currentSpan = []
}
// Build the columns and headers
const visibleColumns = this.props.columns.filter(d => _.getFirstDefined(d.show, true))
visibleColumns.forEach((column, i) => {
if (column.columns) {
const nestedColumns = column.columns.filter(d => _.getFirstDefined(d.show, true))
nestedColumns.forEach(nestedColumn => {
decoratedColumns.push(this.makeDecoratedColumn(nestedColumn))
})
if (hasHeaderGroups) {
if (currentSpan.length > 0) {
addHeader(currentSpan)
}
addHeader(_.takeRight(decoratedColumns, nestedColumns.length), column)
}
} else {
decoratedColumns.push(this.makeDecoratedColumn(column))
currentSpan.push(_.last(decoratedColumns))
}
})
const columnPercentage = 100 / decoratedColumns.length
if (hasHeaderGroups && currentSpan.length > 0) {
addHeader(currentSpan)
}
const sorting = this.getSorting(decoratedColumns)
const accessedData = this.props.data.map((d, i) => {
const row = {
__original: d,
__index: i
}
decoratedColumns.forEach(column => {
row[column.id] = column.accessor(d)
})
return row
})
const resolvedData = this.props.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 endRow = startRow + pageSize
const pageRows = this.props.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 rowWidth = (SubComponent ? expanderColumnWidth : 0) + _.sum(decoratedColumns.map(d => d.minWidth))
return (
<div
className={classnames(className, 'ReactTable')}
style={style}
>
<TableComponent
className={classnames(tableClassName)}
style={tableStyle}
>
{hasHeaderGroups && (
<TheadComponent
className={classnames(theadGroupClassName, '-headerGroups')}
style={Object.assign({}, theadStyle, {
minWidth: `${rowWidth}px`
})}
>
<TrComponent
className={trClassName}
style={trStyle}
>
{SubComponent && (
<ThComponent
className={classnames(thClassname, 'rt-expander-header')}
style={prefixAll({
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
})}
/>
)}
{headerGroups.map((column, i) => {
return (
<ThComponent
key={i}
className={classnames(thClassname, column.headerClassName)}
style={Object.assign({}, thStyle, column.headerStyle, prefixAll({
flex: `${column.columns.length * columnPercentage} 0 auto`,
width: `${_.sum(column.columns.map(d => d.minWidth))}px`
}))}
>
{typeof column.header === 'function' ? (
<column.header
data={resolvedData}
column={column}
/>
) : column.header}
</ThComponent>
)
})}
</TrComponent>
</TheadComponent>
)}
<TheadComponent
className={classnames(theadClassName, '-header')}
style={Object.assign({}, theadStyle, {
minWidth: `${rowWidth}px`
})}
>
<TrComponent
className={trClassName}
style={trStyle}
>
{SubComponent && (
<ThComponent
className={classnames(thClassname, 'rt-expander-header')}
style={prefixAll({
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
})}
/>
)}
{decoratedColumns.map((column, i) => {
const sort = sorting.find(d => d.id === column.id)
const show = typeof column.show === 'function' ? column.show() : column.show
return (
<ThComponent
key={i}
className={classnames(
thClassname,
column.headerClassName,
sort ? (sort.asc ? '-sort-asc' : '-sort-desc') : '',
{
'-cursor-pointer': column.sortable,
'-hidden': !show
}
)}
style={Object.assign({}, thStyle, column.headerStyle, prefixAll({
flex: `${columnPercentage} 0 auto`,
width: `${column.minWidth}px`
}))}
toggleSort={(e) => {
column.sortable && this.sortColumn(column, e.shiftKey)
}}
>
{typeof column.header === 'function' ? (
<column.header
data={resolvedData}
column={column}
/>
) : column.header}
</ThComponent>
)
})}
</TrComponent>
</TheadComponent>
<TbodyComponent
className={classnames(tbodyClassName)}
style={Object.assign({}, tbodyStyle, {
minWidth: `${rowWidth}px`
})}
>
{pageRows.map((row, i) => {
const rowInfo = {
row: row.__original,
rowValues: row,
index: row.__index,
viewIndex: i
}
return (
<TrGroupComponent key={i}>
<TrComponent
onClick={event => onTrClick(rowInfo.row, event)}
className={classnames(trClassName, trClassCallback(rowInfo))}
style={Object.assign({}, trStyle, trStyleCallback(rowInfo))}
>
{SubComponent && (
<ThComponent
className={classnames(thClassname, 'rt-expander-wrap')}
style={prefixAll({
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
})}
onClick={() => {
this.setState({
visibleSubComponents: {
...visibleSubComponents,
[i]: !visibleSubComponents[i]
}
})
}}
>
<ExpanderComponent
isOpen={visibleSubComponents[i]}
/>
</ThComponent>
)}
{decoratedColumns.map((column, i2) => {
const Cell = column.render
const show = typeof column.show === 'function' ? column.show() : column.show
return (
<TdComponent
key={i2}
className={classnames(column.className, {hidden: !show})}
style={Object.assign({}, tdStyle, column.style, prefixAll({
flex: `${columnPercentage} 0 auto`,
width: `${column.minWidth}px`
}))}
>
{typeof Cell === 'function' ? (
<Cell
{...rowInfo}
value={rowInfo.rowValues[column.id]}
/>
) : typeof Cell !== 'undefined' ? Cell
: rowInfo.rowValues[column.id]}
</TdComponent>
)
})}
</TrComponent>
{SubComponent && visibleSubComponents[i] ? (
SubComponent(rowInfo)
) : null}
</TrGroupComponent>
)
})}
{padRows.map((row, i) => {
return (
<TrComponent
key={i}
className={classnames(trClassName, '-padRow')}
style={trStyle}
>
{SubComponent && (
<ThComponent
className={classnames(thClassname, 'rt-expander-header')}
style={prefixAll({
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
})}
/>
)}
{decoratedColumns.map((column, i2) => {
const show = typeof column.show === 'function' ? column.show() : column.show
return (
<TdComponent
key={i2}
className={classnames(column.className, {hidden: !show})}
style={Object.assign({}, tdStyle, column.style, {
flex: `${columnPercentage} 0 auto`,
width: `${column.minWidth}px`
})}
>
&nbsp;
</TdComponent>
)
})}
</TrComponent>
)
})}
</TbodyComponent>
</TableComponent>
{showPagination && (
<PaginationComponent
currentPage={currentPage}
pagesLength={pagesLength}
pageSize={pageSize}
showPageSizeOptions={showPageSizeOptions}
pageSizeOptions={pageSizeOptions}
showPageJump={showPageJump}
canPrevious={canPrevious}
canNext={canNext}
previousText={previousText}
nextText={nextText}
pageText={pageText}
ofText={ofText}
rowsText={rowsText}
previousComponent={PreviousComponent}
nextComponent={NextComponent}
//
onChange={this.setPage}
onPageSizeChange={this.setPageSize}
//
className={paginationClassName}
/>
)}
<LoadingComponent {...this.props} />
</div>
)
},
// User actions
setPage (page) {
onPageChange (page) {
const { onPageChange } = this.props
if (onPageChange) {
return onPageChange(page)
}
this.setState({
visibleSubComponents: {},
visibleSubComponents: [],
page
}, () => {
this.fireOnChange()
})
},
setPageSize (pageSize) {
const currentPageSize = this.getStateOrProp('pageSize')
const currentPage = this.getPropOrState('page')
const currentRow = currentPageSize * currentPage
const page = Math.floor(currentRow / pageSize)
onPageSizeChange (newPageSize) {
const { onPageSizeChange } = this.props
const { pageSize, page } = this.getResolvedState()
// Normalize the page to display
const currentRow = pageSize * page
const newPage = Math.floor(currentRow / pageSize)
if (onPageSizeChange) {
return onPageSizeChange(newPageSize, newPage)
}
this.setState({
pageSize,
page
pageSize: newPageSize,
page: newPage
}, () => {
this.fireOnChange()
})
},
sortColumn (column, additive) {
const { onSortingChange } = this.props
if (onSortingChange) {
return onSortingChange(column, additive)
}
const existingSorting = this.getSorting()
let sorting = _.clone(this.state.sorting || [])
const existingIndex = sorting.findIndex(d => d.id === column.id)
+14 -13
View File
@@ -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)
@@ -22,17 +22,19 @@ export default React.createClass({
changePage (page) {
page = this.getSafePage(page)
this.setState({page})
this.props.onChange(page)
this.props.onPageChange(page)
},
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,
// Computed
pagesLength,
// Props
page,
showPageSizeOptions,
pageSizeOptions,
pageSize,
@@ -40,12 +42,11 @@ export default React.createClass({
canPrevious,
canNext,
onPageSizeChange,
className
className,
PreviousComponent = defaultButton,
NextComponent = defaultButton
} = this.props
const PreviousComponent = this.props.previousComponent || defaultButton
const NextComponent = this.props.nextComponent || defaultButton
return (
<div
className={classnames(className, '-pagination')}
@@ -55,7 +56,7 @@ export default React.createClass({
<PreviousComponent
onClick={(e) => {
if (!canPrevious) return
this.changePage(currentPage - 1)
this.changePage(page - 1)
}}
disabled={!canPrevious}
>
@@ -82,8 +83,8 @@ export default React.createClass({
onBlur={this.applyPage}
/>
</form>
) : (
<span className='-currentPage'>{currentPage + 1}</span>
) : (
<span className='-currentPage'>{page + 1}</span>
)} {this.props.ofText} <span className='-totalPages'>{pagesLength}</span>
</span>
{showPageSizeOptions && (
@@ -109,7 +110,7 @@ export default React.createClass({
<NextComponent
onClick={(e) => {
if (!canNext) return
this.changePage(currentPage + 1)
this.changePage(page + 1)
}}
disabled={!canNext}
>
+21 -1
View File
@@ -1,3 +1,6 @@
import React from 'react'
import classnames from 'classnames'
//
export default {
get,
takeRight,
@@ -7,7 +10,9 @@ export default {
clone,
remove,
getFirstDefined,
sum
sum,
makeTemplateComponent,
prefixAll
}
function remove (a, b) {
@@ -100,3 +105,18 @@ function sum (arr) {
return a + b
}, 0)
}
function makeTemplateComponent (compClass) {
return ({children, className, ...rest}) => (
<div
className={classnames(compClass, className)}
{...rest}
>
{children}
</div>
)
}
function prefixAll (obj) {
return obj
}
+2 -2
View File
@@ -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
@@ -111,7 +111,7 @@ function getCode () {
import ReactTable from 'react-table'
import Axios from 'axios'
export default React.creatClass({
export default React.createClass({
getInitialState () {
// To handle our data server-side, we need to keep track of our table state
return {
+2 -2
View File
@@ -38,7 +38,7 @@ export default () => {
<ReactTable
data={data}
columns={columns}
pageSize={10}
defaultPageSize={10}
/>
</div>
<div style={{textAlign: 'center'}}>
@@ -78,7 +78,7 @@ return (
<ReactTable
data={data}
columns={columns}
pageSize={10}
defaultPageSize={10}
/>
)
`
+30 -10
View File
@@ -38,20 +38,22 @@ export default () => {
<ReactTable
data={data}
columns={columns}
pageSize={10}
subComponent={(row) => {
defaultPageSize={10}
SubComponent={(row) => {
return (
<div style={{padding: '20px'}}>
<em>You can put any component you want here, even another React Table!. It has access to the row data: </em>
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
<em>You can put any component you want here, even another React Table!</em>
<br />
<br />
<ReactTable
data={data}
columns={columns}
pageSize={10}
subComponent={(row) => {
defaultPageSize={3}
showPagination={false}
SubComponent={(row) => {
return (
<div style={{padding: '20px'}}>
<em>You can put any component you want here, even another React Table! It even has access to the row data: </em>
<em>It even has access to the row data: </em>
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
</div>
)
@@ -95,10 +97,28 @@ export default (
<ReactTable
data={data}
columns={columns}
pageSize={10}
subComponent={(row) => {
defaultPageSize={10}
SubComponent={(row) => {
return (
<code><pre>{JSON.stringify(row, null, 2)}</pre></code>
<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>
)
}}
/>
-15
View File
@@ -1362,10 +1362,6 @@ boom@2.x.x:
dependencies:
hoek "2.x.x"
bowser@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.6.0.tgz#37fc387b616cb6aef370dab4d6bd402b74c5c54d"
brace-expansion@^1.0.0:
version "1.1.6"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9"
@@ -3000,10 +2996,6 @@ https-browserify@~0.0.0, https-browserify@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
hyphenate-style-name@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b"
iconv-lite@^0.4.13, iconv-lite@~0.4.13:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
@@ -3071,13 +3063,6 @@ inline-source-map@~0.6.0:
dependencies:
source-map "~0.5.3"
inline-style-prefixer:
version "2.0.5"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz#c153c7e88fd84fef5c602e95a8168b2770671fe7"
dependencies:
bowser "^1.0.0"
hyphenate-style-name "^1.0.1"
inquirer@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"