Compare commits

..
2 Commits
Author SHA1 Message Date
Tanner Linsley 2fe97f921f 3.0.0 2016-10-31 01:44:13 -06:00
Tanner Linsley 9a0369ee33 Major changes for server-side data 2016-10-31 01:44:07 -06:00
14 changed files with 172 additions and 550 deletions
-2
View File
@@ -3,5 +3,3 @@ lib/
react-table.js
react-table.css
*.log
dist/
+13 -34
View File
@@ -8,11 +8,10 @@ A fast, lightweight, opinionated table and datagrid built on React
## Features
- Lightweight at 4kb (and just 2kb more for styles)
- Lightweight at 3kb (and just 2kb more for styles)
- Fully customizable JSX and callbacks for everything
- Supports both Client-side & Server-side pagination and sorting
- Minimal design & easily themeable
- ["Why I wrote React Table and the problems it has solved for Nozzle.io"](https://medium.com/@tannerlinsley/why-i-wrote-react-table-and-the-problems-it-has-solved-for-nozzle-others-445c4e93d4a8#.axza4ixba) by Tanner Linsley
## [Demo](http://react-table.zabapps.com)
@@ -52,28 +51,27 @@ const data = [{
const columns = [{
header: 'Name',
accessor: 'name' // String-based value accessors !
accessor: 'name',
}, {
header: 'Age',
accessor: 'age',
render: props => <span className='number'>props.value</span> // Custom cell components!
accessor: 'age'
}, {
header: 'Friend Name',
accessor: d => d.friend.name // Custom value accessors!
accessor: d => d.friend.name
}, {
header: props => <span>Friend Age</span>, // Custom header components!
header: 'Friend Age',
accessor: 'friend.age'
}]
<ReactTable
data={data}
data=[data]
columns={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!
Every React-Table instance requires you to set the `data` prop. To use client-side 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 if you change any props. [Server-side data](#server-side-data) is also supported.
<a name="default-props"></a>
@@ -82,15 +80,8 @@ These are the default props for the main react component `<ReactTable />`
```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)
pageSize: 20,
minRows: 0, // Ensure this many rows are always rendered, regardless of rows on page
showPagination: true, // Shows or hides the pagination component
showPageSizeOptions: true, // Enables the user to change the page size
pageSizeOptions: [5, 10, 20, 25, 50, 100], // The available page size options
// Callbacks
onChange: (state, instance) => null, // Anytime the internal state of the table changes, this will fire
// Text
previousText: 'Previous',
@@ -141,7 +132,7 @@ Or just define them on the component per-instance
<a name="columns"></a>
## Columns
`<ReactTable/>` requires a `columns` prop, which is an array of objects with the following properties
Every React-Table instance requires a `columns` prop, which is an array of objects containing the following properties
```javascript
[{
@@ -149,8 +140,8 @@ Or just define them on the component per-instance
accessor: 'propertyName' or Accessor eg. (row) => row.propertyName,
id: 'myProperty', // Conditional - A unique ID is required if the accessor is not a string or if you would like to override the column name used in server-side calls
sortable: true,
sort: 'asc' or 'desc', // used to determine the column sorting on init
show: true, // can be used to hide a column
sort: 'asc' or 'desc',
show: true,
minWidth: Number // Allows the column to flex above this minimum amount
// Cell Options
@@ -207,7 +198,7 @@ If you want to handle pagination, and sorting on the server, `react-table` makes
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. Subscribe to the `onChange` prop. This function is called at `compomentDidMount` and any time sorting or pagination is changed by the user
1. Subscribe to the `onChange` prop. This function is called any time sorting or pagination is changed by the user
1. In the `onChange` callback, request your data using the provided information in the params of the function (state and instance)
1. Update your data with the rows to be displayed
1. Optionally set how many pages there are total
@@ -217,23 +208,17 @@ If you want to handle pagination, and sorting on the server, `react-table` makes
...
data={this.state.data} // should default to []
pages={this.state.pages} // should default to -1 (which means we don't know how many pages we have)
loading={this.state.loading}
manual // informs React Table that you'll be handling sorting and pagination server-side
onChange={(state, instance) => {
// show the loading overlay
this.setState({loading: true})
// fetch your data
Axios.post('mysite.com/data', {
page: state.page,
pageSize: state.pageSize,
sorting: state.sorting
})
.then((res) => {
// Update react-table
this.setState({
data: res.data.rows,
pages: res.data.pages,
loading: false
pages: res.data.pages
})
})
}}
@@ -295,9 +280,3 @@ If you would like to preview your changes, you can link and utilize the example
- `npm watch`
- Make changes to the example in `src/screens/index.js` if needed
- View changes at `localhost:8000`
## Used By
<a href='https://nozzle.io'>
<img src='https://nozzle.io/img/logo-blue.png' alt='Nozzle Logo' style='width:300px;'/>
</a>
+1 -3
View File
@@ -10,8 +10,7 @@
},
"scripts": {
"watch": "jumpsuit watch",
"build": "jumpsuit build",
"deploy": "jumpsuit build && zab deploy"
"build": "jumpsuit build"
},
"devDependencies": {
"nib": "^1.1.0",
@@ -22,7 +21,6 @@
"jumpsuit": "^0.7.5",
"lodash": "^4.16.4",
"namor": "^0.3.0",
"react-syntax-highlighter": "^3.0.0",
"react-table": "^2.0.0"
}
}
+4 -14
View File
@@ -1,15 +1,5 @@
import { Render, Router, Route, IndexRoute } from 'jumpsuit'
//
import Layout from 'components/layout'
import Simple from 'screens/simple'
import ServerSide from 'screens/serverSide'
import { Render } from 'jumpsuit'
// import App from 'screens/async'
import App from 'screens/index'
Render(null, (
<Router>
<Route path='/' component={Layout}>
<IndexRoute component={Simple} />
<Route path='simple' component={Simple} />
<Route path='server-side' component={ServerSide} />
</Route>
</Router>
))
Render(null, <App />)
+6 -39
View File
@@ -11,7 +11,7 @@ global-reset()
// vendor styles
// -----------------------------------------------------------------------------
@import '../../src/index'
@import '../node_modules/react-table/react-table.css'
// -----------------------------------------------------------------------------
// variables
@@ -34,6 +34,7 @@ body
background: white
font-family: $fnt-open-sans
font-weight: 300
padding-bottom: 50px
h1
font-size: 2.5em
@@ -49,7 +50,6 @@ strong
.logo
width: 400px
max-width: 100%
.container
display: flex
@@ -63,34 +63,11 @@ strong
font-size: 20px
padding: 10px
.viewport
width:100%
.table-wrap
width: 90%
margin: auto
padding: 10px
border-radius: 5px
box-shadow: 0 0 20px 0 alpha(black, .2)
.menu
display:block
margin: 0 10px 20px
ul
display:block
li
display:inline-block
a
display:block
.table-wrap
width: 700px
padding: 10px
margin: 5px
background: alpha(black, .7)
color: white
border-radius: 3px
transition: all .2s ease-out
&.active
&:hover
background: alpha(black, .9)
border-radius: 5px
box-shadow: 0 0 20px 0 alpha(black, .2)
.ReactTable
thead
@@ -100,13 +77,3 @@ strong
box-shadow:inset 0 3px 0 0 alpha(black, .6)
&.-sort-desc
box-shadow:inset 0 -3px 0 0 alpha(black, .6)
pre
display:block
font-family: monospace
font-size: 15px
line-height: 20px
border-radius: 5px
margin: 20px auto
max-width: 90%
padding: 0 20px !important
-1
View File
@@ -2,7 +2,6 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React-Table Demo</title>
<link rel="stylesheet" href="/app.css" charset="utf-8">
</head>
-11
View File
@@ -1,11 +0,0 @@
import { Component } from 'jumpsuit'
import SyntaxHighlighter from 'react-syntax-highlighter'
import atomOneDark from '../../node_modules/react-syntax-highlighter/dist/styles/atom-one-dark'
export default Component({
render () {
return (
<SyntaxHighlighter language='javascript' style={atomOneDark}>{this.props.children}</SyntaxHighlighter>
)
}
})
@@ -2,11 +2,10 @@ import { Component } from 'jumpsuit'
import _ from 'lodash'
import namor from 'namor'
import CodeHighlight from 'components/codeHighlight'
import ReactTable from '../../../lib/index.js'
import ReactTable from 'react-table'
// Let's mock some data to play around with
const rawData = _.map(_.range(3424), d => {
const rawData = _.map(_.range(1000), d => {
return {
firstName: namor.generate({ words: 1, numLen: 0 }),
lastName: namor.generate({ words: 1, numLen: 0 }),
@@ -65,7 +64,39 @@ export default Component({
},
render () {
return (
<div>
<div className='container'>
<div style={{textAlign: 'center'}}>
<h1>
<span style={{position: 'absolute', textIndent: '-9999em'}}>
react-table <strong>server-side demo</strong>
</span>
<img src='/Banner.png' className='logo' />
</h1>
<br />
<div>
<a
className='github-button'
href='https://github.com/tannerlinsley/react-table'
target='_blank'
data-style='mega'
data-count-href='/tannerlinsley/react-table/stargazers'
data-count-api='/repos/tannerlinsley/react-table#stargazers_count'
data-count-aria-label='# stargazers on GitHub'
aria-label='Star tannerlinsley/react-table on GitHub'>
Star
</a>
</div>
<br />
<div className='github-addon'>
<a
target='_blank'
href='https://github.com/tannerlinsley/react-table'>
View on Github
</a>
</div>
<br />
<br />
</div>
<div className='table-wrap'>
<ReactTable
columns={[{
@@ -86,7 +117,7 @@ export default Component({
}]
}]}
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
pageSize={10}
pageSize={5}
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
@@ -97,74 +128,7 @@ export default Component({
<br />
<em>Tip: Hold shift when sorting to multi-sort!</em>
</div>
<CodeHighlight>{getCode()}</CodeHighlight>
</div>
)
}
})
function getCode () {
return `
import ReactTable from 'react-table'
export default React.creatClass({
getInitialState () {
// To handle our data server-side, we need to keep track of our table state
return {
data: [],
pages: null,
loading: true
}
},
fetchData (state, instance) {
// Whenever the table model changes (sorting, pagination, etc), this method gets called and passed the current table model.
// You can set the 'loading' prop of the table to true to use the built-in loading notice, or show you're own loading bar if you want.
this.setState({loading: true})
// Request the data from a server however you want! Be sure to send the bits of the table model that it may neeed.
Axios.post('mysite.com/data', {
pageSize: state.pageSize,
page: state.page,
sorting: state.sorting
})
.then((res) => {
// Now update your state!
this.setState({
data: res.rows,
pages: res.pages,
loading: false
})
})
},
render () {
const columns = [{
header: 'Name',
columns: [{
header: 'First Name',
accessor: 'firstName'
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
}]
}]
return (
<ReactTable
columns={columns}
manual // This forces table not to paginate or sort automatically, so we can handle things server-side
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
onChange={this.fetchData} // Request new data when things change
/>
)
}
})
`
}
@@ -1,13 +1,43 @@
import { Component, Link } from 'jumpsuit'
import { Component } from 'jumpsuit'
import _ from 'lodash'
import namor from 'namor'
import ReactTable from 'react-table'
export default Component({
render () {
const data = _.map(_.range(5000), d => {
return {
firstName: namor.generate({ words: 1, numLen: 0 }),
lastName: namor.generate({ words: 1, numLen: 0 }),
age: Math.floor(Math.random() * 30)
}
})
const columns = [{
header: 'Name',
columns: [{
header: 'First Name',
accessor: 'firstName'
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
}]
}]
return (
<div className='container'>
<div style={{textAlign: 'center'}}>
<h1>
<span style={{position: 'absolute', textIndent: '-9999em'}}>
react-table
react-table <strong>demo</strong>
</span>
<img src='/Banner.png' className='logo' />
</h1>
@@ -36,22 +66,15 @@ export default Component({
<br />
<br />
</div>
<div className='menu'>
<ul>
<li>
<Link to='/simple' activeClassName='active'>
Simple
</Link>
</li>
<li>
<Link to='/server-side' activeClassName='active'>
Server-Side
</Link>
</li>
</ul>
<div className='table-wrap'>
<ReactTable
data={data}
columns={columns}
/>
</div>
<div className='viewport'>
{this.props.children}
<div style={{textAlign: 'center'}}>
<br />
<em>Tip: Hold shift when sorting to multi-sort!</em>
</div>
</div>
)
-103
View File
@@ -1,103 +0,0 @@
import { Component } from 'jumpsuit'
import _ from 'lodash'
import namor from 'namor'
import CodeHighlight from 'components/codeHighlight'
import ReactTable from '../../../lib/index.js'
export default Component({
render () {
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)
}
})
const columns = [{
header: 'Name',
columns: [{
header: 'First Name',
accessor: 'firstName'
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
}]
}]
return (
<div>
<div className='table-wrap'>
<ReactTable
data={data}
columns={columns}
pageSize={10}
/>
</div>
<div style={{textAlign: 'center'}}>
<br />
<em>Tip: Hold shift when sorting to multi-sort!</em>
</div>
<CodeHighlight>{getCode()}</CodeHighlight>
</div>
)
}
})
function getCode () {
return `
import ReactTable from 'react-table'
// To help us mock some data
import namor from 'namor'
import _ from 'lodash'
export default () => {
// Mock some data
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)
}
})
// Create some column definitions
const columns = [{
header: 'Name',
columns: [{
header: 'First Name',
accessor: 'firstName'
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
}]
}]
// Display your table!
return (
<ReactTable
data={data}
columns={columns}
pageSize={10}
/>
)
})
`
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "react-table",
"version": "3.1.0",
"version": "3.0.0",
"description": "A fast, lightweight, opinionated table and datagrid built on React",
"license": "MIT",
"homepage": "https://github.com/tannerlinsley/react-table#readme",
+61 -84
View File
@@ -3,19 +3,20 @@ import classnames from 'classnames'
//
import _ from './utils'
import Pagination from './pagination'
const defaultButton = (props) => (
<button {...props} className='-btn'>{props.children}</button>
)
export const ReactTableDefaults = {
// General
// State
data: [],
loading: false,
pageSize: 20,
showPagination: true,
showPageSizeOptions: true,
pageSizeOptions: [5, 10, 20, 25, 50, 100],
showPageJump: true,
minRows: 0,
// Callbacks
onChange: () => null,
onPageChange: () => null,
onSort: () => null,
// Classes
className: '-striped -highlight',
tableClassName: '',
@@ -59,24 +60,10 @@ export const ReactTableDefaults = {
theadComponent: (props) => <thead {...props}>{props.children}</thead>,
tbodyComponent: (props) => <tbody {...props}>{props.children}</tbody>,
trComponent: (props) => <tr {...props}>{props.children}</tr>,
thComponent: (props) => {
const {toggleSort, ...rest} = props
return (
<th {...rest} onClick={e => {
toggleSort && toggleSort(e)
}}>{props.children}</th>
)
},
thComponent: (props) => <th {...props}>{props.children}</th>,
tdComponent: (props) => <td {...props}>{props.children}</td>,
previousComponent: null,
nextComponent: null,
loadingComponent: props => (
<div className={classnames('-loading', {'-active': props.loading})}>
<div className='-loading-inner'>
{props.loadingText}
</div>
</div>
)
nextComponent: null
}
export default React.createClass({
@@ -86,6 +73,7 @@ export default React.createClass({
getInitialState () {
return {
page: 0,
pages: -1,
sorting: false
}
},
@@ -94,18 +82,12 @@ export default React.createClass({
},
fireOnChange () {
this.props.onChange({
page: this.getPropOrState('page'),
pageSize: this.getStateOrProp('pageSize'),
pages: this.getPagesLength(),
page: _.getFirstDefined(this.props.page, this.state.page),
pageSize: this.props.pageSize,
pages: this.props.pages,
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 []
@@ -158,13 +140,6 @@ export default React.createClass({
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.props.pageSize)
},
render () {
// Build Columns
const decoratedColumns = []
@@ -225,22 +200,17 @@ export default React.createClass({
})
const data = 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 pagesLength = this.props.manual ? this.props.pages : Math.ceil(data.length / this.props.pageSize)
const startRow = this.props.pageSize * this.state.page
const endRow = startRow + this.props.pageSize
const pageRows = this.props.manual ? data : data.slice(startRow, endRow)
const minRows = this.getMinRows()
const padRows = pagesLength > 1 ? _.range(pageSize - pageRows.length)
: minRows ? _.range(Math.max(minRows - pageRows.length, 0))
const padRows = pagesLength > 1 ? _.range(this.props.pageSize - pageRows.length)
: this.props.minRows ? _.range(Math.max(this.props.minRows - pageRows.length, 0))
: []
const canPrevious = currentPage > 0
const canNext = currentPage + 1 < pagesLength
const canPrevious = this.state.page > 0
const canNext = this.state.page + 1 < pagesLength
const TableComponent = this.props.tableComponent
const TheadComponent = this.props.theadComponent
@@ -248,9 +218,9 @@ export default React.createClass({
const TrComponent = this.props.trComponent
const ThComponent = this.props.thComponent
const TdComponent = this.props.tdComponent
const PreviousComponent = this.props.previousComponent
const NextComponent = this.props.nextComponent
const LoadingComponent = this.props.loadingComponent
const PreviousComponent = this.props.previousComponent || defaultButton
const NextComponent = this.props.nextComponent || defaultButton
return (
<div
@@ -319,7 +289,7 @@ export default React.createClass({
}
)}
style={Object.assign({}, this.props.thStyle, column.headerStyle)}
toggleSort={(e) => {
onClick={(e) => {
column.sortable && this.sortColumn(column, e.shiftKey)
}}
>
@@ -416,26 +386,37 @@ export default React.createClass({
})}
</TbodyComponent>
</TableComponent>
{this.props.showPagination && pagesLength > 1 && (
<Pagination
currentPage={currentPage}
pagesLength={pagesLength}
pageSize={pageSize}
showPageSizeOptions={this.props.showPageSizeOptions}
pageSizeOptions={this.props.pageSizeOptions}
showPageJump={this.props.showPageJump}
canPrevious={canPrevious}
canNext={canNext}
previousText={this.props.previousText}
nextText={this.props.nextText}
previousComponent={PreviousComponent}
nextComponent={NextComponent}
//
onChange={this.setPage}
onPageSizeChange={this.setPageSize}
/>
{pagesLength > 1 && (
<div
className={classnames(this.props.paginationClassName, '-pagination')}
style={this.props.paginationStyle}
>
<div className='-left'>
<PreviousComponent
onClick={canPrevious && ((e) => this.previousPage(e))}
disabled={!canPrevious}
>
{this.props.previousText}
</PreviousComponent>
</div>
<div className='-center'>
Page {this.state.page + 1} of {pagesLength}
</div>
<div className='-right'>
<NextComponent
onClick={canNext && ((e) => this.nextPage(e))}
disabled={!canNext}
>
{this.props.nextText}
</NextComponent>
</div>
</div>
)}
<LoadingComponent {...this.props} />
<div className={classnames('-loading', {'-active': this.props.loading})}>
<div className='-loading-inner'>
{this.props.loadingText}
</div>
</div>
</div>
)
},
@@ -447,17 +428,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)
this.setState({
pageSize,
page
}, () => {
this.fireOnChange()
})
nextPage (e) {
e.preventDefault()
this.setPage(this.state.page + 1)
},
previousPage (e) {
e.preventDefault()
this.setPage(this.state.page - 1)
},
sortColumn (column, additive) {
const existingSorting = this.getSorting()
+10 -48
View File
@@ -120,10 +120,10 @@ $easeOutBack = cubic-bezier(0.175, 0.885, 0.320, 1.275)
.-pagination
width:100%
display:flex
margin-top:5px
justify-content: space-between
align-items: center
flex-wrap: wrap
padding-top:3px
.-btn
appearance:none
@@ -147,33 +147,21 @@ $easeOutBack = cubic-bezier(0.175, 0.885, 0.320, 1.275)
background: alpha(black, .3)
color: white
.-previous
.-left
.-center
.-next
margin:3px 0
.-previous
.-next
.-right
flex: 1
// min-width:150px
margin-bottom:5px
.-left
.-right
text-align: center
.-center
flex: 1.5
text-align:center
.-pageInfo
display: inline-block
margin: 0 10px 3px
white-space: nowrap
.-pageJump
display:inline-block
input
width: 70px
text-align:center
.-pageSizeOptions
margin-left:12px
padding: 0 5px
.-loading
@@ -206,29 +194,3 @@ $easeOutBack = cubic-bezier(0.175, 0.885, 0.320, 1.275)
pointer-events: all
> div
transform: translateY(50%)
input
select
appearance: none
border: 1px solid rgba(0,0,0,0.1)
padding: 5px 7px
font-size: inherit
border-radius: 3px
font-weight: normal
outline:none
.select-wrap
position:relative
display:inline-block
select
padding: 5px 15px 5px 7px
min-width:100px
&:after
content: ''
position: absolute
right: 8px
top: 50%
transform: translate(0, -50%)
border-color: #999 transparent transparent
border-style: solid
border-width: 5px 5px 2.5px
-121
View File
@@ -1,121 +0,0 @@
import React from 'react'
import classnames from 'classnames'
//
// import _ from './utils'
const defaultButton = (props) => (
<button {...props} className='-btn'>{props.children}</button>
)
export default React.createClass({
getInitialState () {
return {
page: this.props.currentPage
}
},
componentWillReceiveProps (nextProps) {
this.setState({page: nextProps.currentPage})
},
getSafePage (page) {
return Math.min(Math.max(page, 0), this.props.pagesLength - 1)
},
changePage (page) {
page = this.getSafePage(page)
this.setState({page})
this.props.onChange(page)
},
applyPage (e) {
e && e.preventDefault()
const page = this.state.page
this.changePage(page === '' ? this.props.currentPage : page)
},
render () {
const {
currentPage,
pagesLength,
showPageSizeOptions,
pageSizeOptions,
pageSize,
showPageJump,
canPrevious,
canNext,
onPageSizeChange
} = this.props
const PreviousComponent = this.props.PreviousComponent || defaultButton
const NextComponent = this.props.NextComponent || defaultButton
return (
<div
className={classnames(this.props.paginationClassName, '-pagination')}
style={this.props.paginationStyle}
>
<div className='-previous'>
<PreviousComponent
onClick={(e) => {
if (!canPrevious) return
this.changePage(currentPage - 1)
}}
disabled={!canPrevious}
>
{this.props.previousText}
</PreviousComponent>
</div>
<div className='-center'>
<span className='-pageInfo'>
Page {showPageJump ? (
<form className='-pageJump'
onSubmit={this.applyPage}
>
<input
type={this.state.page === '' ? 'text' : 'number'}
onChange={e => {
const val = e.target.value
const page = val - 1
if (val === '') {
return this.setState({page: val})
}
this.setState({page: this.getSafePage(page)})
}}
value={this.state.page === '' ? '' : this.state.page + 1}
onBlur={this.applyPage}
/>
</form>
) : (
<span className='-currentPage'>{currentPage + 1}</span>
)} of <span className='-totalPages'>{pagesLength}</span>
</span>
{showPageSizeOptions && (
<span className='select-wrap -pageSizeOptions'>
<select
onChange={(e) => onPageSizeChange(Number(e.target.value))}
value={pageSize}
>
{pageSizeOptions.map((option, i) => {
return (
<option
key={i}
value={option}>
{option} rows
</option>
)
})}
</select>
</span>
)}
</div>
<div className='-next'>
<NextComponent
onClick={(e) => {
if (!canNext) return
this.changePage(currentPage + 1)
}}
disabled={!canNext}
>
{this.props.nextText}
</NextComponent>
</div>
</div>
)
}
})