mirror of
https://github.com/gosticks/react-table.git
synced 2026-01-31 05:47:32 +00:00
Pagination Features
- Page Jumping - Page Size Changing - Updated Examples
This commit is contained in:
parent
e181f8e3d1
commit
57ac1f142d
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@ lib/
|
||||
react-table.js
|
||||
react-table.css
|
||||
*.log
|
||||
|
||||
dist/
|
||||
|
||||
@ -83,11 +83,14 @@ These are the default props for the main react component `<ReactTable />`
|
||||
{
|
||||
// General
|
||||
loading: false, // Whether to show the loading overlay or not
|
||||
pageSize: 20,
|
||||
pageSize: 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
|
||||
showPageSizeOptions: true, // Enables the user to change the page size
|
||||
pageSizeOptions: [5, 10, 20, 25, 50, 100], // The available page size options
|
||||
|
||||
// Callbacks
|
||||
onChange: () => null,
|
||||
onChange: (state, instance) => null, // Anytime the internal state of the table changes, this will fire
|
||||
|
||||
// Text
|
||||
previousText: 'Previous',
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "jumpsuit watch",
|
||||
"build": "jumpsuit build"
|
||||
"build": "jumpsuit build",
|
||||
"deploy": "jumpsuit build && zab deploy"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nib": "^1.1.0",
|
||||
@ -21,6 +22,7 @@
|
||||
"jumpsuit": "^0.7.5",
|
||||
"lodash": "^4.16.4",
|
||||
"namor": "^0.3.0",
|
||||
"react-syntax-highlighter": "^3.0.0",
|
||||
"react-table": "^2.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,15 @@
|
||||
import { Render } from 'jumpsuit'
|
||||
// import App from 'screens/async'
|
||||
import App from 'screens/index'
|
||||
import { Render, Router, Route, IndexRoute } from 'jumpsuit'
|
||||
//
|
||||
import Layout from 'components/layout'
|
||||
import Simple from 'screens/simple'
|
||||
import ServerSide from 'screens/serverSide'
|
||||
|
||||
Render(null, <App />)
|
||||
Render(null, (
|
||||
<Router>
|
||||
<Route path='/' component={Layout}>
|
||||
<IndexRoute component={Simple} />
|
||||
<Route path='simple' component={Simple} />
|
||||
<Route path='server-side' component={ServerSide} />
|
||||
</Route>
|
||||
</Router>
|
||||
))
|
||||
|
||||
@ -11,7 +11,7 @@ global-reset()
|
||||
// vendor styles
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@import '../node_modules/react-table/react-table.css'
|
||||
@import '../../src/index'
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// variables
|
||||
@ -34,7 +34,6 @@ body
|
||||
background: white
|
||||
font-family: $fnt-open-sans
|
||||
font-weight: 300
|
||||
padding-bottom: 50px
|
||||
|
||||
h1
|
||||
font-size: 2.5em
|
||||
@ -50,6 +49,7 @@ strong
|
||||
|
||||
.logo
|
||||
width: 400px
|
||||
max-width: 100%
|
||||
|
||||
.container
|
||||
display: flex
|
||||
@ -63,11 +63,34 @@ strong
|
||||
font-size: 20px
|
||||
padding: 10px
|
||||
|
||||
.table-wrap
|
||||
width: 700px
|
||||
.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
|
||||
padding: 10px
|
||||
border-radius: 5px
|
||||
box-shadow: 0 0 20px 0 alpha(black, .2)
|
||||
margin: 5px
|
||||
background: alpha(black, .7)
|
||||
color: white
|
||||
border-radius: 3px
|
||||
transition: all .2s ease-out
|
||||
&.active
|
||||
&:hover
|
||||
background: alpha(black, .9)
|
||||
|
||||
.ReactTable
|
||||
thead
|
||||
@ -77,3 +100,13 @@ 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
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
<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
example/src/components/codeHighlight.js
Normal file
11
example/src/components/codeHighlight.js
Normal file
@ -0,0 +1,11 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
})
|
||||
@ -1,43 +1,13 @@
|
||||
import { Component } from 'jumpsuit'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from 'react-table'
|
||||
import { Component, Link } from 'jumpsuit'
|
||||
|
||||
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 <strong>demo</strong>
|
||||
react-table
|
||||
</span>
|
||||
<img src='/Banner.png' className='logo' />
|
||||
</h1>
|
||||
@ -66,15 +36,22 @@ export default Component({
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
/>
|
||||
<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>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
<div className='viewport'>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@ -1,96 +0,0 @@
|
||||
import { Component } from 'jumpsuit'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
// Let's mock some data to play around with
|
||||
const rawData = _.map(_.range(1000), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numLen: 0 }),
|
||||
lastName: namor.generate({ words: 1, numLen: 0 }),
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
|
||||
// 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) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 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
|
||||
const sortedData = _.orderBy(rawData, 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'))
|
||||
|
||||
// Be sure to send back the rows to be displayed and any other pertinent information, like how many pages there are total.
|
||||
const res = {
|
||||
rows: sortedData.slice(pageSize * page, (pageSize * page) + pageSize),
|
||||
pages: Math.ceil(rawData.length / pageSize)
|
||||
}
|
||||
|
||||
// Here we'll simulate a server response with 500ms of delay.
|
||||
setTimeout(() => resolve(res), 500)
|
||||
})
|
||||
}
|
||||
|
||||
export default Component({
|
||||
getInitialState () {
|
||||
// To handle our data server-side, we need a few things in the state to help us out:
|
||||
return {
|
||||
data: [],
|
||||
pages: null,
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
fetchData (state, instance) {
|
||||
// Whenever the table model changes, or the user sorts or changes pages, 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 one or show you're own loading bar if you want.
|
||||
this.setState({loading: true})
|
||||
// Request the data however you want. Here, we'll use our mocked service we created earlier
|
||||
requestData(state.pageSize, state.page, state.sorting)
|
||||
.then((res) => {
|
||||
// Now just get the rows of data to your React Table (and update anything else like total pages or loading)
|
||||
this.setState({
|
||||
data: res.rows,
|
||||
pages: res.pages,
|
||||
loading: false
|
||||
})
|
||||
})
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<ReactTable
|
||||
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'
|
||||
}]
|
||||
}]}
|
||||
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
|
||||
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
|
||||
onChange={this.fetchData} // Request new data when things change
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
170
example/src/screens/serverSide.js
Normal file
170
example/src/screens/serverSide.js
Normal file
@ -0,0 +1,170 @@
|
||||
import { Component } from 'jumpsuit'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from 'components/codeHighlight'
|
||||
import ReactTable from '../../../lib/index.js'
|
||||
|
||||
// Let's mock some data to play around with
|
||||
const rawData = _.map(_.range(3424), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numLen: 0 }),
|
||||
lastName: namor.generate({ words: 1, numLen: 0 }),
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
|
||||
// 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) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 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
|
||||
const sortedData = _.orderBy(rawData, 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'))
|
||||
|
||||
// Be sure to send back the rows to be displayed and any other pertinent information, like how many pages there are total.
|
||||
const res = {
|
||||
rows: sortedData.slice(pageSize * page, (pageSize * page) + pageSize),
|
||||
pages: Math.ceil(rawData.length / pageSize)
|
||||
}
|
||||
|
||||
// Here we'll simulate a server response with 500ms of delay.
|
||||
setTimeout(() => resolve(res), 500)
|
||||
})
|
||||
}
|
||||
|
||||
export default Component({
|
||||
getInitialState () {
|
||||
// To handle our data server-side, we need a few things in the state to help us out:
|
||||
return {
|
||||
data: [],
|
||||
pages: null,
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
fetchData (state, instance) {
|
||||
// Whenever the table model changes, or the user sorts or changes pages, 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 one or show you're own loading bar if you want.
|
||||
this.setState({loading: true})
|
||||
// Request the data however you want. Here, we'll use our mocked service we created earlier
|
||||
requestData(state.pageSize, state.page, state.sorting)
|
||||
.then((res) => {
|
||||
// Now just get the rows of data to your React Table (and update anything else like total pages or loading)
|
||||
this.setState({
|
||||
data: res.rows,
|
||||
pages: res.pages,
|
||||
loading: false
|
||||
})
|
||||
})
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
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'
|
||||
}]
|
||||
}]}
|
||||
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
|
||||
pageSize={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
|
||||
onChange={this.fetchData} // Request new data when things change
|
||||
/>
|
||||
</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'
|
||||
|
||||
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
|
||||
/>
|
||||
)
|
||||
}
|
||||
})
|
||||
`
|
||||
}
|
||||
103
example/src/screens/simple.js
Normal file
103
example/src/screens/simple.js
Normal file
@ -0,0 +1,103 @@
|
||||
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}
|
||||
/>
|
||||
)
|
||||
})
|
||||
`
|
||||
}
|
||||
143
src/index.js
143
src/index.js
@ -3,16 +3,17 @@ import classnames from 'classnames'
|
||||
//
|
||||
import _ from './utils'
|
||||
|
||||
const defaultButton = (props) => (
|
||||
<button {...props} className='-btn'>{props.children}</button>
|
||||
)
|
||||
import Pagination from './pagination'
|
||||
|
||||
export const ReactTableDefaults = {
|
||||
// State
|
||||
// General
|
||||
data: [],
|
||||
loading: false,
|
||||
pageSize: 20,
|
||||
minRows: 0,
|
||||
showPagination: true,
|
||||
showPageSizeOptions: true,
|
||||
pageSizeOptions: [5, 10, 20, 25, 50, 100],
|
||||
showPageJump: true,
|
||||
// Callbacks
|
||||
onChange: () => null,
|
||||
// Classes
|
||||
@ -58,10 +59,24 @@ 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) => <th {...props}>{props.children}</th>,
|
||||
thComponent: (props) => {
|
||||
const {toggleSort, ...rest} = props
|
||||
return (
|
||||
<th {...rest} onClick={e => {
|
||||
toggleSort && toggleSort(e)
|
||||
}}>{props.children}</th>
|
||||
)
|
||||
},
|
||||
tdComponent: (props) => <td {...props}>{props.children}</td>,
|
||||
previousComponent: null,
|
||||
nextComponent: null
|
||||
nextComponent: null,
|
||||
loadingComponent: props => (
|
||||
<div className={classnames('-loading', {'-active': props.loading})}>
|
||||
<div className='-loading-inner'>
|
||||
{props.loadingText}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.createClass({
|
||||
@ -71,7 +86,6 @@ export default React.createClass({
|
||||
getInitialState () {
|
||||
return {
|
||||
page: 0,
|
||||
pages: -1,
|
||||
sorting: false
|
||||
}
|
||||
},
|
||||
@ -80,12 +94,18 @@ export default React.createClass({
|
||||
},
|
||||
fireOnChange () {
|
||||
this.props.onChange({
|
||||
page: _.getFirstDefined(this.props.page, this.state.page),
|
||||
pageSize: this.props.pageSize,
|
||||
pages: this.props.pages,
|
||||
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 []
|
||||
@ -138,6 +158,13 @@ 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 = []
|
||||
@ -198,17 +225,22 @@ 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 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 startRow = pageSize * currentPage
|
||||
const endRow = startRow + pageSize
|
||||
const pageRows = this.props.manual ? data : data.slice(startRow, endRow)
|
||||
const padRows = pagesLength > 1 ? _.range(this.props.pageSize - pageRows.length)
|
||||
: this.props.minRows ? _.range(Math.max(this.props.minRows - pageRows.length, 0))
|
||||
const minRows = this.getMinRows()
|
||||
const padRows = pagesLength > 1 ? _.range(pageSize - pageRows.length)
|
||||
: minRows ? _.range(Math.max(minRows - pageRows.length, 0))
|
||||
: []
|
||||
|
||||
const canPrevious = this.state.page > 0
|
||||
const canNext = this.state.page + 1 < pagesLength
|
||||
const canPrevious = currentPage > 0
|
||||
const canNext = currentPage + 1 < pagesLength
|
||||
|
||||
const TableComponent = this.props.tableComponent
|
||||
const TheadComponent = this.props.theadComponent
|
||||
@ -216,9 +248,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 || defaultButton
|
||||
const NextComponent = this.props.nextComponent || defaultButton
|
||||
const PreviousComponent = this.props.previousComponent
|
||||
const NextComponent = this.props.nextComponent
|
||||
const LoadingComponent = this.props.loadingComponent
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -287,7 +319,7 @@ export default React.createClass({
|
||||
}
|
||||
)}
|
||||
style={Object.assign({}, this.props.thStyle, column.headerStyle)}
|
||||
onClick={(e) => {
|
||||
toggleSort={(e) => {
|
||||
column.sortable && this.sortColumn(column, e.shiftKey)
|
||||
}}
|
||||
>
|
||||
@ -384,37 +416,26 @@ export default React.createClass({
|
||||
})}
|
||||
</TbodyComponent>
|
||||
</TableComponent>
|
||||
{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>
|
||||
{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}
|
||||
/>
|
||||
)}
|
||||
<div className={classnames('-loading', {'-active': this.props.loading})}>
|
||||
<div className='-loading-inner'>
|
||||
{this.props.loadingText}
|
||||
</div>
|
||||
</div>
|
||||
<LoadingComponent {...this.props} />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
@ -426,13 +447,17 @@ export default React.createClass({
|
||||
this.fireOnChange()
|
||||
})
|
||||
},
|
||||
nextPage (e) {
|
||||
e.preventDefault()
|
||||
this.setPage(this.state.page + 1)
|
||||
},
|
||||
previousPage (e) {
|
||||
e.preventDefault()
|
||||
this.setPage(this.state.page - 1)
|
||||
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()
|
||||
})
|
||||
},
|
||||
sortColumn (column, additive) {
|
||||
const existingSorting = this.getSorting()
|
||||
|
||||
@ -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,21 +147,33 @@ $easeOutBack = cubic-bezier(0.175, 0.885, 0.320, 1.275)
|
||||
background: alpha(black, .3)
|
||||
color: white
|
||||
|
||||
.-left
|
||||
.-previous
|
||||
.-center
|
||||
.-right
|
||||
flex: 1
|
||||
// min-width:150px
|
||||
margin-bottom:5px
|
||||
.-next
|
||||
margin:3px 0
|
||||
|
||||
.-left
|
||||
.-right
|
||||
.-previous
|
||||
.-next
|
||||
flex: 1
|
||||
text-align: center
|
||||
|
||||
|
||||
.-center
|
||||
flex: 1.5
|
||||
text-align:center
|
||||
padding: 0 5px
|
||||
|
||||
.-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
|
||||
|
||||
|
||||
.-loading
|
||||
@ -194,3 +206,29 @@ $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
src/pagination.js
Normal file
121
src/pagination.js
Normal file
@ -0,0 +1,121 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user