mirror of
https://github.com/gosticks/react-table.git
synced 2026-08-19 16:30:21 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a64c6a3a76 | ||
|
|
f502ef4e2b | ||
|
|
c58c96c84f | ||
|
|
7202776c86 | ||
|
|
734ba48e9c | ||
|
|
db84ecf789 | ||
|
|
50668fe90f | ||
|
|
52f2c9fdac | ||
|
|
3b39f523be | ||
|
|
9d85981c5b | ||
|
|
b779a98d7a | ||
|
|
1f4c9afd2b | ||
|
|
cef7fbd780 | ||
|
|
e8ebf7c780 | ||
|
|
ac20220d55 | ||
|
|
9acf8f37eb | ||
|
|
64a427d91b | ||
|
|
d21ce226aa | ||
|
|
92c9ae2196 | ||
|
|
23a031b48a | ||
|
|
bd8b273dea | ||
|
|
1a9f0e57b4 | ||
|
|
4c11248c69 | ||
|
|
784311bcd6 | ||
|
|
7b232cd6b4 | ||
|
|
533bea77b6 | ||
|
|
218cf42bb2 | ||
|
|
62287d1641 | ||
|
|
10dc68acc1 | ||
|
|
bd82468852 | ||
|
|
6d28bb2c4d | ||
|
|
9e95b331df | ||
|
|
f296b075fe | ||
|
|
a948a9a41e | ||
|
|
08dbe38ced | ||
|
|
884058b459 | ||
|
|
ee37ad7101 | ||
|
|
6b384f2a90 | ||
|
|
782e9efdfb | ||
|
|
ff952de830 | ||
|
|
149b11674b | ||
|
|
8a362e4d04 | ||
|
|
265a5c8753 | ||
|
|
5155ebad72 | ||
|
|
d7a58450b7 | ||
|
|
bb2527e280 | ||
|
|
6d5207eb4e | ||
|
|
0d397e0c29 | ||
|
|
b15f3ac3b0 | ||
|
|
48ea0df09d | ||
|
|
403fb7a708 | ||
|
|
ecd5658e4b | ||
|
|
3852227bdd | ||
|
|
1fec9b882f | ||
|
|
46c4c6fdb2 | ||
|
|
e702fc80ca | ||
|
|
beaa748187 | ||
|
|
cce77d24f2 | ||
|
|
3984a540c1 | ||
|
|
726fd52753 | ||
|
|
6a6d373526 | ||
|
|
e11fdab457 | ||
|
|
1e4f838326 | ||
|
|
aa48ef57ee | ||
|
|
22f79fcd28 | ||
|
|
ff8e24760a | ||
|
|
f1511bd27e | ||
|
|
4733997c26 | ||
|
|
c3a6e236ae | ||
|
|
83200d4e4c | ||
|
|
5e16c2cb24 | ||
|
|
66cee186a3 | ||
|
|
55bbf4bf37 | ||
|
|
10ce93344c | ||
|
|
114621a359 | ||
|
|
8de0fa420b | ||
|
|
aa77193810 | ||
|
|
92c33de7ac | ||
|
|
c3fb084550 | ||
|
|
04c71f17a8 | ||
|
|
34fa57c4d3 | ||
|
|
de1c8a6156 | ||
|
|
ab9d67d2d9 | ||
|
|
dc0713c1d1 | ||
|
|
c1004ee9e9 | ||
|
|
c0098ebc2c | ||
|
|
33645b4fea | ||
|
|
3d325dad8d | ||
|
|
f3ee89a514 | ||
|
|
90001c1b3a | ||
|
|
8d7b9c2feb | ||
|
|
f754eb0bd5 | ||
|
|
66f1b04ba6 | ||
|
|
bea4b366d6 | ||
|
|
64168eb3ef | ||
|
|
77be7c066f | ||
|
|
e9e69428a2 | ||
|
|
3c039789f1 | ||
|
|
5865f59129 | ||
|
|
c663cce719 | ||
|
|
a6f803c1a3 | ||
|
|
4a871db71f | ||
|
|
505db8d43e | ||
|
|
387f22028a | ||
|
|
f2839dcc6d | ||
|
|
8a74b2b795 | ||
|
|
9bbdd762e6 | ||
|
|
37cf57e1d0 | ||
|
|
cd507d39a4 | ||
|
|
4873af8c99 | ||
|
|
6c05306d99 | ||
|
|
ac2f9a2467 | ||
|
|
69eccc8909 | ||
|
|
721d193883 | ||
|
|
21cae2416f | ||
|
|
0bf363aeaf | ||
|
|
517702922e | ||
|
|
ba1325336f | ||
|
|
7bd30b623c |
+2
-3
@@ -3,6 +3,5 @@ lib/
|
||||
react-table.js
|
||||
react-table.css
|
||||
*.log
|
||||
example/dist/
|
||||
|
||||
storybook-static
|
||||
.idea
|
||||
.DS_Store
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
react-table.js.org
|
||||
+23
-3
@@ -11,11 +11,21 @@ import '../react-table.css'
|
||||
import Readme from '../README.md'
|
||||
//
|
||||
import Simple from '../stories/Simple.js'
|
||||
import CellRenderers from '../stories/CellRenderers.js'
|
||||
import DefaultSorting from '../stories/DefaultSorting.js'
|
||||
import CustomWidths from '../stories/CustomWidths.js'
|
||||
import CustomComponentProps from '../stories/CustomComponentProps.js'
|
||||
import ServerSide from '../stories/ServerSide.js'
|
||||
import SubComponents from '../stories/SubComponents.js'
|
||||
import Pivoting from '../stories/Pivoting.js'
|
||||
import PivotingSubComponents from '../stories/PivotingSubComponents.js'
|
||||
import MillionRows from '../stories/MillionRows.js'
|
||||
import OneHundredKRows from '../stories/OneHundredKRows.js'
|
||||
import FunctionalRendering from '../stories/FunctionalRendering.js'
|
||||
import CustomExpanderPosition from '../stories/CustomExpanderPosition.js'
|
||||
import NoDataText from '../stories/NoDataText.js'
|
||||
import Footers from '../stories/Footers.js'
|
||||
import Filtering from '../stories/Filtering.js'
|
||||
import ControlledTable from '../stories/ControlledTable.js'
|
||||
//
|
||||
configure(() => {
|
||||
storiesOf('1. Docs')
|
||||
@@ -31,10 +41,20 @@ configure(() => {
|
||||
return <ReadmeCmp />
|
||||
})
|
||||
storiesOf('2. Demos')
|
||||
.add('Client-side Data', Simple)
|
||||
.add('Simple Table', Simple)
|
||||
.add('Cell Renderers & Custom Components', CellRenderers)
|
||||
.add('Default Sorting', DefaultSorting)
|
||||
.add('Custom Column Widths', CustomWidths)
|
||||
.add('Custom Component Props', CustomComponentProps)
|
||||
.add('Server-side Data', ServerSide)
|
||||
.add('Sub Components', SubComponents)
|
||||
.add('Pivoting & Aggregation', Pivoting)
|
||||
.add('Pivoting & Aggregation w/ Sub Components', PivotingSubComponents)
|
||||
.add('1 Million Rows w/ Pivoting & Sub Components', MillionRows)
|
||||
.add('100k Rows w/ Pivoting & Sub Components', OneHundredKRows)
|
||||
.add('Functional Rendering', FunctionalRendering)
|
||||
.add('Custom Expander Position', CustomExpanderPosition)
|
||||
.add('Custom "No Data" Text', NoDataText)
|
||||
.add('Footers', Footers)
|
||||
.add('Custom Filtering', Filtering)
|
||||
.add('Controlled Component', ControlledTable)
|
||||
}, module)
|
||||
|
||||
@@ -4,3 +4,15 @@ body {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
line-height: 22px;
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
body{
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
## Problem Description
|
||||
include a detailed explanation of the problem here...
|
||||
|
||||
## Code Snippet(s)
|
||||
|
||||
## Steps to Reproduce
|
||||
1. list the steps
|
||||
2. to reproduce the issue
|
||||
|
||||
## System Information
|
||||
* **Browser & Browser Version:**
|
||||
* **Node Version:**
|
||||
* **OS Version:**
|
||||
* **NPM Version:**
|
||||
* **Yarn Version:**
|
||||
@@ -1,5 +1,5 @@
|
||||
<div align="center">
|
||||
<a href="https://github.com/tannerlinsley/react-table"><img src="https://github.com/tannerlinsley/react-table/raw/master/media/Banner.png" alt="React Table Logo" style="width:450px;"/></a>
|
||||
<a href="https://github.com/tannerlinsley/react-table" target="\_parent"><img src="https://github.com/tannerlinsley/react-table/raw/master/media/Banner.png" alt="React Table Logo" style="width:450px;"/></a>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
@@ -8,7 +8,25 @@
|
||||
# React Table
|
||||
`react-table` is a **lightweight, fast and extendable datagrid** built for React
|
||||
|
||||
[](https://travis-ci.org/tannerlinsley/react-table) [](https://npmjs.com/packag/react-table) [](https://react-table-slack.herokuapp.com/) [](https://github.com/tannerlinsley/react-table) [](https://twitter.com/tannerlinsley)
|
||||
|
||||
<a href="https://travis-ci.org/tannerlinsley/react-table" target="\_parent">
|
||||
<img alt="" src="https://travis-ci.org/tannerlinsley/react-table.svg?branch=master" />
|
||||
</a>
|
||||
<a href="https://npmjs.com/package/react-table" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/npm/dm/react-table.svg" />
|
||||
</a>
|
||||
<a href="https://react-chat-signup.herokuapp.com/" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/badge/slack-react--chat-blue.svg" />
|
||||
</a>
|
||||
<a href="https://github.com/tannerlinsley/react-table" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/github/stars/tannerlinsley/react-table.svg?style=social&label=Star" />
|
||||
</a>
|
||||
<a href="https://twitter.com/tannerlinsley" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/twitter/follow/tannerlinsley.svg?style=social&label=Follow" />
|
||||
</a>
|
||||
<a href="https://cash.me/$tannerlinsley" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/badge/%24-Donate-brightgreen.svg" />
|
||||
</a>
|
||||
|
||||
## Features
|
||||
|
||||
@@ -18,9 +36,9 @@
|
||||
- Column Pivoting & Aggregation
|
||||
- Minimal design & easily themeable
|
||||
- Fully controllable via optional props and callbacks
|
||||
- <a href="https://medium.com/@tannerlinsley/why-i-wrote-react-table-and-the-problems-it-has-solved-for-nozzle-others-445c4e93d4a8#.axza4ixba" target="\_blank">"Why I wrote React Table and the problems it has solved for Nozzle.io</a> by Tanner Linsley
|
||||
- <a href="https://medium.com/@tannerlinsley/why-i-wrote-react-table-and-the-problems-it-has-solved-for-nozzle-others-445c4e93d4a8#.axza4ixba" target="\_parent">"Why I wrote React Table and the problems it has solved for Nozzle.io</a> by Tanner Linsley
|
||||
|
||||
## <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>
|
||||
## <a href="http://react-table.js.org/?selectedKind=2.%20Demos&selectedStory=Client-side%20Data&full=0&down=0&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel" target="\_parent">Demo</a>
|
||||
|
||||
## Table of Contents
|
||||
- [Installation](#installation)
|
||||
@@ -28,54 +46,83 @@
|
||||
- [Data](#data)
|
||||
- [Props](#props)
|
||||
- [Columns](#columns)
|
||||
- [Column Header Groups](#column-header-groups)
|
||||
- [Custom Cell and Header and Footer Rendering](#custom-cell-header-and-footer-rendering)
|
||||
- [Styles](#styles)
|
||||
- [Header Groups](#header-groups)
|
||||
- [Pivoting & Aggregation](#pivoting-aggregation)
|
||||
- [Sub Tables & Sub Components](#sub-tables-sub-components)
|
||||
- [Custom Props](#custom-props)
|
||||
- [Pivoting and Aggregation](#pivoting-and-aggregation)
|
||||
- [Sub Tables and Sub Components](#sub-tables-and-sub-components)
|
||||
- [Server-side Data](#server-side-data)
|
||||
- [Fully Controlled Component](#fully-controlled-component)
|
||||
- [Multi-sort](#multi-sort)
|
||||
- [Functional Rendering](#functional-rendering)
|
||||
- [Multi-Sort](#multi-sort)
|
||||
- [Filtering](#filtering)
|
||||
- [Component Overrides](#component-overrides)
|
||||
- [Contributing](#contributing)
|
||||
- [Scripts](#scripts)
|
||||
- [Used By](#used-by)
|
||||
|
||||
|
||||
## Installation
|
||||
1. Install React Table as a dependency
|
||||
```bash
|
||||
$ npm install react-table
|
||||
# or
|
||||
$ yarn add react-table
|
||||
```
|
||||
2. Import the `react-table` module
|
||||
```javascript
|
||||
// ES6
|
||||
import ReactTable from 'react-table'
|
||||
// ES5
|
||||
var ReactTable = require('react-table').default
|
||||
```
|
||||
3. Import styles by including `react-table.css` anywhere on the page
|
||||
```javascript
|
||||
// JS (Webpack)
|
||||
import 'react-table/react-table.css'
|
||||
// html
|
||||
<link rel="stylesheet" href="node_modules/react-table/react-table.css">
|
||||
```
|
||||
|
||||
|
||||
## Example
|
||||
```javascript
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
const data = [{
|
||||
name: 'Tanner Linsley',
|
||||
age: 26,
|
||||
friend: {
|
||||
name: 'Jason Maurer',
|
||||
age: 23,
|
||||
}
|
||||
},{
|
||||
...
|
||||
}]
|
||||
render() {
|
||||
const data = [{
|
||||
name: 'Tanner Linsley',
|
||||
age: 26,
|
||||
friend: {
|
||||
name: 'Jason Maurer',
|
||||
age: 23,
|
||||
}
|
||||
},{
|
||||
...
|
||||
}]
|
||||
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
accessor: 'name' // String-based value accessors !
|
||||
}, {
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
render: props => <span className='number'>props.value</span> // Custom cell components!
|
||||
}, {
|
||||
header: 'Friend Name',
|
||||
accessor: d => d.friend.name // Custom value accessors!
|
||||
}, {
|
||||
header: props => <span>Friend Age</span>, // Custom header components!
|
||||
accessor: 'friend.age'
|
||||
}]
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
accessor: 'name' // String-based value accessors!
|
||||
}, {
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
render: props => <span className='number'>{props.value}</span> // Custom cell components!
|
||||
}, {
|
||||
id: 'friendName', // Required because our accessor is not a string
|
||||
header: 'Friend Name',
|
||||
accessor: d => d.friend.name // Custom value accessors!
|
||||
}, {
|
||||
header: props => <span>Friend Age</span>, // Custom header components!
|
||||
accessor: 'friend.age'
|
||||
}]
|
||||
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
/>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
/>
|
||||
}
|
||||
```
|
||||
|
||||
## Data
|
||||
@@ -87,55 +134,127 @@ These are all of the available props (and their default values) for the main `<R
|
||||
```javascript
|
||||
{
|
||||
// General
|
||||
loading: false, // Whether to show the loading overlay or not
|
||||
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
|
||||
showPageSizeOptions: true, // Enables the user to change the page size
|
||||
pageSizeOptions: [5, 10, 20, 25, 50, 100], // The available page size options
|
||||
expanderColumnWidth: 30, // default columnWidth for the expander column
|
||||
data: [],
|
||||
loading: false,
|
||||
showPagination: true,
|
||||
showPageSizeOptions: true,
|
||||
pageSizeOptions: [5, 10, 20, 25, 50, 100],
|
||||
defaultPageSize: 20,
|
||||
showPageJump: true,
|
||||
expanderColumnWidth: 35,
|
||||
collapseOnSortingChange: true,
|
||||
collapseOnPageChange: true,
|
||||
collapseOnDataChange: true,
|
||||
freezeWhenExpanded: false,
|
||||
defaultSorting: [],
|
||||
showFilters: false,
|
||||
defaultFiltering: [],
|
||||
defaultFilterMethod: (filter, row, column) => {
|
||||
const id = filter.pivotId || filter.id
|
||||
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
|
||||
},
|
||||
resizable: true,
|
||||
defaultResizing: [],
|
||||
|
||||
// Callbacks
|
||||
onChange: (state, instance) => null, // Anytime the internal state of the table changes, this will fire
|
||||
onTrClick: (row, event) => null, // Handler for row click events
|
||||
// Controlled State Overrides (see Fully Controlled Component section)
|
||||
page: undefined,
|
||||
pageSize: undefined,
|
||||
sorting: undefined,
|
||||
|
||||
// Controlled State Callbacks
|
||||
onExpandSubComponent: undefined,
|
||||
onPageChange: undefined,
|
||||
onPageSizeChange: undefined,
|
||||
onSortingChange: undefined,
|
||||
onFilteringChange: undefined,
|
||||
onResize: undefined,
|
||||
|
||||
// Pivoting
|
||||
pivotBy: undefined,
|
||||
pivotColumnWidth: 200,
|
||||
pivotValKey: '_pivotVal',
|
||||
pivotIDKey: '_pivotID',
|
||||
subRowsKey: '_subRows',
|
||||
|
||||
// Pivoting State Overrides (see Fully Controlled Component section)
|
||||
expandedRows: {},
|
||||
|
||||
// Pivoting State Callbacks
|
||||
onExpandRow: undefined,
|
||||
|
||||
// General Callbacks
|
||||
onChange: () => null,
|
||||
|
||||
// Classes
|
||||
className: '',
|
||||
style: {},
|
||||
|
||||
// Component decorators
|
||||
getProps: () => ({}),
|
||||
getTableProps: () => ({}),
|
||||
getTheadGroupProps: () => ({}),
|
||||
getTheadGroupTrProps: () => ({}),
|
||||
getTheadGroupThProps: () => ({}),
|
||||
getTheadProps: () => ({}),
|
||||
getTheadTrProps: () => ({}),
|
||||
getTheadThProps: () => ({}),
|
||||
getTheadFilterProps: () => ({}),
|
||||
getTheadFilterTrProps: () => ({}),
|
||||
getTheadFilterThProps: () => ({}),
|
||||
getTbodyProps: () => ({}),
|
||||
getTrGroupProps: () => ({}),
|
||||
getTrProps: () => ({}),
|
||||
getThProps: () => ({}),
|
||||
getTdProps: () => ({}),
|
||||
getTfootProps: () => ({}),
|
||||
getTfootTrProps: () => ({}),
|
||||
getTfootThProps: () => ({}),
|
||||
getPaginationProps: () => ({}),
|
||||
getLoadingProps: () => ({}),
|
||||
getNoDataProps: () => ({}),
|
||||
getResizerProps: () => ({}),
|
||||
|
||||
// Global Column Defaults
|
||||
column: {
|
||||
sortable: true,
|
||||
show: true,
|
||||
minWidth: 100,
|
||||
// Cells only
|
||||
render: undefined,
|
||||
className: '',
|
||||
style: {},
|
||||
getProps: () => ({}),
|
||||
// Headers only
|
||||
header: undefined,
|
||||
headerClassName: '',
|
||||
headerStyle: {},
|
||||
getHeaderProps: () => ({})
|
||||
// Footers only
|
||||
footer: undefined,
|
||||
footerClassName: '',
|
||||
footerStyle: {},
|
||||
getFooterProps: () => ({}),
|
||||
filterMethod: undefined,
|
||||
hideFilter: false,
|
||||
filterRender: ({filter, onFilterChange}) => (
|
||||
<input type='text'
|
||||
style={{
|
||||
width: '100%'
|
||||
}}
|
||||
value={filter ? filter.value : ''}
|
||||
onChange={(event) => onFilterChange(event.target.value)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
||||
// Text
|
||||
previousText: 'Previous',
|
||||
nextText: 'Next',
|
||||
loadingText: 'Loading...',
|
||||
noDataText: 'No rows found',
|
||||
pageText: 'Page',
|
||||
ofText: 'of',
|
||||
rowsText: 'rows',
|
||||
|
||||
// Classes
|
||||
className: '-striped -highlight', // The most top level className for the component
|
||||
tableClassName: '', // ClassName for the `table` element
|
||||
theadClassName: '', // ClassName for the `thead` element
|
||||
tbodyClassName: '', // ClassName for the `tbody` element
|
||||
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
|
||||
theadStyle: {}, // style object for the `thead` component
|
||||
tbodyStyle: {}, // style object for the `tbody` component
|
||||
trStyle: {}, // style object for the `tr` component
|
||||
trStyleCallback: row => {}, // A call back to dynamically add styles to a row element
|
||||
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,
|
||||
expandedRows: undefined,
|
||||
// Controlled Callbacks
|
||||
onExpandRow: undefined,
|
||||
onPageChange: undefined,
|
||||
onPageSizeChange: undefined,
|
||||
}
|
||||
```
|
||||
|
||||
@@ -151,7 +270,7 @@ Object.assign(ReactTableDefaults, {
|
||||
})
|
||||
```
|
||||
|
||||
Or just define them on the component per-instance
|
||||
Or just define them as props
|
||||
|
||||
```javascript
|
||||
<ReactTable
|
||||
@@ -167,17 +286,22 @@ Or just define them on the component per-instance
|
||||
```javascript
|
||||
[{
|
||||
// General
|
||||
accessor: 'propertyName' or Accessor eg. (row) => row.propertyName,
|
||||
accessor: 'propertyName', // or Accessor eg. (row) => row.propertyName (see "Accessors" section for more details)
|
||||
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
|
||||
minWidth: 100 // A minimum width for this column. If there is room, columns will flex to fill available space
|
||||
width: undefined, // A hardcoded width for the column. This overrides both min and max width options
|
||||
minWidth: 100, // A minimum width for this column. If there is extra room, column will flex to fill available space (up to the max-width, if set)
|
||||
maxWidth: undefined, // A maximum width for this column.
|
||||
|
||||
// Special
|
||||
expander: false, // This option will override all data-related options and designates the column to be used
|
||||
// for pivoting and sub-component expansion
|
||||
|
||||
// Cell Options
|
||||
className: '', // Set the classname of the `td` element of the column
|
||||
style: {}, // Set the style of the `td` element of the column
|
||||
render: JSX eg. ({value, rowValues, row, index, viewIndex}) => <span>{value}</span>, // Provide a JSX element or stateless function to render whatever you want as the column's cell with access to the entire row
|
||||
render: JSX eg. (rowInfo: {value, rowValues, row, index, viewIndex}) => <span>{value}</span>, // Provide a JSX element or stateless function to render whatever you want as the column's cell with access to the entire row
|
||||
// value == the accessed value of the column
|
||||
// rowValues == an object of all of the accessed values for the row
|
||||
// row == the original row of data supplied to the table
|
||||
@@ -185,24 +309,59 @@ Or just define them on the component per-instance
|
||||
// viewIndex == the index of the row in the current page
|
||||
|
||||
// Header & HeaderGroup Options
|
||||
header: 'Header Name' or JSX eg. ({data, column}) => <div>Header Name</div>,
|
||||
header: 'Header Name', a function that returns a primitive, or JSX / React Component eg. ({data, column}) => <div>Header Name</div>,
|
||||
headerClassName: '', // Set the classname of the `th` element of the column
|
||||
headerStyle: {}, // Set the style of the `th` element of the column
|
||||
getHeaderProps: (state, rowInfo, column, instance) => ({}), // a function that returns props to decorate the `th` element of the column
|
||||
|
||||
// Header Groups only
|
||||
columns: [...] // See Header Groups section below
|
||||
columns: [...], // See Header Groups section below
|
||||
|
||||
// Footer
|
||||
footer: 'Footer Name' or JSX eg. ({data, column}) => <div>Footer Name</div>,
|
||||
footerClassName: '', // Set the classname of the `td` element of the column's footer
|
||||
footerStyle: {}, // Set the style of the `td` element of the column's footer
|
||||
getFooterProps: (state, rowInfo, column, instance) => ({}), // A function that returns props to decorate the `td` element of the column's footer
|
||||
|
||||
// Filtering
|
||||
filterMethod: (filter, row, column) => {return true}, // A function returning a boolean that specifies the filtering logic for the column
|
||||
// filter == an object specifying which filter is being applied. Format: {id: [the filter column's id], value: [the value the user typed in the filter field], pivotId: [if filtering on a pivot column, the pivotId will be set to the pivot column's id and the `id` field will be set to the top level pivoting column]}
|
||||
// row == the row of data supplied to the table
|
||||
// column == the column that the filter is on
|
||||
hideFilter: false, // If `showFilters` is set on the table, this option will let you selectively hide the filter on a particular row
|
||||
filterRender: JSX // eg. ({filter, onFilterChange}) => <select onChange={event => onFilterChange(event.target.value)} value={filter ? filter.value : ''}></select> // The value passed to onFilterChange will be the value passed to filter.value of the filterMethod
|
||||
}]
|
||||
```
|
||||
|
||||
## 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 :)
|
||||
## Accessors
|
||||
Accessors are functions that return the value to populate the row's value for the column.
|
||||
This lets the render function not have to worry about accessing the correct data, the value is automatically populated in it's props.
|
||||
|
||||
## Header Groups
|
||||
To group columns with another header column, just nest your columns in a header column like so:
|
||||
If a `string` or `array` is passed the default accessor is used.
|
||||
The default accessor will parse the input into an array and recursively flatten it.
|
||||
Any values that contain a dot (`.`) will be split.
|
||||
Any values that contain bracket (`[]`) will be split.
|
||||
This array is then used as the path to the value to return.
|
||||
|
||||
("$" is the placeholder value that would be returned by the default accessor)
|
||||
|
||||
| value | path | data |
|
||||
|--------------|-----------------|------------------------|
|
||||
| "a" | ["a"] | {"a": $} |
|
||||
| "a.b" | ["a", "b"] | {"a": {"b": $}} |
|
||||
| "a[0]" | ["a", "0"] | {"a": [$]} |
|
||||
| ["a.b", "c"] | ["a", "b", "c"] | {"a": {"b": {"c": $}}} |
|
||||
|
||||
*NOTE*
|
||||
If your data has a field/key with a dot (`.`) you will need to supply a custom accessor.
|
||||
|
||||
|
||||
## Column Header Groups
|
||||
To group columns with another header column, just nest your columns in a header column. Header columns utilize the same header properties as regular columns.
|
||||
```javascript
|
||||
const columns = [{
|
||||
header: 'Favorites',
|
||||
headerClassName: 'my-favorites-column-header-group'
|
||||
columns: [{
|
||||
header: 'Color',
|
||||
accessor: 'favorites.color'
|
||||
@@ -216,7 +375,153 @@ const columns = [{
|
||||
}]
|
||||
```
|
||||
|
||||
## Pivoting & Aggregation
|
||||
## Custom Cell, Header and Footer Rendering
|
||||
You can use any react component or JSX to display content in column headers, cells and footers. Any component you use will be passed the following props (if available):
|
||||
- `row` - Original row from your data
|
||||
- `rowValues` - The post-accessed values from the original row
|
||||
- `index` - The index of the row
|
||||
- `viewIndex` - the index of the row relative to the current page
|
||||
- `level` - The nesting depth (zero-indexed)
|
||||
- `nestingPath` - The nesting path of the row
|
||||
- `aggregated` - A boolean stating if the row is an aggregation row
|
||||
- `subRows` - An array of any expandable sub-rows contained in this row
|
||||
|
||||
```javascript
|
||||
// This column uses a stateless component to produce a different colored bar depending on the value
|
||||
// You can also use stateful components or any other function that returns JSX
|
||||
const columns = [{
|
||||
header: () => <span><i className='fa-tasks' /> Progress</span>,
|
||||
accessor: 'progress',
|
||||
render: row => (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#dadada',
|
||||
borderRadius: '2px'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: `${row.value}%`,
|
||||
height: '100%',
|
||||
backgroundColor: row.value > 66 ? '#85cc00'
|
||||
: row.value > 33 ? '#ffbf00'
|
||||
: '#ff2e00',
|
||||
borderRadius: '2px',
|
||||
transition: 'all .2s ease-out'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}]
|
||||
```
|
||||
|
||||
## Styles
|
||||
- React-table ships with a minimal and clean stylesheet to get you on your feet quickly.
|
||||
- The stylesheet is located at `react-table/react-table.css`.
|
||||
- There are countless ways to import a stylesheet. If you have questions on how to do so, consult the documentation of your build system.
|
||||
|
||||
#### Classes
|
||||
- Adding a `-striped` className to ReactTable will slightly color odd numbered rows for legibility
|
||||
- Adding a `-highlight` className to ReactTable will highlight any row as you hover over it
|
||||
|
||||
#### CSS
|
||||
We think the default styles looks great! But, if you prefer a more custom look, all of the included styles are easily overridable. Every single component contains a unique class that makes it super easy to customize. Just go for it!
|
||||
|
||||
#### JS Styles
|
||||
Every single react-table element and `get[ComponentName]Props` callback supports `classname` and `style` props.
|
||||
|
||||
## Custom Props
|
||||
|
||||
#### Built-in Components
|
||||
Every single built-in component's props can be dynamically extended using any one of these prop-callbacks:
|
||||
```javascript
|
||||
<ReactTable
|
||||
getProps={fn}
|
||||
getTableProps={fn}
|
||||
getTheadGroupProps={fn}
|
||||
getTheadGroupTrProps={fn}
|
||||
getTheadGroupThProps={fn}
|
||||
getTheadProps={fn}
|
||||
getTheadTrProps={fn}
|
||||
getTheadThProps={fn}
|
||||
getTbodyProps={fn}
|
||||
getTrGroupProps={fn}
|
||||
getTrProps={fn}
|
||||
getThProps={fn}
|
||||
getTdProps={fn}
|
||||
getPaginationProps={fn}
|
||||
getLoadingProps={fn}
|
||||
getNoDataProps: {fn},
|
||||
getResizerProps: {fn}
|
||||
/>
|
||||
```
|
||||
|
||||
These callbacks are executed with each render of the element with four parameters:
|
||||
1. Table State
|
||||
2. RowInfo (undefined if not applicable)
|
||||
3. Column (undefined if not applicable)
|
||||
4. React Table Instance
|
||||
|
||||
This makes it extremely easy to add, say... a row click callback!
|
||||
```javascript
|
||||
// When any Td element is clicked, we'll log out some information
|
||||
<ReactTable
|
||||
getTdProps={(state, rowInfo, column, instance) => {
|
||||
return {
|
||||
onClick: e => {
|
||||
console.log('A Td Element was clicked!')
|
||||
console.log('it produced this event:', e)
|
||||
console.log('It was in this column:', column)
|
||||
console.log('It was in this row:', rowInfo)
|
||||
console.log('It was in this table instance:', instance)
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
You can use these callbacks for dynamic styling as well!
|
||||
```javascript
|
||||
// Any Tr element will be green if its (row.age > 20)
|
||||
<ReactTable
|
||||
getTrProps={(state, rowInfo, column) => {
|
||||
return {
|
||||
style: {
|
||||
background: rowInfo.age > 20 ? 'green' : 'red'
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Column Components
|
||||
Just as core components can have dynamic props, columns and column headers can too!
|
||||
|
||||
You can utilize either of these prop callbacks on columns:
|
||||
```javascript
|
||||
const columns = [{
|
||||
getHeaderProps: () => (...),
|
||||
getProps: () => (...)
|
||||
}]
|
||||
```
|
||||
|
||||
In a similar fashion these can be used to dynamically style just about anything!
|
||||
```javascript
|
||||
// This columns cells will be red if (row.name === Santa Clause)
|
||||
const columns = [{
|
||||
getProps: (state, rowInfo, column) => {
|
||||
return {
|
||||
style: {
|
||||
background: rowInfo.name === 'Santa Clause' ? 'red' : null
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
## Pivoting and Aggregation
|
||||
Pivoting the table will group records together based on their accessed values and allow the rows in that group to be expanded underneath it.
|
||||
To pivot, pass an array of `columnID`'s to `pivotBy`. Remember, a column's `id` is either the one that you assign it (when using a custom accessors) or its `accessor` string.
|
||||
```javascript
|
||||
@@ -246,7 +551,7 @@ const columns = [{
|
||||
|
||||
Pivoted columns can be sorted just like regular columns, but not independently of each other. For instance, if you click to sort the pivot column in ascending order, it will sort by each pivot recursively in ascending order together.
|
||||
|
||||
## Sub Tables & Sub Components
|
||||
## Sub Tables and Sub Components
|
||||
By adding a `SubComponent` props, you can easily add an expansion level to all root-level rows:
|
||||
```javascript
|
||||
<ReactTable
|
||||
@@ -265,7 +570,7 @@ By adding a `SubComponent` props, you can easily add an expansion level to all r
|
||||
|
||||
|
||||
## Server-side Data
|
||||
If you want to handle pagination, and sorting on the server, `react-table` makes it easy on you.
|
||||
If you want to handle pagination, sorting, and filtering on the server, `react-table` makes it easy on you.
|
||||
|
||||
1. Feed React Table `data` from somewhere dynamic. eg. `state`, a redux store, etc...
|
||||
1. Add `manual` as a prop. This informs React Table that you'll be handling sorting and pagination server-side
|
||||
@@ -288,7 +593,8 @@ If you want to handle pagination, and sorting on the server, `react-table` makes
|
||||
Axios.post('mysite.com/data', {
|
||||
page: state.page,
|
||||
pageSize: state.pageSize,
|
||||
sorting: state.sorting
|
||||
sorting: state.sorting,
|
||||
filtering: state.filtering
|
||||
})
|
||||
.then((res) => {
|
||||
// Update react-table
|
||||
@@ -302,7 +608,7 @@ 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)
|
||||
For a detailed example, take a peek at our <a href="https://github.com/tannerlinsley/react-table/blob/master/stories/ServerSide.js" target="\_parent">async table mockup</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.
|
||||
@@ -334,30 +640,85 @@ Here are the props and their corresponding callbacks that control the state of t
|
||||
onPageSizeChange={(pageSize, pageIndex) => {...}} // Called when the pageSize is changed by the user. The resolve page is also sent to maintain approximate position in the data
|
||||
onSortingChange={(column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. If the column is a pivoted column, `column` will be an array of columns
|
||||
onExpandRow={(index, event) => {...}} // Called when an expander is clicked. Use this to manage `expandedRows`
|
||||
onFilteringChange={(column, value) => {...}} // Called when a user enters a value into a filter input field or the value passed to the onFilterChange handler by the filterRender option.
|
||||
onResize={(column, event, isTouch) => {...}} // Called when a user clicks on a resizing component (the right edge of a column header)
|
||||
/>
|
||||
```
|
||||
|
||||
## Functional Rendering
|
||||
Possibly one of the coolest features of React-Table is its ability to expose internal components and state for custom render logic. The easiest way to do this is to pass a function as the child of `<ReactTable />`.
|
||||
|
||||
The function you pass will be called with the following items:
|
||||
- Fully-resolved state of the table
|
||||
- A function that returns the standard table component
|
||||
- The instance of the component
|
||||
|
||||
You can then return any JSX or react you want! This turns out to be perfect for:
|
||||
- Accessing the internal state of the table without a `ref`
|
||||
- Decorating the table or extending it with your own UI
|
||||
- Building your own custom display logic
|
||||
|
||||
Accessing internal state and wrapping with more UI:
|
||||
```javascript
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
>
|
||||
{(state, makeTable, instance) => {
|
||||
return (
|
||||
<div style={{
|
||||
background: '#ffcf00',
|
||||
borderRadius: '5px',
|
||||
overflow: 'hidden',
|
||||
padding: '5px'
|
||||
}}>
|
||||
<pre><code>state.allVisibleColumns === {JSON.stringify(state.allVisibleColumns, null, 4)}</code></pre>
|
||||
{makeTable()}
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</ReactTable>
|
||||
```
|
||||
|
||||
The possibilities are endless!
|
||||
|
||||
## 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!
|
||||
|
||||
## Filtering
|
||||
Filtering can be enabled by setting the `showFilters` option on the table.
|
||||
|
||||
If you don't want particular column to be filtered you can set the `hideFilter` option on the column.
|
||||
|
||||
By default the table tries to filter by checking if the row's value starts with the filter text. The default method for filtering the table can be set with the table's `defaultFilterMethod` option.
|
||||
|
||||
If you want to override a particular column's filtering method, you can set the `filterMethod` option on a column.
|
||||
|
||||
To completely override the filter that is shown, you can set the `filterRender` column option. Using this option you can specify the JSX that is shown. The option is passed an `onFilterChange` method which must be called with the the value that you wan't to pass to the `filterMethod` option whenever the filter has changed.
|
||||
|
||||
See <a href="http://react-table.js.org/?selectedKind=2.%20Demos&selectedStory=Custom%20Filtering&full=0&down=1&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel" target="\_parent">Custom Filtering</a> demo for examples.
|
||||
|
||||
## Component Overrides
|
||||
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,
|
||||
TrGroupComponent: Component,
|
||||
TrComponent: Component,
|
||||
ThComponent: Component,
|
||||
TdComponent: Component,
|
||||
PaginationComponent: Component,
|
||||
PreviousComponent: Component,
|
||||
NextComponent: Component,
|
||||
LoadingComponent: Component,
|
||||
ExpanderComponent: Component
|
||||
TableComponent: component,
|
||||
TheadComponent: component,
|
||||
TbodyComponent: component,
|
||||
TrGroupComponent: component,
|
||||
TrComponent: component,
|
||||
ThComponent: component
|
||||
TdComponent: component,
|
||||
TfootComponent: component,
|
||||
ExpanderComponent: component,
|
||||
PaginationComponent: component,
|
||||
PreviousComponent: undefined,
|
||||
NextComponent: undefined,
|
||||
LoadingComponent: component,
|
||||
NoDataComponent: component,
|
||||
ResizerComponent: component
|
||||
})
|
||||
|
||||
// Or change per instance
|
||||
@@ -368,7 +729,7 @@ Object.assign(ReactTableDefaults, {
|
||||
/>
|
||||
```
|
||||
|
||||
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.
|
||||
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 <a href="https://github.com/tannerlinsley/react-table/blob/master/src/index.js" target="\_parent">the source</a> for the component you wish to replace.
|
||||
|
||||
|
||||
## Contributing
|
||||
@@ -376,23 +737,22 @@ To suggest a feature, create an issue if it does not already exist.
|
||||
If you would like to help develop a suggested feature follow these steps:
|
||||
|
||||
- Fork this repo
|
||||
- `npm install`
|
||||
- `npm run watch`
|
||||
- `$ yarn`
|
||||
- `$ yarn run storybook`
|
||||
- Implement your changes to files in the `src/` directory
|
||||
- View changes as you code via our <a href="https://github.com/storybooks/react-storybook" target="\_parent">React Storybook</a> `localhost:8000`
|
||||
- Make changes to stories in `/stories`, or create a new one if needed
|
||||
- Submit PR for review
|
||||
|
||||
If you would like to preview your changes, you can link and utilize the example like so:
|
||||
#### Scripts
|
||||
|
||||
- `npm link`
|
||||
- `cd example`
|
||||
- `npm install`
|
||||
- `npm link react-table`
|
||||
- `npm run watch`
|
||||
- Make changes to the example in `src/screens/index.js` if needed
|
||||
- View changes at `localhost:8000`
|
||||
- `$ yarn run storybook` Runs the storybook server
|
||||
- `$ yarn run test` Runs the test suite
|
||||
- `$ yarn run prepublish` Builds the distributable bundle
|
||||
- `$ yarn run docs` Builds the website/docs from the storybook for github pages
|
||||
|
||||
## Used By
|
||||
|
||||
<a href='https://nozzle.io'>
|
||||
<a href='https://nozzle.io' target="\_parent">
|
||||
<img src='https://nozzle.io/img/logo-blue.png' alt='Nozzle Logo' style='width:300px;'/>
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
react-table.js.org
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,22 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script>
|
||||
if (window.parent !== window) {
|
||||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
}
|
||||
</script>
|
||||
<title>React Storybook</title>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div id="error-display"></div>
|
||||
<script src="static/preview.0cd0bb2bf09f3220bea7.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="storybook-version" content="2.35.3">
|
||||
<title>React Storybook</title>
|
||||
<style>
|
||||
/*
|
||||
When resizing panels, the drag event breaks if the cursor
|
||||
moves over the iframe. Add the 'dragging' class to the body
|
||||
at drag start and remove it when the drag ends.
|
||||
*/
|
||||
.dragging iframe {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Styling the fuzzy search box placeholders */
|
||||
.searchBox::-webkit-input-placeholder { /* Chrome/Opera/Safari */
|
||||
color: #ddd;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.searchBox::-moz-placeholder { /* Firefox 19+ */
|
||||
color: #ddd;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.searchBox:focus{
|
||||
border-color: #EEE !important;
|
||||
}
|
||||
|
||||
.btn:hover{
|
||||
background-color: #eee
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="margin: 0;">
|
||||
<div id="root"></div>
|
||||
<script src="static/manager.064c8fe6b78907f0a142.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"static/manager.064c8fe6b78907f0a142.bundle.js","sources":["webpack:///static/manager.064c8fe6b78907f0a142.bundle.js"],"mappings":"AAAA;AAkuDA;AA84DA;AAy8DA;AA00DA;AAsyEA;AA89CA;AA+rDA;AAsiDA;AAg6DA;AA2nDA;AA++CA;AAkvDA;AAsnEA;AA2oDA;AAivCA;AA+nDA;AAkpDA;AA6lEA;AAs4DA;AAquDA;AA+pDA;AAqxDA;AAsrDA;AA4yDA;AA88GA;AAj6CA;AAopGA;AAsuFA;AA+zEA;AAtMA;AAizEA","sourceRoot":""}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"static/preview.0cd0bb2bf09f3220bea7.bundle.js","sources":["webpack:///static/preview.0cd0bb2bf09f3220bea7.bundle.js"],"mappings":"AAAA;AAkuDA;AA62DA;AAwtFA;AAsiFA;AA0uOA;AAgmGA;AA6rDA;AA0iEA;AA0xDA;AA25DA;AAmqDA;AA0vDA;AA+9CA;AAg5DA;AAwoDA;AAk7CA;AA2pDA;AAmjEA;AA27DA;AAghCA;AA4vDA;AA8kDA;AAwpEA;AAk3DA;AAy0DA;AA8xCA;AAu3EA;AAi4GA;AAwoDA;AAk+CA;AA+oCA;AAutCA;AA4jDA;AAwcA;AA+jFA","sourceRoot":""}
|
||||
+12
-6
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-table",
|
||||
"version": "4.2.1",
|
||||
"version": "5.5.2",
|
||||
"description": "A fast, lightweight, opinionated table and datagrid built on React",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/tannerlinsley/react-table#readme",
|
||||
@@ -24,15 +24,16 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build:node": "babel src --out-dir lib --source-maps inline",
|
||||
"build:css": "rm -rf react-table.css && stylus src/index.styl --use ./node_modules/nib/lib/nib.js --compress -o react-table.css",
|
||||
"build:css": "rimraf react-table.css && stylus src/index.styl --compress -o react-table.css && yarn run css:autoprefix",
|
||||
"css:autoprefix": "postcss --use autoprefixer react-table.css -r",
|
||||
"watch": "npm-run-all --parallel watch:*",
|
||||
"watch:node": "onchange 'src/**/*.js' -i -- npm run build:node",
|
||||
"watch:css": "onchange 'src/**/*.styl' -i -- npm run build:css",
|
||||
"test": "standard",
|
||||
"umd": "rm -rf react-table.js && browserify lib/index.js -s reactTable -x react -t babelify -g uglifyify -o react-table.js",
|
||||
"umd": "rimraf react-table.js && browserify lib/index.js -s reactTable -x react -t babelify -g uglifyify -o react-table.js",
|
||||
"prepublish": "npm-run-all build:* && npm run umd",
|
||||
"storybook": "start-storybook -p 8000 -c .storybook",
|
||||
"deploy": "build-storybook && zab deploy"
|
||||
"docs": "build-storybook -o docs && cp .storybook/CNAME docs/CNAME"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5"
|
||||
@@ -42,6 +43,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kadira/storybook": "^2.35.1",
|
||||
"autoprefixer": "^6.7.0",
|
||||
"babel-cli": "6.14.0",
|
||||
"babel-eslint": "6.1.2",
|
||||
"babel-preset-es2015": "6.14.0",
|
||||
@@ -53,12 +55,15 @@
|
||||
"html-loader": "^0.4.4",
|
||||
"markdown-loader": "^0.1.7",
|
||||
"namor": "^0.3.0",
|
||||
"nib": "^1.1.2",
|
||||
"npm-run-all": "^3.1.1",
|
||||
"onchange": "^3.0.2",
|
||||
"postcss-cli": "^2.6.0",
|
||||
"react": "^15.4.2",
|
||||
"react-dom": "^15.4.2",
|
||||
"react-json-tree": "^0.10.1",
|
||||
"rimraf": "^2.6.1",
|
||||
"standard": "^8.0.0",
|
||||
"storybook": "^0.0.0",
|
||||
"stylus": "^0.54.5",
|
||||
"uglifyify": "3.0.3"
|
||||
},
|
||||
@@ -73,7 +78,8 @@
|
||||
"lib",
|
||||
"example",
|
||||
"react-table.js",
|
||||
"stories"
|
||||
"stories",
|
||||
"docs"
|
||||
]
|
||||
},
|
||||
"babel": {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,179 @@
|
||||
import React from 'react'
|
||||
import classnames from 'classnames'
|
||||
//
|
||||
import _ from './utils'
|
||||
import Pagination from './pagination'
|
||||
|
||||
const emptyObj = () => ({})
|
||||
|
||||
export default {
|
||||
// General
|
||||
data: [],
|
||||
loading: false,
|
||||
showPagination: true,
|
||||
showPageSizeOptions: true,
|
||||
pageSizeOptions: [5, 10, 20, 25, 50, 100],
|
||||
defaultPageSize: 20,
|
||||
showPageJump: true,
|
||||
expanderColumnWidth: 35,
|
||||
collapseOnSortingChange: true,
|
||||
collapseOnPageChange: true,
|
||||
collapseOnDataChange: true,
|
||||
freezeWhenExpanded: false,
|
||||
defaultSorting: [],
|
||||
showFilters: false,
|
||||
defaultFiltering: [],
|
||||
defaultFilterMethod: (filter, row, column) => {
|
||||
const id = filter.pivotId || filter.id
|
||||
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
|
||||
},
|
||||
resizable: true,
|
||||
defaultResizing: [],
|
||||
|
||||
// Controlled State Overrides
|
||||
// page: undefined,
|
||||
// pageSize: undefined,
|
||||
// sorting: undefined,
|
||||
|
||||
// Controlled State Callbacks
|
||||
onExpandSubComponent: undefined,
|
||||
onPageChange: undefined,
|
||||
onPageSizeChange: undefined,
|
||||
onSortingChange: undefined,
|
||||
onFilteringChange: undefined,
|
||||
onResize: undefined,
|
||||
|
||||
// Pivoting
|
||||
pivotBy: undefined,
|
||||
pivotColumnWidth: 200,
|
||||
pivotValKey: '_pivotVal',
|
||||
pivotIDKey: '_pivotID',
|
||||
subRowsKey: '_subRows',
|
||||
|
||||
// Pivoting State Overrides
|
||||
// expandedRows: {},
|
||||
|
||||
// Pivoting State Callbacks
|
||||
onExpandRow: undefined,
|
||||
|
||||
// General Callbacks
|
||||
onChange: () => null,
|
||||
|
||||
// Classes
|
||||
className: '',
|
||||
style: {},
|
||||
|
||||
// Component decorators
|
||||
getProps: emptyObj,
|
||||
getTableProps: emptyObj,
|
||||
getTheadGroupProps: emptyObj,
|
||||
getTheadGroupTrProps: emptyObj,
|
||||
getTheadGroupThProps: emptyObj,
|
||||
getTheadProps: emptyObj,
|
||||
getTheadTrProps: emptyObj,
|
||||
getTheadThProps: emptyObj,
|
||||
getTheadFilterProps: emptyObj,
|
||||
getTheadFilterTrProps: emptyObj,
|
||||
getTheadFilterThProps: emptyObj,
|
||||
getTbodyProps: emptyObj,
|
||||
getTrGroupProps: emptyObj,
|
||||
getTrProps: emptyObj,
|
||||
getTdProps: emptyObj,
|
||||
getTfootProps: emptyObj,
|
||||
getTfootTrProps: emptyObj,
|
||||
getTfootTdProps: emptyObj,
|
||||
getPaginationProps: emptyObj,
|
||||
getLoadingProps: emptyObj,
|
||||
getNoDataProps: emptyObj,
|
||||
getResizerProps: emptyObj,
|
||||
|
||||
// Global Column Defaults
|
||||
column: {
|
||||
sortable: true,
|
||||
show: true,
|
||||
minWidth: 100,
|
||||
// Cells only
|
||||
render: undefined,
|
||||
className: '',
|
||||
style: {},
|
||||
getProps: emptyObj,
|
||||
// Headers only
|
||||
header: undefined,
|
||||
headerClassName: '',
|
||||
headerStyle: {},
|
||||
getHeaderProps: emptyObj,
|
||||
// Footers only
|
||||
footer: undefined,
|
||||
footerClassName: '',
|
||||
footerStyle: {},
|
||||
getFooterProps: emptyObj,
|
||||
filterMethod: undefined,
|
||||
hideFilter: false,
|
||||
filterRender: ({filter, onFilterChange}) => (
|
||||
<input type='text'
|
||||
style={{
|
||||
width: '100%'
|
||||
}}
|
||||
value={filter ? filter.value : ''}
|
||||
onChange={(event) => onFilterChange(event.target.value)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
||||
// Text
|
||||
previousText: 'Previous',
|
||||
nextText: 'Next',
|
||||
loadingText: 'Loading...',
|
||||
noDataText: 'No rows found',
|
||||
pageText: 'Page',
|
||||
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}) => {
|
||||
return (
|
||||
<div
|
||||
className={classnames(className, 'rt-th')}
|
||||
onClick={e => {
|
||||
toggleSort && toggleSort(e)
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
TdComponent: _.makeTemplateComponent('rt-td'),
|
||||
TfootComponent: _.makeTemplateComponent('rt-tfoot'),
|
||||
ExpanderComponent: ({isExpanded, ...rest}) => {
|
||||
return (
|
||||
<div
|
||||
className={classnames('rt-expander', isExpanded && '-open')}
|
||||
{...rest}
|
||||
>•</div>
|
||||
)
|
||||
},
|
||||
PaginationComponent: Pagination,
|
||||
PreviousComponent: undefined,
|
||||
NextComponent: undefined,
|
||||
LoadingComponent: ({className, loading, loadingText, ...rest}) => (
|
||||
<div className={classnames(
|
||||
'-loading',
|
||||
{'-active': loading},
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
<div className='-loading-inner'>
|
||||
{loadingText}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
NoDataComponent: _.makeTemplateComponent('rt-noData'),
|
||||
ResizerComponent: _.makeTemplateComponent('rt-resizer')
|
||||
}
|
||||
+779
-798
File diff suppressed because it is too large
Load Diff
+61
-4
@@ -1,5 +1,3 @@
|
||||
@import 'nib'
|
||||
|
||||
$easeOutQuad = cubic-bezier(0.250, 0.460, 0.450, 0.940)
|
||||
$easeOutBack = cubic-bezier(0.175, 0.885, 0.320, 1.275)
|
||||
$expandSize = 7px
|
||||
@@ -20,10 +18,21 @@ $expandSize = 7px
|
||||
.rt-thead
|
||||
display: flex
|
||||
flex-direction: column
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
&.-headerGroups
|
||||
background: alpha(black, .03)
|
||||
border-bottom: 1px solid alpha(black, .05)
|
||||
|
||||
&.-filters
|
||||
border-bottom: 1px solid alpha(black, 0.05)
|
||||
|
||||
.rt-th
|
||||
border-right: 1px solid alpha(black, 0.02)
|
||||
|
||||
&.-header
|
||||
box-shadow: 0 2px 15px 0px alpha(black, .15)
|
||||
|
||||
@@ -32,7 +41,9 @@ $expandSize = 7px
|
||||
|
||||
.rt-th
|
||||
.rt-td
|
||||
color: black
|
||||
padding: 5px 5px
|
||||
line-height: normal
|
||||
position: relative
|
||||
border-right: 1px solid alpha(black, .05)
|
||||
transition box-shadow .3s $easeOutBack
|
||||
box-shadow:inset 0 0 0 0 transparent
|
||||
@@ -44,6 +55,16 @@ $expandSize = 7px
|
||||
cursor: pointer
|
||||
&:last-child
|
||||
border-right: 0
|
||||
|
||||
.rt-resizable-header
|
||||
overflow: visible
|
||||
&:last-child
|
||||
overflow: hidden
|
||||
|
||||
.rt-resizable-header-content
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
|
||||
.rt-tbody
|
||||
display: flex
|
||||
flex-direction: column
|
||||
@@ -103,6 +124,26 @@ $expandSize = 7px
|
||||
&.-open:after
|
||||
transform: translate(-50%, -50%) rotate(0deg)
|
||||
|
||||
.rt-resizer
|
||||
display: inline-block
|
||||
position: absolute
|
||||
width: 36px
|
||||
top: 0
|
||||
bottom: 0
|
||||
right: -18px
|
||||
cursor: col-resize
|
||||
z-index: 10
|
||||
|
||||
.rt-tfoot
|
||||
display: flex
|
||||
flex-direction: column
|
||||
box-shadow: 0 0px 15px 0px alpha(black, .15)
|
||||
|
||||
.rt-td
|
||||
border-right:1px solid alpha(black, .05)
|
||||
&:last-child
|
||||
border-right:0
|
||||
|
||||
&.-striped
|
||||
.rt-tr.-odd
|
||||
background: alpha(black, .03)
|
||||
@@ -119,6 +160,7 @@ $expandSize = 7px
|
||||
flex-wrap: wrap
|
||||
padding: 3px
|
||||
box-shadow: 0 0px 15px 0px alpha(black, .1)
|
||||
border-top: 2px solid alpha(black, .1)
|
||||
|
||||
.-btn
|
||||
appearance:none
|
||||
@@ -172,6 +214,18 @@ $expandSize = 7px
|
||||
.-pageSizeOptions
|
||||
margin: 3px 10px
|
||||
|
||||
.rt-noData
|
||||
display:block
|
||||
position:absolute
|
||||
left:50%
|
||||
top:50%
|
||||
transform: translate(-50%, -50%)
|
||||
background: alpha(white, .8)
|
||||
transition: all .3s ease
|
||||
z-index: 1
|
||||
pointer-events: none
|
||||
padding: 20px
|
||||
color: alpha(black, .5)
|
||||
|
||||
.-loading
|
||||
display:block
|
||||
@@ -206,7 +260,6 @@ $expandSize = 7px
|
||||
|
||||
input
|
||||
select
|
||||
appearance: none
|
||||
border: 1px solid rgba(0,0,0,0.1)
|
||||
background: white
|
||||
padding: 5px 7px
|
||||
@@ -215,6 +268,10 @@ $expandSize = 7px
|
||||
font-weight: normal
|
||||
outline:none
|
||||
|
||||
input:not([type="checkbox"]):not([type="radio"])
|
||||
select
|
||||
appearance: none
|
||||
|
||||
.select-wrap
|
||||
position:relative
|
||||
display:inline-block
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import _ from './utils'
|
||||
import defaultProps from './defaultProps'
|
||||
|
||||
export default {
|
||||
getDefaultProps () {
|
||||
return defaultProps
|
||||
},
|
||||
|
||||
getInitialState () {
|
||||
return {
|
||||
page: 0,
|
||||
pageSize: this.props.defaultPageSize || 10,
|
||||
sorting: this.props.defaultSorting,
|
||||
expandedRows: {},
|
||||
filtering: this.props.defaultFiltering,
|
||||
resizing: this.props.defaultResizing,
|
||||
currentlyResizing: undefined,
|
||||
skipNextSort: false
|
||||
}
|
||||
},
|
||||
|
||||
getResolvedState (props, state) {
|
||||
const resolvedState = {
|
||||
..._.compactObject(this.state),
|
||||
..._.compactObject(this.props),
|
||||
..._.compactObject(state),
|
||||
..._.compactObject(props)
|
||||
}
|
||||
return resolvedState
|
||||
},
|
||||
|
||||
componentWillMount () {
|
||||
this.setStateWithData(this.getDataModel(this.getResolvedState()))
|
||||
},
|
||||
|
||||
componentDidMount () {
|
||||
this.fireOnChange()
|
||||
},
|
||||
|
||||
componentWillReceiveProps (nextProps, nextState) {
|
||||
const oldState = this.getResolvedState()
|
||||
const newState = this.getResolvedState(nextProps, nextState)
|
||||
|
||||
if (oldState.defaultSorting !== newState.defaultSorting) {
|
||||
newState.sorting = newState.defaultSorting
|
||||
}
|
||||
|
||||
if ((oldState.showFilters !== newState.showFilters) ||
|
||||
(oldState.showFilters !== newState.showFilters)) {
|
||||
newState.filtering = newState.defaultFiltering
|
||||
}
|
||||
|
||||
// Props that trigger a data update
|
||||
if (
|
||||
oldState.data !== newState.data ||
|
||||
oldState.columns !== newState.columns ||
|
||||
oldState.pivotBy !== newState.pivotBy ||
|
||||
oldState.sorting !== newState.sorting ||
|
||||
oldState.showFilters !== newState.showFilters ||
|
||||
oldState.filtering !== newState.filtering
|
||||
) {
|
||||
this.setStateWithData(this.getDataModel(newState))
|
||||
}
|
||||
},
|
||||
|
||||
setStateWithData (newState, cb) {
|
||||
const oldState = this.getResolvedState()
|
||||
const newResolvedState = this.getResolvedState({}, newState)
|
||||
const {freezeWhenExpanded} = newResolvedState
|
||||
|
||||
// Default to unfrozen state
|
||||
newResolvedState.frozen = false
|
||||
|
||||
// If freezeWhenExpanded is set, check for frozen conditions
|
||||
if (freezeWhenExpanded) {
|
||||
// if any rows are expanded, freeze the existing data and sorting
|
||||
const keys = Object.keys(newResolvedState.expandedRows)
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (newResolvedState.expandedRows[keys[i]]) {
|
||||
newResolvedState.frozen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the data isn't frozen and either the data or
|
||||
// sorting model has changed, update the data
|
||||
if (
|
||||
(oldState.frozen && !newResolvedState.frozen) ||
|
||||
oldState.sorting !== newResolvedState.sorting ||
|
||||
oldState.filtering !== newResolvedState.filtering ||
|
||||
oldState.showFilters !== newResolvedState.showFilters ||
|
||||
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData)
|
||||
) {
|
||||
// Handle collapseOnSortingChange & collapseOnDataChange
|
||||
if (
|
||||
(oldState.sorting !== newResolvedState.sorting && this.props.collapseOnSortingChange) ||
|
||||
(oldState.filtering !== newResolvedState.filtering) ||
|
||||
(oldState.showFilters !== newResolvedState.showFilters) ||
|
||||
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData && this.props.collapseOnDataChange)
|
||||
) {
|
||||
newResolvedState.expandedRows = {}
|
||||
}
|
||||
|
||||
Object.assign(newResolvedState, this.getSortedData(newResolvedState))
|
||||
}
|
||||
|
||||
// Calculate pageSize all the time
|
||||
if (newResolvedState.sortedData) {
|
||||
newResolvedState.pages = newResolvedState.manual ? newResolvedState.pages : Math.ceil(newResolvedState.sortedData.length / newResolvedState.pageSize)
|
||||
newResolvedState.page = Math.max(newResolvedState.page >= newResolvedState.pages ? newResolvedState.pages - 1 : newResolvedState.page, 0)
|
||||
}
|
||||
|
||||
return this.setState(newResolvedState, cb)
|
||||
}
|
||||
}
|
||||
+574
@@ -0,0 +1,574 @@
|
||||
import _ from './utils'
|
||||
|
||||
export default {
|
||||
getDataModel (newState) {
|
||||
const {
|
||||
columns,
|
||||
pivotBy = [],
|
||||
data,
|
||||
pivotIDKey,
|
||||
pivotValKey,
|
||||
subRowsKey,
|
||||
expanderColumnWidth,
|
||||
SubComponent
|
||||
} = newState
|
||||
|
||||
// Determine Header Groups
|
||||
let hasHeaderGroups = false
|
||||
columns.forEach(column => {
|
||||
if (column.columns) {
|
||||
hasHeaderGroups = true
|
||||
}
|
||||
})
|
||||
|
||||
// Build Header Groups
|
||||
const headerGroups = []
|
||||
let currentSpan = []
|
||||
|
||||
// A convenience function to add a header and reset the currentSpan
|
||||
const addHeader = (columns, column = columns[0]) => {
|
||||
headerGroups.push({
|
||||
...this.props.column,
|
||||
...column,
|
||||
columns: columns
|
||||
})
|
||||
currentSpan = []
|
||||
}
|
||||
|
||||
const noSubExpanderColumns = columns.map(col => {
|
||||
return {
|
||||
...col,
|
||||
columns: col.columns ? col.columns.filter(d => !d.expander) : undefined
|
||||
}
|
||||
})
|
||||
|
||||
let expanderColumnIndex = columns.findIndex(col => col.expander)
|
||||
const needsExpander = (SubComponent || pivotBy.length) && expanderColumnIndex === -1
|
||||
const columnsWithExpander = needsExpander ? [{expander: true}, ...noSubExpanderColumns] : noSubExpanderColumns
|
||||
if (needsExpander) {
|
||||
expanderColumnIndex = 0
|
||||
}
|
||||
|
||||
const makeDecoratedColumn = (column) => {
|
||||
const dcol = {
|
||||
...this.props.column,
|
||||
...column
|
||||
}
|
||||
|
||||
if (dcol.expander) {
|
||||
dcol.width = expanderColumnWidth
|
||||
return dcol
|
||||
}
|
||||
|
||||
if (typeof dcol.accessor === 'string') {
|
||||
dcol.id = dcol.id || dcol.accessor
|
||||
const accessorString = dcol.accessor
|
||||
dcol.accessor = row => _.get(row, accessorString)
|
||||
return dcol
|
||||
}
|
||||
|
||||
if (dcol.accessor && !dcol.id) {
|
||||
console.warn(dcol)
|
||||
throw new Error('A column id is required if using a non-string accessor for column above.')
|
||||
}
|
||||
|
||||
if (!dcol.accessor) {
|
||||
dcol.accessor = d => undefined
|
||||
}
|
||||
|
||||
// Ensure minWidth is not greater than maxWidth if set
|
||||
if (dcol.maxWidth < dcol.minWidth) {
|
||||
dcol.minWidth = dcol.maxWidth
|
||||
}
|
||||
|
||||
return dcol
|
||||
}
|
||||
|
||||
// Decorate the columns
|
||||
const decorateAndAddToAll = (col) => {
|
||||
const decoratedColumn = makeDecoratedColumn(col)
|
||||
allDecoratedColumns.push(decoratedColumn)
|
||||
return decoratedColumn
|
||||
}
|
||||
let allDecoratedColumns = []
|
||||
const decoratedColumns = columnsWithExpander.map((column, i) => {
|
||||
if (column.columns) {
|
||||
return {
|
||||
...column,
|
||||
columns: column.columns.map(decorateAndAddToAll)
|
||||
}
|
||||
} else {
|
||||
return decorateAndAddToAll(column)
|
||||
}
|
||||
})
|
||||
|
||||
// Build the visible columns, headers and flat column list
|
||||
let visibleColumns = decoratedColumns.slice()
|
||||
let allVisibleColumns = []
|
||||
|
||||
visibleColumns = visibleColumns.map((column, i) => {
|
||||
if (column.columns) {
|
||||
const visibleSubColumns = column.columns.filter(d => pivotBy.indexOf(d.id) > -1 ? false : _.getFirstDefined(d.show, true))
|
||||
return {
|
||||
...column,
|
||||
columns: visibleSubColumns
|
||||
}
|
||||
}
|
||||
return column
|
||||
})
|
||||
|
||||
visibleColumns = visibleColumns.filter(column => {
|
||||
return column.columns ? column.columns.length : pivotBy.indexOf(column.id) > -1 ? false : _.getFirstDefined(column.show, true)
|
||||
})
|
||||
|
||||
// Move the pivot columns into a single column if needed
|
||||
if (pivotBy.length) {
|
||||
const pivotColumns = []
|
||||
for (var i = 0; i < allDecoratedColumns.length; i++) {
|
||||
if (pivotBy.indexOf(allDecoratedColumns[i].id) > -1) {
|
||||
pivotColumns.push(allDecoratedColumns[i])
|
||||
}
|
||||
}
|
||||
const pivotColumn = {
|
||||
...pivotColumns[0],
|
||||
pivotColumns,
|
||||
expander: true
|
||||
}
|
||||
visibleColumns[expanderColumnIndex] = pivotColumn
|
||||
}
|
||||
|
||||
// Build flast list of allVisibleColumns and HeaderGroups
|
||||
visibleColumns.forEach((column, i) => {
|
||||
if (column.columns) {
|
||||
allVisibleColumns = allVisibleColumns.concat(column.columns)
|
||||
if (currentSpan.length > 0) {
|
||||
addHeader(currentSpan)
|
||||
}
|
||||
addHeader(column.columns, column)
|
||||
return
|
||||
}
|
||||
allVisibleColumns.push(column)
|
||||
currentSpan.push(column)
|
||||
})
|
||||
if (hasHeaderGroups && currentSpan.length > 0) {
|
||||
addHeader(currentSpan)
|
||||
}
|
||||
|
||||
// Access the data
|
||||
let resolvedData = data.map((d, i) => {
|
||||
const row = {
|
||||
__original: d,
|
||||
__index: i
|
||||
}
|
||||
allDecoratedColumns.forEach(column => {
|
||||
if (column.expander) return
|
||||
row[column.id] = column.accessor(d)
|
||||
})
|
||||
return row
|
||||
})
|
||||
|
||||
// If pivoting, recursively group the data
|
||||
const aggregate = (rows) => {
|
||||
const aggregationValues = {}
|
||||
aggregatingColumns.forEach(column => {
|
||||
const values = rows.map(d => d[column.id])
|
||||
aggregationValues[column.id] = column.aggregate(values, rows)
|
||||
})
|
||||
return aggregationValues
|
||||
}
|
||||
let standardColumns = pivotBy.length ? allVisibleColumns.slice(1) : allVisibleColumns
|
||||
const aggregatingColumns = standardColumns.filter(d => d.aggregate)
|
||||
let pivotColumn
|
||||
if (pivotBy.length) {
|
||||
pivotColumn = allVisibleColumns[0]
|
||||
const groupRecursively = (rows, keys, i = 0) => {
|
||||
// This is the last level, just return the rows
|
||||
if (i === keys.length) {
|
||||
return rows
|
||||
}
|
||||
// Group the rows together for this level
|
||||
let groupedRows = Object.entries(
|
||||
_.groupBy(rows, keys[i]))
|
||||
.map(([key, value]) => {
|
||||
return {
|
||||
[pivotIDKey]: keys[i],
|
||||
[pivotValKey]: key,
|
||||
[keys[i]]: key,
|
||||
[subRowsKey]: value
|
||||
}
|
||||
})
|
||||
// Recurse into the subRows
|
||||
groupedRows = groupedRows.map(rowGroup => {
|
||||
let subRows = groupRecursively(rowGroup[subRowsKey], keys, i + 1)
|
||||
return {
|
||||
...rowGroup,
|
||||
[subRowsKey]: subRows,
|
||||
...aggregate(subRows)
|
||||
}
|
||||
})
|
||||
return groupedRows
|
||||
}
|
||||
resolvedData = groupRecursively(resolvedData, pivotBy)
|
||||
}
|
||||
|
||||
return {
|
||||
...newState,
|
||||
resolvedData,
|
||||
pivotColumn,
|
||||
allVisibleColumns,
|
||||
headerGroups,
|
||||
allDecoratedColumns,
|
||||
hasHeaderGroups
|
||||
}
|
||||
},
|
||||
getSortedData (resolvedState) {
|
||||
const {
|
||||
manual,
|
||||
sorting,
|
||||
filtering,
|
||||
showFilters,
|
||||
defaultFilterMethod,
|
||||
resolvedData,
|
||||
allVisibleColumns
|
||||
} = resolvedState
|
||||
|
||||
// Resolve the data from either manual data or sorted data
|
||||
return {
|
||||
sortedData: manual ? resolvedData : this.sortData(this.filterData(resolvedData, showFilters, filtering, defaultFilterMethod, allVisibleColumns), sorting)
|
||||
}
|
||||
},
|
||||
fireOnChange () {
|
||||
this.props.onChange(this.getResolvedState(), this)
|
||||
},
|
||||
getPropOrState (key) {
|
||||
return _.getFirstDefined(this.props[key], this.state[key])
|
||||
},
|
||||
getStateOrProp (key) {
|
||||
return _.getFirstDefined(this.state[key], this.props[key])
|
||||
},
|
||||
filterData (data, showFilters, filtering, defaultFilterMethod, allVisibleColumns) {
|
||||
let filteredData = data
|
||||
|
||||
if (showFilters && filtering.length) {
|
||||
filteredData = filtering.reduce(
|
||||
(filteredSoFar, nextFilter) => {
|
||||
return filteredSoFar.filter(
|
||||
(row) => {
|
||||
let column
|
||||
|
||||
if (nextFilter.pivotId) {
|
||||
const parentColumn = allVisibleColumns.find(x => x.id === nextFilter.id)
|
||||
column = parentColumn.pivotColumns.find(x => x.id === nextFilter.pivotId)
|
||||
} else {
|
||||
column = allVisibleColumns.find(x => x.id === nextFilter.id)
|
||||
}
|
||||
|
||||
const filterMethod = column.filterMethod || defaultFilterMethod
|
||||
|
||||
return filterMethod(nextFilter, row, column)
|
||||
})
|
||||
}
|
||||
, filteredData
|
||||
)
|
||||
|
||||
// Apply the filter to the subrows if we are pivoting, and then
|
||||
// filter any rows without subcolumns because it would be strange to show
|
||||
filteredData = filteredData.map(row => {
|
||||
if (!row[this.props.subRowsKey]) {
|
||||
return row
|
||||
}
|
||||
return {
|
||||
...row,
|
||||
[this.props.subRowsKey]: this.filterData(row[this.props.subRowsKey], showFilters, filtering, defaultFilterMethod, allVisibleColumns)
|
||||
}
|
||||
}).filter(row => {
|
||||
if (!row[this.props.subRowsKey]) {
|
||||
return true
|
||||
}
|
||||
return row[this.props.subRowsKey].length > 0
|
||||
})
|
||||
}
|
||||
|
||||
return filteredData
|
||||
},
|
||||
sortData (data, sorting) {
|
||||
if (!sorting.length) {
|
||||
return data
|
||||
}
|
||||
|
||||
const sorted = _.orderBy(data, sorting.map(sort => {
|
||||
return row => {
|
||||
if (row[sort.id] === null || row[sort.id] === undefined) {
|
||||
return -Infinity
|
||||
}
|
||||
return typeof row[sort.id] === 'string' ? row[sort.id].toLowerCase() : row[sort.id]
|
||||
}
|
||||
}), sorting.map(d => !d.desc))
|
||||
|
||||
return sorted.map(row => {
|
||||
if (!row[this.props.subRowsKey]) {
|
||||
return row
|
||||
}
|
||||
return {
|
||||
...row,
|
||||
[this.props.subRowsKey]: this.sortData(row[this.props.subRowsKey], sorting)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getMinRows () {
|
||||
return _.getFirstDefined(this.props.minRows, this.getStateOrProp('pageSize'))
|
||||
},
|
||||
|
||||
// User actions
|
||||
onPageChange (page) {
|
||||
const {onPageChange, collapseOnPageChange} = this.props
|
||||
if (onPageChange) {
|
||||
return onPageChange(page)
|
||||
}
|
||||
const newState = {page}
|
||||
if (collapseOnPageChange) {
|
||||
newState.expandedRows = {}
|
||||
}
|
||||
this.setStateWithData(
|
||||
newState
|
||||
, () => {
|
||||
this.fireOnChange()
|
||||
})
|
||||
},
|
||||
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 / newPageSize)
|
||||
|
||||
if (onPageSizeChange) {
|
||||
return onPageSizeChange(newPageSize, newPage)
|
||||
}
|
||||
|
||||
this.setStateWithData({
|
||||
pageSize: newPageSize,
|
||||
page: newPage
|
||||
}, () => {
|
||||
this.fireOnChange()
|
||||
})
|
||||
},
|
||||
sortColumn (column, additive) {
|
||||
const {sorting, skipNextSort} = this.getResolvedState()
|
||||
|
||||
// we can't stop event propagation from the column resize move handlers
|
||||
// attached to the document because of react's synthetic events
|
||||
// so we have to prevent the sort function from actually sorting
|
||||
// if we click on the column resize element within a header.
|
||||
if (skipNextSort) {
|
||||
this.setStateWithData({
|
||||
skipNextSort: false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const {onSortingChange} = this.props
|
||||
if (onSortingChange) {
|
||||
return onSortingChange(column, additive)
|
||||
}
|
||||
let newSorting = _.clone(sorting || []).map(d => {
|
||||
d.desc = _.isSortingDesc(d)
|
||||
return d
|
||||
})
|
||||
if (!_.isArray(column)) {
|
||||
// Single-Sort
|
||||
const existingIndex = newSorting.findIndex(d => d.id === column.id)
|
||||
if (existingIndex > -1) {
|
||||
const existing = newSorting[existingIndex]
|
||||
if (existing.desc) {
|
||||
if (additive) {
|
||||
newSorting.splice(existingIndex, 1)
|
||||
} else {
|
||||
existing.desc = false
|
||||
newSorting = [existing]
|
||||
}
|
||||
} else {
|
||||
existing.desc = true
|
||||
if (!additive) {
|
||||
newSorting = [existing]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (additive) {
|
||||
newSorting.push({
|
||||
id: column.id,
|
||||
desc: false
|
||||
})
|
||||
} else {
|
||||
newSorting = [{
|
||||
id: column.id,
|
||||
desc: false
|
||||
}]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Multi-Sort
|
||||
const existingIndex = newSorting.findIndex(d => d.id === column[0].id)
|
||||
// Existing Sorted Column
|
||||
if (existingIndex > -1) {
|
||||
const existing = newSorting[existingIndex]
|
||||
if (existing.desc) {
|
||||
if (additive) {
|
||||
newSorting.splice(existingIndex, column.length)
|
||||
} else {
|
||||
column.forEach((d, i) => {
|
||||
newSorting[existingIndex + i].desc = false
|
||||
})
|
||||
}
|
||||
} else {
|
||||
column.forEach((d, i) => {
|
||||
newSorting[existingIndex + i].desc = true
|
||||
})
|
||||
}
|
||||
if (!additive) {
|
||||
newSorting = newSorting.slice(existingIndex, column.length)
|
||||
}
|
||||
} else {
|
||||
// New Sort Column
|
||||
if (additive) {
|
||||
newSorting = newSorting.concat(column.map(d => ({
|
||||
id: d.id,
|
||||
desc: false
|
||||
})))
|
||||
} else {
|
||||
newSorting = column.map(d => ({
|
||||
id: d.id,
|
||||
desc: false
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setStateWithData({
|
||||
page: ((!sorting.length && newSorting.length) || !additive) ? 0 : this.state.page,
|
||||
sorting: newSorting
|
||||
}, () => {
|
||||
this.fireOnChange()
|
||||
})
|
||||
},
|
||||
filterColumn (column, value, pivotColumn) {
|
||||
const {filtering} = this.getResolvedState()
|
||||
const {onFilteringChange} = this.props
|
||||
|
||||
if (onFilteringChange) {
|
||||
return onFilteringChange(column, value, pivotColumn)
|
||||
}
|
||||
|
||||
// Remove old filter first if it exists
|
||||
const newFiltering = (filtering || []).filter(x => {
|
||||
if (x.id !== column.id) {
|
||||
return true
|
||||
}
|
||||
if (x.pivotId) {
|
||||
if (pivotColumn) {
|
||||
return x.pivotId !== pivotColumn.id
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
if (value !== '') {
|
||||
newFiltering.push({
|
||||
id: column.id,
|
||||
value: value,
|
||||
pivotId: pivotColumn ? pivotColumn.id : undefined
|
||||
})
|
||||
}
|
||||
|
||||
this.setStateWithData({
|
||||
filtering: newFiltering
|
||||
}, () => {
|
||||
this.fireOnChange()
|
||||
})
|
||||
},
|
||||
resizeColumnStart (column, event, isTouch) {
|
||||
const {onResize} = this.props
|
||||
|
||||
if (onResize) {
|
||||
return onResize(column, event, isTouch)
|
||||
}
|
||||
|
||||
const parentWidth = event.target.parentElement.getBoundingClientRect().width
|
||||
|
||||
let pageX
|
||||
if (isTouch) {
|
||||
pageX = event.changedTouches[0].pageX
|
||||
} else {
|
||||
pageX = event.pageX
|
||||
}
|
||||
|
||||
this.setStateWithData({
|
||||
currentlyResizing: {
|
||||
id: column.id,
|
||||
startX: pageX,
|
||||
parentWidth: parentWidth
|
||||
}
|
||||
}, () => {
|
||||
if (isTouch) {
|
||||
document.addEventListener('touchmove', this.resizeColumnMoving)
|
||||
document.addEventListener('touchcancel', this.resizeColumnEnd)
|
||||
document.addEventListener('touchend', this.resizeColumnEnd)
|
||||
} else {
|
||||
document.addEventListener('mousemove', this.resizeColumnMoving)
|
||||
document.addEventListener('mouseup', this.resizeColumnEnd)
|
||||
document.addEventListener('mouseleave', this.resizeColumnEnd)
|
||||
}
|
||||
})
|
||||
},
|
||||
resizeColumnEnd (event) {
|
||||
let isTouch = event.type === 'touchend' || event.type === 'touchcancel'
|
||||
|
||||
if (isTouch) {
|
||||
document.removeEventListener('touchmove', this.resizeColumnMoving)
|
||||
document.removeEventListener('touchcancel', this.resizeColumnEnd)
|
||||
document.removeEventListener('touchend', this.resizeColumnEnd)
|
||||
}
|
||||
|
||||
// If its a touch event clear the mouse one's as well because sometimes
|
||||
// the mouseDown event gets called as well, but the mouseUp event doesn't
|
||||
document.removeEventListener('mousemove', this.resizeColumnMoving)
|
||||
document.removeEventListener('mouseup', this.resizeColumnEnd)
|
||||
document.removeEventListener('mouseleave', this.resizeColumnEnd)
|
||||
|
||||
// The touch events don't propagate up to the sorting's onMouseDown event so
|
||||
// no need to prevent it from happening or else the first click after a touch
|
||||
// event resize will not sort the column.
|
||||
if (!isTouch) {
|
||||
this.setStateWithData({
|
||||
skipNextSort: true
|
||||
})
|
||||
}
|
||||
},
|
||||
resizeColumnMoving (event) {
|
||||
const {resizing, currentlyResizing} = this.getResolvedState()
|
||||
|
||||
// Delete old value
|
||||
const newResizing = resizing.filter(x => x.id !== currentlyResizing.id)
|
||||
|
||||
let pageX
|
||||
|
||||
if (event.type === 'touchmove') {
|
||||
pageX = event.changedTouches[0].pageX
|
||||
} else if (event.type === 'mousemove') {
|
||||
pageX = event.pageX
|
||||
}
|
||||
|
||||
// Set the min size to 10 to account for margin and border or else the group headers don't line up correctly
|
||||
const newWidth = Math.max(currentlyResizing.parentWidth + pageX - currentlyResizing.startX, 11)
|
||||
|
||||
newResizing.push({
|
||||
id: currentlyResizing.id,
|
||||
value: newWidth
|
||||
})
|
||||
|
||||
this.setStateWithData({
|
||||
resizing: newResizing
|
||||
})
|
||||
}
|
||||
}
|
||||
+14
-6
@@ -4,7 +4,7 @@ import classnames from 'classnames'
|
||||
// import _ from './utils'
|
||||
|
||||
const defaultButton = (props) => (
|
||||
<button {...props} className='-btn'>{props.children}</button>
|
||||
<button type='button' {...props} className='-btn'>{props.children}</button>
|
||||
)
|
||||
|
||||
export default React.createClass({
|
||||
@@ -17,12 +17,17 @@ export default React.createClass({
|
||||
this.setState({page: nextProps.page})
|
||||
},
|
||||
getSafePage (page) {
|
||||
if (isNaN(page)) {
|
||||
page = this.props.page
|
||||
}
|
||||
return Math.min(Math.max(page, 0), this.props.pages - 1)
|
||||
},
|
||||
changePage (page) {
|
||||
page = this.getSafePage(page)
|
||||
this.setState({page})
|
||||
this.props.onPageChange(page)
|
||||
if (this.props.page !== page) {
|
||||
this.props.onPageChange(page)
|
||||
}
|
||||
},
|
||||
applyPage (e) {
|
||||
e && e.preventDefault()
|
||||
@@ -66,9 +71,7 @@ export default React.createClass({
|
||||
<div className='-center'>
|
||||
<span className='-pageInfo'>
|
||||
{this.props.pageText} {showPageJump ? (
|
||||
<form className='-pageJump'
|
||||
onSubmit={this.applyPage}
|
||||
>
|
||||
<div className='-pageJump'>
|
||||
<input
|
||||
type={this.state.page === '' ? 'text' : 'number'}
|
||||
onChange={e => {
|
||||
@@ -81,8 +84,13 @@ export default React.createClass({
|
||||
}}
|
||||
value={this.state.page === '' ? '' : this.state.page + 1}
|
||||
onBlur={this.applyPage}
|
||||
onKeyPress={e => {
|
||||
if (e.which === 13 || e.keyCode === 13) {
|
||||
this.applyPage()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
) : (
|
||||
<span className='-currentPage'>{page + 1}</span>
|
||||
)} {this.props.ofText} <span className='-totalPages'>{pages}</span>
|
||||
|
||||
+43
-10
@@ -6,16 +6,19 @@ export default {
|
||||
set,
|
||||
takeRight,
|
||||
last,
|
||||
sortBy,
|
||||
orderBy,
|
||||
range,
|
||||
remove,
|
||||
clone,
|
||||
getFirstDefined,
|
||||
sum,
|
||||
makeTemplateComponent,
|
||||
prefixAll,
|
||||
groupBy,
|
||||
isArray
|
||||
isArray,
|
||||
splitProps,
|
||||
compactObject,
|
||||
isSortingDesc,
|
||||
normalizeComponent
|
||||
}
|
||||
|
||||
function get (obj, path, def) {
|
||||
@@ -61,7 +64,7 @@ function range (n) {
|
||||
return arr
|
||||
}
|
||||
|
||||
function sortBy (arr, funcs, dirs) {
|
||||
function orderBy (arr, funcs, dirs) {
|
||||
return arr.sort((a, b) => {
|
||||
for (let i = 0; i < funcs.length; i++) {
|
||||
const comp = funcs[i]
|
||||
@@ -75,7 +78,9 @@ function sortBy (arr, funcs, dirs) {
|
||||
return desc ? 1 : -1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
return dirs[0]
|
||||
? a.__index - b.__index
|
||||
: b.__index - a.__index
|
||||
})
|
||||
}
|
||||
|
||||
@@ -128,14 +133,10 @@ function makeTemplateComponent (compClass) {
|
||||
)
|
||||
}
|
||||
|
||||
function prefixAll (obj) {
|
||||
return obj
|
||||
}
|
||||
|
||||
function groupBy (xs, key) {
|
||||
return xs.reduce((rv, x, i) => {
|
||||
const resKey = typeof key === 'function' ? key(x, i) : x[key]
|
||||
rv[resKey] = rv[resKey] || []
|
||||
rv[resKey] = isArray(rv[resKey]) ? rv[resKey] : []
|
||||
rv[resKey].push(x)
|
||||
return rv
|
||||
}, {})
|
||||
@@ -167,3 +168,35 @@ function flattenDeep (arr, newArr = []) {
|
||||
}
|
||||
return newArr
|
||||
}
|
||||
|
||||
function splitProps ({className, style, ...rest}) {
|
||||
return {
|
||||
className,
|
||||
style,
|
||||
rest
|
||||
}
|
||||
}
|
||||
|
||||
function compactObject (obj) {
|
||||
const newObj = {}
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key) && obj[key] !== undefined && typeof obj[key] !== 'undefined') {
|
||||
newObj[key] = obj[key]
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
||||
function isSortingDesc (d) {
|
||||
return !!(d.sort === 'desc' || d.desc === true || d.asc === false)
|
||||
}
|
||||
|
||||
function normalizeComponent (Comp, params = {}, fallback = Comp) {
|
||||
return typeof Comp === 'function' ? (
|
||||
Object.getPrototypeOf(Comp).isReactComponent ? (
|
||||
<Comp
|
||||
{...params}
|
||||
/>
|
||||
) : Comp(params)
|
||||
) : fallback
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
const statusChance = Math.random()
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numLen: 0 }),
|
||||
lastName: namor.generate({ words: 1, numLen: 0 }),
|
||||
progress: Math.floor(Math.random() * 100),
|
||||
status: statusChance > 0.66 ? 'relationship'
|
||||
: statusChance > 0.33 ? 'complicated'
|
||||
: 'single'
|
||||
}
|
||||
})
|
||||
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName'
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Profile Progress',
|
||||
accessor: 'progress',
|
||||
render: row => (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#dadada',
|
||||
borderRadius: '2px'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: `${row.value}%`,
|
||||
height: '100%',
|
||||
backgroundColor: row.value > 66 ? '#85cc00'
|
||||
: row.value > 33 ? '#ffbf00'
|
||||
: '#ff2e00',
|
||||
borderRadius: '2px',
|
||||
transition: 'all .2s ease-out'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}, {
|
||||
header: 'Status',
|
||||
accessor: 'status',
|
||||
render: row => (
|
||||
<span>
|
||||
<span style={{
|
||||
color: row.value === 'relationship' ? '#ff2e00'
|
||||
: row.value === 'complicated' ? '#ffbf00'
|
||||
: '#57d500',
|
||||
transition: 'all .3s ease'
|
||||
}}>
|
||||
●
|
||||
</span> {
|
||||
row.value === 'relationship' ? 'In a relationship'
|
||||
: row.value === 'complicated' ? `It's complicated`
|
||||
: 'Single'
|
||||
}
|
||||
</span>
|
||||
)
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={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'
|
||||
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName'
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Profile Progress',
|
||||
accessor: 'progress',
|
||||
render: row => (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#dadada',
|
||||
borderRadius: '2px'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: \`$\{row.value}%\`,
|
||||
height: '100%',
|
||||
backgroundColor: row.value > 66 ? '#85cc00'
|
||||
: row.value > 33 ? '#ffbf00'
|
||||
: '#ff2e00',
|
||||
borderRadius: '2px',
|
||||
transition: 'all .2s ease-out'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}, {
|
||||
header: 'Status',
|
||||
accessor: 'status',
|
||||
render: row => (
|
||||
<span>
|
||||
<span style={{
|
||||
color: row.value === 'relationship' ? '#ff2e00'
|
||||
: row.value === 'complicated' ? '#ffbf00'
|
||||
: '#57d500',
|
||||
transition: 'all .3s ease'
|
||||
}}>
|
||||
●
|
||||
</span> {
|
||||
row.value === 'relationship' ? 'In a relationship'
|
||||
: row.value === 'complicated' ? \`It's complicated\`
|
||||
: 'Single'
|
||||
}
|
||||
</span>
|
||||
)
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
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'
|
||||
}]
|
||||
}]
|
||||
|
||||
class ControlledTable extends React.Component {
|
||||
constructor() {
|
||||
super()
|
||||
this.sortChange = this.sortChange.bind(this)
|
||||
this.state = {
|
||||
sorting: [],
|
||||
page: 0,
|
||||
pageSize: 10
|
||||
}
|
||||
}
|
||||
|
||||
sortChange(column, shift) {
|
||||
if(shift)
|
||||
alert('Shift click not implemented in this demo')
|
||||
var sort = {id: column.id}
|
||||
if(this.state.sorting.length && this.state.sorting[0].id == column.id)
|
||||
this.state.sorting[0].asc ? sort.desc = true : sort.asc = true
|
||||
else
|
||||
sort.asc = true
|
||||
this.setState({
|
||||
sorting: [sort]
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
sorting={this.state.sorting}
|
||||
onSortingChange={this.sortChange}
|
||||
page={this.state.page}
|
||||
onPageChange={page => this.setState({page})}
|
||||
pageSize={this.state.pageSize}
|
||||
onPageSizeChange={(pageSize, page) => this.setState({page, pageSize})}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: For simplicity, multi-sort is not implemented in this demo</em>
|
||||
</div>
|
||||
<CodeHighlight>{() => getCode()}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function getCode () {
|
||||
return `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'
|
||||
}]
|
||||
}]
|
||||
|
||||
class ControlledTable extends React.Component {
|
||||
constructor() {
|
||||
super()
|
||||
this.sortChange = this.sortChange.bind(this)
|
||||
this.state = {
|
||||
sorting: [],
|
||||
page: 0,
|
||||
pageSize: 10
|
||||
}
|
||||
}
|
||||
|
||||
sortChange(column, shift) {
|
||||
if(shift)
|
||||
alert('Shift click not implemented in this demo')
|
||||
var sort = {id: column.id}
|
||||
if(this.state.sorting.length && this.state.sorting[0].id == column.id)
|
||||
this.state.sorting[0].asc ? sort.desc = true : sort.asc = true
|
||||
else
|
||||
sort.asc = true
|
||||
this.setState({
|
||||
sorting: [sort]
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
sorting={this.state.sorting}
|
||||
onSortingChange={this.sortChange}
|
||||
page={this.state.page}
|
||||
onPageChange={page => this.setState({page})}
|
||||
pageSize={this.state.pageSize}
|
||||
onPageSizeChange={(pageSize, page) => this.setState({page, pageSize})}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
export default () => <ControlledTable />
|
||||
@@ -0,0 +1,106 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
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>
|
||||
<strong>Hey!</strong> Open your console! :)
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
getTdProps={(state, rowInfo, column, instance) => {
|
||||
return {
|
||||
onMouseEnter: e => console.log('Cell - onMouseEnter', {
|
||||
state,
|
||||
rowInfo,
|
||||
column,
|
||||
instance,
|
||||
event: e
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
<CodeHighlight>{() => getCode()}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function getCode () {
|
||||
return `
|
||||
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
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
getTdProps={(state, rowInfo, column, instance) => {
|
||||
return {
|
||||
onMouseEnter: e => console.log('Cell - onMouseEnter', {
|
||||
state,
|
||||
rowInfo,
|
||||
column,
|
||||
instance,
|
||||
event: e
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
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',
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? '...' : row.value}</span>
|
||||
}
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
aggregate: vals => _.round(_.mean(vals)),
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? `${row.value} (avg)` : row.value}</span>
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
expander: true
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
SubComponent={() => <span>Hello</span>}
|
||||
/>
|
||||
</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'
|
||||
|
||||
// Create some column definitions
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? '...' : row.value}</span>
|
||||
}
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
aggregate: vals => _.round(_.mean(vals)),
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? \`$\{row.value} (avg)\` : row.value}</span>
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
expander: true
|
||||
}]
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
SubComponent={() => <span>Hello</span>}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
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',
|
||||
maxWidth: 200
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
width: 300
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
minWidth: 400
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={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'
|
||||
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
maxWidth: 200
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
width: 300
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
minWidth: 400
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
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
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
defaultSorting={[{
|
||||
id: 'age',
|
||||
desc: true
|
||||
}]}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
<CodeHighlight>{() => getCode()}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function getCode () {
|
||||
return `
|
||||
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
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
defaultSorting={[{
|
||||
id: 'age',
|
||||
desc: true
|
||||
}]}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
class Filtering extends React.Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({words: 1, numLen: 0}),
|
||||
lastName: namor.generate({words: 1, numLen: 0}),
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
|
||||
this.state = {
|
||||
tableOptions: {
|
||||
loading: false,
|
||||
showPagination: true,
|
||||
showPageSizeOptions: true,
|
||||
showPageJump: true,
|
||||
collapseOnSortingChange: true,
|
||||
collapseOnPageChange: true,
|
||||
collapseOnDataChange: true,
|
||||
freezeWhenExpanded: false,
|
||||
showFilters: true
|
||||
},
|
||||
data: data
|
||||
}
|
||||
|
||||
this.setTableOption = this.setTableOption.bind(this)
|
||||
}
|
||||
|
||||
render () {
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
filterMethod: (filter, row) => (row[filter.id].startsWith(filter.value) && row[filter.id].endsWith(filter.value))
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
filterMethod: (filter, row) => (row[filter.id].includes(filter.value))
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
}, {
|
||||
header: 'Over 21',
|
||||
accessor: 'age',
|
||||
id: 'over',
|
||||
render: ({value}) => (value >= 21 ? 'Yes' : 'No'),
|
||||
filterMethod: (filter, row) => {
|
||||
if (filter.value === 'all') {
|
||||
return true
|
||||
}
|
||||
if (filter.value === 'true') {
|
||||
return row[filter.id] >= 21
|
||||
}
|
||||
return row[filter.id] < 21
|
||||
},
|
||||
filterRender: ({filter, onFilterChange}) => (
|
||||
<select
|
||||
onChange={event => onFilterChange(event.target.value)}
|
||||
style={{width: '100%'}}
|
||||
value={filter ? filter.value : 'all'}>
|
||||
<option value="all"></option>
|
||||
<option value="true">Can Drink</option>
|
||||
<option value="false">Can't Drink</option>
|
||||
</select>
|
||||
)
|
||||
}
|
||||
]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{float: 'left'}}>
|
||||
<h1>Table Options</h1>
|
||||
<table>
|
||||
<tbody>
|
||||
{
|
||||
Object.keys(this.state.tableOptions).map(optionKey => {
|
||||
const optionValue = this.state.tableOptions[optionKey]
|
||||
return (
|
||||
<tr key={optionKey}>
|
||||
<td>{optionKey}</td>
|
||||
<td style={{paddingLeft: 10, paddingTop: 5}}>
|
||||
<input type="checkbox"
|
||||
name={optionKey}
|
||||
checked={optionValue}
|
||||
onChange={this.setTableOption}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className='table-wrap' style={{paddingLeft: 240}}>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={this.state.data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
defaultFilterMethod={(filter, row) => (String(row[filter.id]) === filter.value)}
|
||||
{...this.state.tableOptions}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>You can put any component you want here, even another React Table!</em>
|
||||
<br />
|
||||
<br />
|
||||
<ReactTable
|
||||
data={this.state.data}
|
||||
columns={columns}
|
||||
defaultPageSize={3}
|
||||
showPagination={false}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>It even has access to the row data: </em>
|
||||
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Custom Filters In This Example</h1>
|
||||
<p>The default filter for all columns of a table if it is not specified in the configuration is set to match
|
||||
on values that start with the filter text. Example: age.startsWith("2").</p>
|
||||
<p>This example overrides the default filter behavior by setting
|
||||
the <strong>defaultFilterMethod</strong> table option to match on values that are exactly equal to the
|
||||
filter text. Example: age == "23")</p>
|
||||
<p>Each column can also be customized with the column <strong>filterMethod</strong> option:</p>
|
||||
<p>In this example the firstName column filters on the value starting with and ending with the filter
|
||||
value.</p>
|
||||
<p>In this example the lastName column filters on the value including the filter value anywhere in its
|
||||
text.</p>
|
||||
<p>To completely override the filter that is shown, you can set the <strong>filterRender</strong> column
|
||||
option. Using this option you can specify the JSX that is shown. The option is passed
|
||||
an <strong>onFilterChange</strong> method that must be called with the value that you wan't to
|
||||
pass to the <strong>filterMethod</strong> option whenever the filter has changed.</p>
|
||||
</div>
|
||||
<CodeHighlight>{() => this.getCode()}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
setTableOption (event) {
|
||||
const target = event.target
|
||||
const value = target.type === 'checkbox' ? target.checked : target.value
|
||||
const name = target.name
|
||||
this.setState({
|
||||
tableOptions: {
|
||||
...this.state.tableOptions,
|
||||
[name]: value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getCode () {
|
||||
return `
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
filterMethod: (filter, row) => (row[filter.id].startsWith(filter.value) && row[filter.id].endsWith(filter.value))
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
filterMethod: (filter, row) => (row[filter.id].includes(filter.value))
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
}, {
|
||||
header: 'Over 21',
|
||||
accessor: 'age',
|
||||
id: 'over',
|
||||
render: ({value}) => (value >= 21 ? 'Yes' : 'No'),
|
||||
filterMethod: (filter, row) => {
|
||||
if (filter.value === 'all') {
|
||||
return true
|
||||
}
|
||||
if (filter.value === 'true') {
|
||||
return row[filter.id] >= 21
|
||||
}
|
||||
return row[filter.id] < 21
|
||||
},
|
||||
filterRender: ({filter, onFilterChange}) => (
|
||||
<select
|
||||
onChange={event => onFilterChange(event.target.value)}
|
||||
style={{width: '100%'}}
|
||||
value={filter ? filter.value : 'all'}>
|
||||
<option value="all"></option>
|
||||
<option value="true">Can Drink</option>
|
||||
<option value="false">Can't Drink</option>
|
||||
</select>
|
||||
)
|
||||
}]
|
||||
}]
|
||||
|
||||
export default (
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
defaultFilterMethod={(filter, row) => (String(row[filter.id]) === filter.value)}
|
||||
{...otherOptions}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>You can put any component you want here, even another React Table!</em>
|
||||
<br />
|
||||
<br />
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={3}
|
||||
showPagination={false}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>It even has access to the row data: </em>
|
||||
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
export default () => <Filtering />
|
||||
@@ -0,0 +1,143 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
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',
|
||||
footer: (
|
||||
<span><strong>Popular:</strong> {
|
||||
_.first(
|
||||
_.reduce(
|
||||
_.map(
|
||||
_.groupBy(
|
||||
data, d => d.firstName
|
||||
)
|
||||
),
|
||||
(a, b) => a.length > b.length ? a : b
|
||||
)
|
||||
).firstName}
|
||||
</span>
|
||||
)
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
footer: (
|
||||
<span><strong>Longest:</strong> {
|
||||
_.reduce(
|
||||
_.map(
|
||||
_.groupBy(
|
||||
data, d => d.lastName
|
||||
),
|
||||
(d, key) => key
|
||||
),
|
||||
(a, b) => a.length > b.length ? a : b
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
footer: <span><strong>Average:</strong> {_.round(_.mean(_.map(data, d => d.age)))}</span>
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={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'
|
||||
|
||||
// Create some column definitions
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
footer: (
|
||||
<span><strong>Popular:</strong> {
|
||||
_.first(
|
||||
_.reduce(
|
||||
_.map(
|
||||
_.groupBy(
|
||||
data, d => d.firstName
|
||||
)
|
||||
),
|
||||
(a, b) => a.length > b.length ? a : b
|
||||
)
|
||||
).firstName}
|
||||
</span>
|
||||
)
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
footer: (
|
||||
<span><strong>Longest:</strong> {
|
||||
_.reduce(
|
||||
_.map(
|
||||
_.groupBy(
|
||||
data, d => d.lastName
|
||||
),
|
||||
(d, key) => key
|
||||
),
|
||||
(a, b) => a.length > b.length ? a : b
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
footer: <span><strong>Average:</strong> {_.round(_.mean(_.map(data, d => d.age)))}</span>
|
||||
}]
|
||||
}]
|
||||
|
||||
// Display your table!
|
||||
return (
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
import JSONTree from 'react-json-tree'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
const JSONtheme = {
|
||||
scheme: 'monokai',
|
||||
author: 'wimer hazenberg (http://www.monokai.nl)',
|
||||
base00: '#272822',
|
||||
base01: '#383830',
|
||||
base02: '#49483e',
|
||||
base03: '#75715e',
|
||||
base04: '#a59f85',
|
||||
base05: '#f8f8f2',
|
||||
base06: '#f5f4f1',
|
||||
base07: '#f9f8f5',
|
||||
base08: '#f92672',
|
||||
base09: '#fd971f',
|
||||
base0A: '#f4bf75',
|
||||
base0B: '#a6e22e',
|
||||
base0C: '#a1efe4',
|
||||
base0D: '#66d9ef',
|
||||
base0E: '#ae81ff',
|
||||
base0F: '#cc6633'
|
||||
}
|
||||
|
||||
export default () => {
|
||||
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',
|
||||
footer: 'Footer'
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
footer: 'Footer'
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
footer: 'Footer'
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<strong>Functional rendering</strong> simply means that you have all of the building blocks to render your own React Table however you'd like.
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<strong>Decorating the standard table output</strong>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
>
|
||||
{(state, makeTable, instance) => {
|
||||
return (
|
||||
<div style={{
|
||||
background: '#ffcf00',
|
||||
borderRadius: '5px',
|
||||
overflow: 'hidden',
|
||||
padding: '5px'
|
||||
}}>
|
||||
<pre><code>state.allVisibleColumns === {JSON.stringify(state.allVisibleColumns, null, 4)}</code></pre>
|
||||
{makeTable()}
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</ReactTable>
|
||||
</div>
|
||||
|
||||
<CodeHighlight>{() => `
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
>
|
||||
{(state, Table, instance) => {
|
||||
return (
|
||||
<div style={{
|
||||
background: '#ffcf00',
|
||||
borderRadius: '5px',
|
||||
overflow: 'hidden',
|
||||
padding: '5px'
|
||||
}}>
|
||||
<pre><code>state.allVisibleColumns === {JSON.stringify(state.allVisibleColumns, null, 4)}</code></pre>
|
||||
<Table />
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</ReactTable>
|
||||
)
|
||||
`}</CodeHighlight>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<strong>Need more control? This is the entire table state and component instance at your disposal!</strong>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
>
|
||||
{(state, StandardTable, instance) => {
|
||||
return (
|
||||
<div>
|
||||
<JSONTree
|
||||
data={Object.assign({}, state, {children: 'function () {...}'})}
|
||||
theme={JSONtheme}
|
||||
invertTheme
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</ReactTable>
|
||||
</div>
|
||||
<br />
|
||||
<CodeHighlight>{() => `
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
>
|
||||
{(state, StandardTable, instance) => {
|
||||
return (
|
||||
<div>
|
||||
<JSONTree
|
||||
data={Object.assign({}, state, {children: 'function () {...}'})}
|
||||
theme={JSONtheme}
|
||||
invertTheme
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</ReactTable>
|
||||
)
|
||||
`}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
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
|
||||
className='-striped -highlight'
|
||||
data={[]}
|
||||
noDataText='Oh Noes!'
|
||||
// noDataText={() => 'Oh Noes!'} // Supports functions
|
||||
// noDataText={() => <span>Oh Noes!</span>} // Supports JSX / React Components
|
||||
columns={columns}
|
||||
defaultPageSize={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'
|
||||
|
||||
// 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={[]}
|
||||
noDataText='Oh Noes!'
|
||||
// noDataText={() => 'Oh Noes!'} // Supports functions
|
||||
// noDataText={() => <span>Oh Noes!</span>} // Supports JSX / React Components
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -3,10 +3,10 @@ import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../lib/index'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
const data = _.map(_.range(1000000), d => {
|
||||
const data = _.map(_.range(100000), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numLen: 0 }),
|
||||
lastName: namor.generate({ words: 1, numLen: 0 }),
|
||||
@@ -47,6 +47,7 @@ export default () => {
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
className='-striped -highlight'
|
||||
defaultPageSize={10}
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
SubComponent={(row) => {
|
||||
@@ -99,15 +100,24 @@ const columns = [{
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
accessor: 'age',
|
||||
aggregate: vals => _.round(_.mean(vals)),
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? \`\${row.value} (avg)\` : row.value}</span>
|
||||
}
|
||||
}, {
|
||||
header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals)
|
||||
}]
|
||||
}]
|
||||
|
||||
export default (
|
||||
return (
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
+14
-32
@@ -3,7 +3,7 @@ import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../lib/index'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
const data = _.map(_.range(10000), d => {
|
||||
@@ -47,14 +47,9 @@ export default () => {
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
className='-striped -highlight'
|
||||
defaultPageSize={10}
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
// expandedRows={{
|
||||
// 2: true,
|
||||
// 3: {
|
||||
// 2: true
|
||||
// }
|
||||
// }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
@@ -82,38 +77,25 @@ const columns = [{
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
accessor: 'age',
|
||||
aggregate: vals => _.round(_.mean(vals)),
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? \`\${row.value} (avg)\` : row.value}</span>
|
||||
}
|
||||
}, {
|
||||
header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals)
|
||||
}]
|
||||
}]
|
||||
|
||||
export default (
|
||||
return (
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
className='-striped -highlight'
|
||||
defaultPageSize={10}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>You can put any component you want here, even another React Table!</em>
|
||||
<br />
|
||||
<br />
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={3}
|
||||
showPagination={false}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>It even has access to the row data: </em>
|
||||
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
/>
|
||||
)
|
||||
`
|
||||
|
||||
@@ -3,7 +3,7 @@ import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../lib/index'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
const data = _.map(_.range(1000), d => {
|
||||
@@ -33,11 +33,13 @@ export default () => {
|
||||
aggregate: vals => _.round(_.mean(vals)),
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? `${row.value} (avg)` : row.value}</span>
|
||||
}
|
||||
},
|
||||
filterMethod: (filter, row) => (filter.value == `${row[filter.id]} (avg)`)
|
||||
}, {
|
||||
header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals)
|
||||
aggregate: vals => _.sum(vals),
|
||||
hideFilter: true
|
||||
}]
|
||||
}]
|
||||
|
||||
@@ -48,7 +50,9 @@ export default () => {
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
showFilters={true}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
@@ -99,16 +103,28 @@ const columns = [{
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
accessor: 'age',
|
||||
aggregate: vals => _.round(_.mean(vals)),
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? \`\${row.value} (avg)\` : row.value}</span>
|
||||
},
|
||||
filterMethod: (filter, row) => (filter.value == \`\${row[filter.id]} (avg)\`)
|
||||
}, {
|
||||
header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals),
|
||||
hideFilter: true
|
||||
}]
|
||||
}]
|
||||
|
||||
export default (
|
||||
return (
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
showFilters={true}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
|
||||
+44
-43
@@ -3,7 +3,7 @@ import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../lib/index'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
const rawData = _.map(_.range(3424), d => {
|
||||
return {
|
||||
@@ -14,23 +14,34 @@ const rawData = _.map(_.range(3424), d => {
|
||||
})
|
||||
|
||||
// Now let's mock the server. It's job is simple: use the table model to sort and return the page data
|
||||
const requestData = (pageSize, page, sorting) => {
|
||||
const requestData = (pageSize, page, sorting, filtering) => {
|
||||
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 => {
|
||||
let filteredData = rawData;
|
||||
if (filtering.length) {
|
||||
filteredData = filtering.reduce(
|
||||
(filteredSoFar, nextFilter) => {
|
||||
return filteredSoFar.filter(
|
||||
(row) => {
|
||||
return (row[nextFilter.id]+"").includes(nextFilter.value)
|
||||
})
|
||||
}
|
||||
, filteredData)
|
||||
}
|
||||
const sortedData = _.orderBy(filteredData, sorting.map(sort => {
|
||||
return row => {
|
||||
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'))
|
||||
}), sorting.map(d => d.desc ? 'desc' : 'asc'))
|
||||
|
||||
// Be sure to send back the rows to be displayed and any other pertinent information, like how many pages there are total.
|
||||
const res = {
|
||||
rows: sortedData.slice(pageSize * page, (pageSize * page) + pageSize),
|
||||
pages: Math.ceil(rawData.length / pageSize)
|
||||
pages: Math.ceil(filteredData.length / pageSize)
|
||||
}
|
||||
|
||||
// Here we'll simulate a server response with 500ms of delay.
|
||||
@@ -48,13 +59,13 @@ const ServerSide = React.createClass({
|
||||
}
|
||||
},
|
||||
fetchData (state, instance) {
|
||||
console.log(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)
|
||||
requestData(state.pageSize, state.page, state.sorting, state.filtering)
|
||||
.then((res) => {
|
||||
console.log(res.rows)
|
||||
// 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,
|
||||
@@ -68,25 +79,21 @@ const ServerSide = React.createClass({
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
columns={[{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName'
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
}]
|
||||
header: 'First Name',
|
||||
accessor: 'firstName'
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
}]
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
}, {
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
}]}
|
||||
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
|
||||
defaultPageSize={10}
|
||||
showFilters={true}
|
||||
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
|
||||
@@ -129,7 +136,8 @@ export default React.createClass({
|
||||
Axios.post('mysite.com/data', {
|
||||
pageSize: state.pageSize,
|
||||
page: state.page,
|
||||
sorting: state.sorting
|
||||
sorting: state.sorting,
|
||||
filtering: state.filtering
|
||||
})
|
||||
.then((res) => {
|
||||
// Now update your state!
|
||||
@@ -141,34 +149,27 @@ export default React.createClass({
|
||||
})
|
||||
},
|
||||
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
|
||||
columns={[{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName'
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
}, {
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
}]}
|
||||
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
|
||||
defaultPageSize={10}
|
||||
showFilters={true}
|
||||
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
|
||||
/>
|
||||
)
|
||||
}
|
||||
})
|
||||
`
|
||||
|
||||
+3
-1
@@ -3,7 +3,7 @@ import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../lib/index'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
@@ -36,6 +36,7 @@ export default () => {
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
@@ -76,6 +77,7 @@ const columns = [{
|
||||
// Display your table!
|
||||
return (
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
|
||||
+131
-64
@@ -3,78 +3,141 @@ import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../lib/index'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
export default () => {
|
||||
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)
|
||||
class SubComponents extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({words: 1, numLen: 0}),
|
||||
lastName: namor.generate({words: 1, numLen: 0}),
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
|
||||
this.state = {
|
||||
tableOptions: {
|
||||
loading: false,
|
||||
showPagination: true,
|
||||
showPageSizeOptions: true,
|
||||
showPageJump: true,
|
||||
collapseOnSortingChange: true,
|
||||
collapseOnPageChange: true,
|
||||
collapseOnDataChange: true,
|
||||
freezeWhenExpanded: false,
|
||||
showFilters: false
|
||||
},
|
||||
data: data
|
||||
}
|
||||
})
|
||||
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName'
|
||||
this.setTableOption = this.setTableOption.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName'
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
}]
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>You can put any component you want here, even another React Table!</em>
|
||||
<br />
|
||||
<br />
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={3}
|
||||
showPagination={false}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>It even has access to the row data: </em>
|
||||
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<div style={{float: "left"}}>
|
||||
<h1>Table Options</h1>
|
||||
<table>
|
||||
<tbody>
|
||||
{
|
||||
Object.keys(this.state.tableOptions).map(optionKey => {
|
||||
const optionValue = this.state.tableOptions[optionKey];
|
||||
return (
|
||||
<tr key={optionKey}>
|
||||
<td>{optionKey}</td>
|
||||
<td style={{paddingLeft: 10, paddingTop: 5}}>
|
||||
<input type="checkbox"
|
||||
name={optionKey}
|
||||
checked={optionValue}
|
||||
onChange={this.setTableOption}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className='table-wrap' style={{paddingLeft: 240}}>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={this.state.data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
{...this.state.tableOptions}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>You can put any component you want here, even another React Table!</em>
|
||||
<br />
|
||||
<br />
|
||||
<ReactTable
|
||||
data={this.state.data}
|
||||
columns={columns}
|
||||
defaultPageSize={3}
|
||||
showPagination={false}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
<em>It even has access to the row data: </em>
|
||||
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
<CodeHighlight>{() => this.getCode()}</CodeHighlight>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
<CodeHighlight>{() => getCode()}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function getCode () {
|
||||
return `
|
||||
setTableOption(event) {
|
||||
const target = event.target;
|
||||
const value = target.type === 'checkbox' ? target.checked : target.value;
|
||||
const name = target.name;
|
||||
this.setState({
|
||||
tableOptions: {
|
||||
...this.state.tableOptions,
|
||||
[name]: value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getCode() {
|
||||
return `
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
columns: [{
|
||||
@@ -98,6 +161,7 @@ export default (
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
{...otherOptions}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
<div style={{padding: '20px'}}>
|
||||
@@ -124,4 +188,7 @@ export default (
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
export default () => <SubComponents/>
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react'
|
||||
import banner from '../../media/Banner.png'
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<a href='https://github.com/tannerlinsley/react-table' target='_blank'>
|
||||
<img src={banner} className='logo' />
|
||||
</a>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
Reference in New Issue
Block a user