mirror of
https://github.com/gosticks/react-table.git
synced 2026-08-19 08:20:30 +00:00
Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdb3e77b00 | ||
|
|
d9248cef2b | ||
|
|
f4704b146c | ||
|
|
004186af24 | ||
|
|
0d0e239baa | ||
|
|
f61b19cb8a | ||
|
|
e1a46fb77e | ||
|
|
a9db5b1ac6 | ||
|
|
846fc0d84d | ||
|
|
87610c83fa | ||
|
|
a58f5c4de2 | ||
|
|
1995bba652 | ||
|
|
680747d1d0 | ||
|
|
e70b29066b | ||
|
|
9d82cee707 | ||
|
|
db1edf119d | ||
|
|
c5fa476b6d | ||
|
|
550a1f7107 | ||
|
|
42e1a12ea9 | ||
|
|
15945d28a1 | ||
|
|
eaa7215674 | ||
|
|
d2835b9e14 | ||
|
|
5691fa2e7a | ||
|
|
f4aba6a008 | ||
|
|
7bce5a8958 | ||
|
|
875d3d93d6 | ||
|
|
a0ede1593b | ||
|
|
0d43b6d601 | ||
|
|
9dbd485cad | ||
|
|
ee5fae8c6f | ||
|
|
95164311fa | ||
|
|
49c5a3d5e1 | ||
|
|
f27ec1fb34 | ||
|
|
dd26802535 | ||
|
|
97e560cb0f | ||
|
|
2ba0bf73ad |
@@ -0,0 +1,74 @@
|
||||
module.exports = {
|
||||
parserOptions: {
|
||||
ecmaVersion: 8,
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true,
|
||||
jsx: true,
|
||||
node: false
|
||||
},
|
||||
sourceType: 'module'
|
||||
},
|
||||
|
||||
extends: ['standard'],
|
||||
|
||||
plugins: ['react'],
|
||||
|
||||
rules: {
|
||||
// Nozzle
|
||||
'jsx-quotes': [2, 'prefer-single'],
|
||||
'comma-dangle': [2, 'always-multiline'],
|
||||
|
||||
// // React
|
||||
'react/jsx-boolean-value': 2,
|
||||
'react/jsx-curly-spacing': [2, 'never'],
|
||||
'react/jsx-equals-spacing': [2, 'never'],
|
||||
// 'react/jsx-indent': 2,
|
||||
'react/jsx-indent-props': [2, 2],
|
||||
'react/jsx-no-duplicate-props': 2,
|
||||
'react/jsx-no-undef': 2,
|
||||
'react/jsx-tag-spacing': [
|
||||
2,
|
||||
{
|
||||
closingSlash: 'never',
|
||||
beforeSelfClosing: 'always',
|
||||
afterOpening: 'never'
|
||||
}
|
||||
],
|
||||
'react/jsx-uses-react': 2,
|
||||
'react/jsx-uses-vars': 2,
|
||||
'react/self-closing-comp': 2,
|
||||
'react/jsx-no-bind': [
|
||||
2,
|
||||
{
|
||||
allowArrowFunctions: true,
|
||||
allowBind: false,
|
||||
ignoreRefs: true
|
||||
}
|
||||
],
|
||||
'react/no-did-update-set-state': 2,
|
||||
'react/no-unknown-property': 2,
|
||||
'react/react-in-jsx-scope': 2,
|
||||
'react/jsx-closing-bracket-location': [2, 'tag-aligned'],
|
||||
'react/jsx-tag-spacing': [2, { beforeSelfClosing: 'always' }],
|
||||
'react/jsx-wrap-multilines': 2,
|
||||
'react/self-closing-comp': 2,
|
||||
'react/jsx-key': 2,
|
||||
'react/jsx-no-comment-textnodes': 2,
|
||||
'react/jsx-no-duplicate-props': 2,
|
||||
'react/jsx-no-target-blank': 2,
|
||||
'react/jsx-no-undef': 2,
|
||||
'react/jsx-uses-react': 2,
|
||||
'react/jsx-uses-vars': 2,
|
||||
'react/no-danger-with-children': 2,
|
||||
'react/no-deprecated': 2,
|
||||
'react/no-direct-mutation-state': 2,
|
||||
'react/no-find-dom-node': 2,
|
||||
'react/no-is-mounted': 2,
|
||||
'react/no-render-return-value': 2,
|
||||
'react/no-string-refs': 2,
|
||||
'react/no-unknown-property': 2,
|
||||
'react/react-in-jsx-scope': 2,
|
||||
'react/require-render-return': 2
|
||||
// 'react/jsx-max-props-per-line': [2, { maximum: 1 }]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
node_modules/
|
||||
lib/
|
||||
docs/build
|
||||
react-table.js
|
||||
react-table.css
|
||||
*.log
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
react-table.js.org
|
||||
@@ -1,64 +0,0 @@
|
||||
import React from 'react'
|
||||
import { configure, storiesOf } from '@kadira/storybook'
|
||||
|
||||
import './reset.css'
|
||||
import './fonts.css'
|
||||
import './layout.css'
|
||||
import '../stories/utils/prism.css'
|
||||
import 'github-markdown-css/github-markdown.css'
|
||||
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 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'
|
||||
import PivotingOptions from '../stories/PivotingOptions.js'
|
||||
import EditableTable from '../stories/EditableTable.js'
|
||||
//
|
||||
configure(() => {
|
||||
storiesOf('1. Docs')
|
||||
.add('Readme', () => {
|
||||
const ReadmeCmp = React.createClass({
|
||||
render () {
|
||||
return <span className='markdown-body' dangerouslySetInnerHTML={{__html: Readme}} />
|
||||
},
|
||||
componentDidMount () {
|
||||
global.Prism.highlightAll()
|
||||
}
|
||||
})
|
||||
return <ReadmeCmp />
|
||||
})
|
||||
storiesOf('2. Demos')
|
||||
.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('100k Rows w/ Pivoting & Sub Components', OneHundredKRows)
|
||||
.add('Pivoting Options', PivotingOptions)
|
||||
.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)
|
||||
.add('Editable table', EditableTable)
|
||||
}, module)
|
||||
@@ -1,18 +0,0 @@
|
||||
@import url("https://fonts.googleapis.com/css?family=Open+Sans:300,600");
|
||||
body {
|
||||
background: #fff;
|
||||
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,7 +0,0 @@
|
||||
body{
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: bold
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
/*ol, ul {
|
||||
list-style: none;
|
||||
}*/
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
const path = require('path')
|
||||
|
||||
// load the default config generator.
|
||||
const genDefaultConfig = require('@kadira/storybook/dist/server/config/defaults/webpack.config.js')
|
||||
|
||||
module.exports = (config, env) => {
|
||||
config = genDefaultConfig(config, env)
|
||||
|
||||
Object.assign(config, {
|
||||
module: Object.assign(config.module, {
|
||||
loaders: config.module.loaders.concat([{
|
||||
test: /\.md$/,
|
||||
loader: 'html!markdown',
|
||||
include: path.resolve(__dirname, '../')
|
||||
}, {
|
||||
test: /\.html$/,
|
||||
loader: 'html',
|
||||
query: {
|
||||
minimize: true
|
||||
},
|
||||
include: path.resolve(__dirname, '../')
|
||||
}])
|
||||
})
|
||||
})
|
||||
|
||||
return config
|
||||
}
|
||||
+45
-7
@@ -1,11 +1,49 @@
|
||||
## 6.3.0
|
||||
##### New Features
|
||||
- `defaultSortDesc` - allows you to set the default sorting direction for all columns to descending.
|
||||
- `column.defaultSortDesc` - allows you to set the default sorting direction for a specific column. Falls back to the global `defaultSortDesc` when not set at all.
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* Pivot Columns can now be sorted and filtered individually.
|
||||
* More control over Pivot Column rendering.
|
||||
* Prevent transitions while column resizing for a smoother resize effect.
|
||||
* Disable text selection while resizing columns.
|
||||
##### New Features
|
||||
- New Renderers:
|
||||
- `Aggregated` - Custom renderer for aggregated cells
|
||||
- `Pivot` - Custom renderer for Pivoted Cells (utilizes `Expander` and `PivotValue`)
|
||||
- `PivotValue` - Custom renderer for Pivot cell values (deprecates the undocumented `pivotRender` option)
|
||||
- `Expander` - Custom renderer for Pivot cell Expander
|
||||
- Added custom sorting methods per table via `defaultSortMethod` and per column via `column.sortMethod`
|
||||
- Pivot columns are now visibly separate and sorted/filtered independently.
|
||||
- Added `column.resizable` to override global table `resizable` option for specific columns.
|
||||
- Added `column.sortable` to override global table `sortable` option for specific columns.
|
||||
- Added `column.filterable` to override global table `filterable` option for specific columns.
|
||||
- Added `defaultExpanded` table option.
|
||||
- All callbacks can now be utilized without needing to hoist and manage the piece of state they export. That is what their prop counterparts are for, so now the corresponding prop is used instead of the callback to detect a "fully controlled" state.
|
||||
- Prevent transitions while column resizing for a smoother resize effect.
|
||||
- Disable text selection while resizing columns.
|
||||
|
||||
|
||||
##### Breaking API Changes
|
||||
* Removed `pivotRender` column option. You can now control how the value is displayed by overriding the `PivotValueComponent`.
|
||||
* Changed how special `expander: true` column renders pivot columns.
|
||||
See [Pivoting Options Story](https://react-table.js.org/?selectedKind=2.%20Demos&selectedStory=Pivoting%20Options&full=0&down=1&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel) for a reference on how to customize pivot column rendering.
|
||||
- New Renderers:
|
||||
- `Cell` - deprecates and replaces `render`
|
||||
- `Header` - deprecates and replaces `header`
|
||||
- `Footer` - deprecates and replaces `footer`
|
||||
- `Filter`- deprecates and replaces `filterRender`
|
||||
- Callbacks now provide the destination state as the primary parameter(s). This makes hoisting and controlling the state in redux or component state much easier. eg.
|
||||
- `onSorting` no longer requires you to build your own toggle logic
|
||||
- `onResize` no longer requires you to build your own resize logic
|
||||
- Renamed `onChange` callback -> `onFetchData` which will always fire when a new data model needs to be fetched (or if not using `manual`, when new data is materialized internally).
|
||||
- Renamed `filtering` -> `filtered`
|
||||
- Renamed `sorting` -> `sorted`
|
||||
- Renamed `expandedRows` -> `expanded`
|
||||
- Renamed `resizing` -> `resized`
|
||||
- Renamed `defaultResizing` -> `defaultResized`
|
||||
- Renamed `defaultFiltering` -> `defaultFiltered`
|
||||
- Renamed `defaultSorting` -> `defaultSorted`
|
||||
- Renamed `onSortingChange` -> `onSortedChange`
|
||||
- Renamed `onFilteringChange` -> `onFilteredChange`
|
||||
- Renamed `onResize` -> `onResizedChange`
|
||||
- Renamed `onExpandRow` -> `onExpandedChange`
|
||||
- Renamed `showFilters` -> `filterable`
|
||||
- Renamed `hideFilter` -> `filterable` (Column option. Note the true/false value is now flipped.)
|
||||
- `cellInfo.row` and `rowInfo.row` now reference the materialize data for the table. To reference the original row, use `cellInfo.original` and `rowInfo.original`
|
||||
- Removed `pivotRender` column option. You can now control how the value is displayed by overriding the `PivotValueComponent` or the individual column's `PivotValue` renderer. See [Pivoting Options Story](https://react-table.js.org/?selectedKind=2.%20Demos&selectedStory=Pivoting%20Options&full=0&down=1&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel) for a reference on how to customize pivot column rendering.
|
||||
|
||||
@@ -28,8 +28,14 @@
|
||||
<img alt="" src="https://img.shields.io/badge/%24-Donate-brightgreen.svg" />
|
||||
</a>
|
||||
|
||||
## Features
|
||||
## Versions
|
||||
This document refers to version 6.x.x of react-table.
|
||||
|
||||
Previous versions:
|
||||
|
||||
[5.x.x Readme](https://github.com/tannerlinsley/react-table/blob/ad7d31cd3978eb45da7c6194dbab93c1e9a8594d/README.md)
|
||||
|
||||
## Features
|
||||
- Lightweight at 7kb (and just 2kb more for styles)
|
||||
- Fully customizable JSX templating
|
||||
- Supports both Client-side & Server-side pagination and multi-sorting
|
||||
@@ -40,7 +46,7 @@
|
||||
|
||||
## Demos and examples
|
||||
- <a href="http://codepen.io/tannerlinsley/pen/QpeZBa?editors=0010" target="\_blank">Codepen</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">Storybook</a>
|
||||
- <a href="http://react-table.js.org/#/story/simple-table" target="\_parent">Storybook</a>
|
||||
|
||||
## Table of Contents
|
||||
- [Installation](#installation)
|
||||
@@ -115,18 +121,18 @@ render() {
|
||||
}]
|
||||
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
Header: 'Name',
|
||||
accessor: 'name' // String-based value accessors!
|
||||
}, {
|
||||
header: 'Age',
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
render: props => <span className='number'>{props.value}</span> // Custom cell components!
|
||||
Cell: props => <span className='number'>{props.value}</span> // Custom cell components!
|
||||
}, {
|
||||
id: 'friendName', // Required because our accessor is not a string
|
||||
header: 'Friend Name',
|
||||
Header: 'Friend Name',
|
||||
accessor: d => d.friend.name // Custom value accessors!
|
||||
}, {
|
||||
header: props => <span>Friend Age</span>, // Custom header components!
|
||||
Header: props => <span>Friend Age</span>, // Custom header components!
|
||||
accessor: 'friend.age'
|
||||
}]
|
||||
|
||||
@@ -149,51 +155,78 @@ These are all of the available props (and their default values) for the main `<R
|
||||
data: [],
|
||||
loading: false,
|
||||
showPagination: true,
|
||||
showPaginationTop: false,
|
||||
showPaginationBottom: true
|
||||
showPageSizeOptions: true,
|
||||
pageSizeOptions: [5, 10, 20, 25, 50, 100],
|
||||
defaultPageSize: 20,
|
||||
minRows: undefined,
|
||||
showPageJump: true,
|
||||
collapseOnSortingChange: true,
|
||||
collapseOnPageChange: true,
|
||||
collapseOnDataChange: true,
|
||||
freezeWhenExpanded: false,
|
||||
defaultSorting: [],
|
||||
showFilters: false,
|
||||
defaultFiltering: [],
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
filterable: false,
|
||||
defaultSortDesc: false,
|
||||
defaultSorted: [],
|
||||
defaultFiltered: [],
|
||||
defaultResized: [],
|
||||
defaultExpanded: {},
|
||||
defaultFilterMethod: (filter, row, column) => {
|
||||
const id = filter.pivotId || filter.id
|
||||
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
|
||||
},
|
||||
resizable: true,
|
||||
defaultResizing: [],
|
||||
defaultSortMethod: (a, b) => {
|
||||
// force null and undefined to the bottom
|
||||
a = (a === null || a === undefined) ? -Infinity : a
|
||||
b = (b === null || b === undefined) ? -Infinity : b
|
||||
// force any string values to lowercase
|
||||
a = a === 'string' ? a.toLowerCase() : a
|
||||
b = b === 'string' ? b.toLowerCase() : b
|
||||
// Return either 1 or -1 to indicate a sort priority
|
||||
if (a > b) {
|
||||
return 1
|
||||
}
|
||||
if (a < b) {
|
||||
return -1
|
||||
}
|
||||
// returning 0, undefined or any falsey value will use subsequent sorts or the index as a tiebreaker
|
||||
return 0
|
||||
},
|
||||
|
||||
// Controlled State Overrides (see Fully Controlled Component section)
|
||||
page: undefined,
|
||||
pageSize: undefined,
|
||||
sorting: undefined,
|
||||
sorted: [],
|
||||
filtered: [],
|
||||
resized: [],
|
||||
expanded: {},
|
||||
|
||||
// Controlled State Callbacks
|
||||
onExpandSubComponent: undefined,
|
||||
onPageChange: undefined,
|
||||
onPageSizeChange: undefined,
|
||||
onSortingChange: undefined,
|
||||
onFilteringChange: undefined,
|
||||
onResize: undefined,
|
||||
onSortedChange: undefined,
|
||||
onFilteredChange: undefined,
|
||||
onResizedChange: undefined,
|
||||
onExpandedChange: undefined,
|
||||
|
||||
// Pivoting
|
||||
pivotBy: undefined,
|
||||
|
||||
// Key Constants
|
||||
pivotValKey: '_pivotVal',
|
||||
pivotIDKey: '_pivotID',
|
||||
subRowsKey: '_subRows',
|
||||
aggregatedKey: '_aggregated',
|
||||
nestingLevelKey: '_nestingLevel',
|
||||
originalKey: '_original',
|
||||
indexKey: '_index',
|
||||
groupedByPivotKey: '_groupedByPivot',
|
||||
|
||||
// Pivoting State Overrides (see Fully Controlled Component section)
|
||||
expandedRows: {},
|
||||
|
||||
// Pivoting State Callbacks
|
||||
onExpandRow: undefined,
|
||||
|
||||
// General Callbacks
|
||||
onChange: () => null,
|
||||
// Server-side callbacks
|
||||
onFetchData: () => null,
|
||||
|
||||
// Classes
|
||||
className: '',
|
||||
@@ -226,50 +259,48 @@ These are all of the available props (and their default values) for the main `<R
|
||||
|
||||
// Global Column Defaults
|
||||
column: {
|
||||
sortable: true,
|
||||
// Renderers
|
||||
Cell: undefined,
|
||||
Header: undefined,
|
||||
Footer: undefined,
|
||||
Aggregated: undefined,
|
||||
Pivot: undefined,
|
||||
PivotValue: undefined,
|
||||
Expander: undefined,
|
||||
Filter: undefined,
|
||||
// Standard options
|
||||
sortable: undefined, // use table default
|
||||
resizable: undefined, // use table default
|
||||
filterable: undefined, // use table default
|
||||
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)}
|
||||
/>
|
||||
)
|
||||
sortMethod: undefined,
|
||||
defaultSortDesc: undefined,
|
||||
},
|
||||
|
||||
|
||||
// Global Expander Column Defaults
|
||||
expanderDefaults: {
|
||||
sortable: false,
|
||||
width: 35,
|
||||
hideFilter: true
|
||||
// render: will be overriden in methods.js to display ExpanderComponent
|
||||
resizable: false,
|
||||
filterable: false,
|
||||
width: 35
|
||||
},
|
||||
|
||||
// Global Pivot Column Defaults
|
||||
pivotDefaults: {
|
||||
filterRender: undefined
|
||||
// render: will be overriden in methods.js to display ExpanderComponent and PivotValueComponent
|
||||
},
|
||||
pivotDefaults: {},
|
||||
|
||||
// Text
|
||||
previousText: 'Previous',
|
||||
@@ -309,37 +340,49 @@ Or just define them as props
|
||||
|
||||
```javascript
|
||||
[{
|
||||
// Renderers - For more information, see "Renderers" section below
|
||||
Cell: JSX | String | Function // Used to render a standard cell, defaults to the accessed value
|
||||
Header: JSX | String | Function // Used to render the header of a column or column group
|
||||
Footer: JSX | String | Function // Used to render the footer of a column
|
||||
Filter: JSX | cellInfo => ( // Used to render the filter UI of a filter-enabled column
|
||||
<select onChange={event => onFiltersChange(event.target.value)} value={filter ? filter.value : ''}></select>
|
||||
// The value passed to onFiltersChange will be the value passed to filter.value of the filterMethod
|
||||
)
|
||||
Aggregated: JSX | String | Function // Used to render aggregated cells. Defaults to a comma separated list of values.
|
||||
Pivot: JSX | String | Function | cellInfo => ( // Used to render a pivoted cell
|
||||
<span>
|
||||
<Expander/><PivotValue /> // By default, will utilize the the PivotValue and Expander components at run time
|
||||
</span>
|
||||
),
|
||||
PivotValue: JSX | String | Function // Used to render the value inside of a Pivot cell
|
||||
Expander: JSX | String | Function // Used to render the expander in both Pivot and Expander cells
|
||||
|
||||
// General
|
||||
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,
|
||||
sortable: boolean, // Overrides the table option
|
||||
resizable: boolean, // Overrides the table option
|
||||
filterable: boolean, // Overrides the table option
|
||||
show: true, // can be used to hide a column
|
||||
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
|
||||
// Turns this column into a special column for specifying expander and pivot column options.
|
||||
// If this option is true and there is NOT a pivot column, the `expanderDefaults` options will be applied on top of the column options.
|
||||
// If this option is true and there IS a pivot column, the `pivotDefaults` options will be applied on top of the column options.
|
||||
// Adding a column with the `expander` option set will allow you to rearrange expander and pivot column orderings in the table.
|
||||
pivot: false,
|
||||
// Turns this column into a special column for specifying pivot position in your column definitions.
|
||||
// The `pivotDefaults` options will be applied on top of this column's options.
|
||||
// It will also let you specify rendering of the header (and header group if this special column is placed in the `columns` option of another column)
|
||||
expander: false,
|
||||
// Turns this column into a special column for specifying expander position and options in your column definitions.
|
||||
// The `expanderDefaults` options will be applied on top of this column's options.
|
||||
// It will also let you specify rendering of the header (and header group if this special column is placed in the `columns` option of another column) and
|
||||
// the rendering of the expander itself.
|
||||
expander: false,
|
||||
// the rendering of the expander itself via the `Expander` property
|
||||
|
||||
// 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. (cellInfo: {value, rowValues, row, column, 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
|
||||
// column == the column properties (id, header, sortable, ...)
|
||||
// index == the original index of the data supplied to the table
|
||||
// viewIndex == the index of the row in the current page
|
||||
|
||||
// Header & HeaderGroup Options
|
||||
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
|
||||
@@ -348,7 +391,6 @@ Or just define them as props
|
||||
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
|
||||
@@ -358,11 +400,54 @@ Or just define them as props
|
||||
// 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
|
||||
}]
|
||||
```
|
||||
|
||||
## Renderers
|
||||
React Table supports very flexible renderers for just about everything:
|
||||
- `Cell` - Renders a standard cell
|
||||
- `Header` - Renders a column header or column group header
|
||||
- `Footer` - Renders a column footer
|
||||
- `Filter` - Renders a column's filter UI
|
||||
- `Aggregated` - Renders an aggregated cell
|
||||
- `Pivot` - Renders a pivoted cell (by default, will utilize `Expander` and `PivotValue` renderers)
|
||||
- `PivotValue` - Renders the value inside a `Pivot` renderer
|
||||
- `Expander` - Renders the Expander used in both the default `Pivot` renderer and any expander-designated column
|
||||
|
||||
Any of these renderers can be one of the following:
|
||||
- A React Class
|
||||
- JSX or any rendered react component
|
||||
- Stateless functional component
|
||||
- Function that returns any primitive
|
||||
|
||||
All of these formats receive the following props:
|
||||
```javascript
|
||||
{
|
||||
// Row-level props
|
||||
row: Object, // the materialized row of data
|
||||
original: , // the original row of data
|
||||
index: '', // the index of the row in the original arra
|
||||
viewIndex: '', // the index of the row relative to the current view
|
||||
level: '', // the nesting level of this row
|
||||
nestingPath: '', // the nesting path of this row
|
||||
aggregated: '', // true if this row's values were aggregated
|
||||
groupedByPivot: '', // true if this row was produced by a pivot
|
||||
subRows: '', // any sub rows defined by the `subRowKey` prop
|
||||
|
||||
// Cells-level props
|
||||
isExpanded: '', // true if this row is expanded
|
||||
value: '', // the materialized value of this cell
|
||||
resized: '', // the resize information for this cell's column
|
||||
show: '', // true if the column is visible
|
||||
width: '', // the resolved width of this cell
|
||||
maxWidth: '', // the resolved maxWidth of this cell
|
||||
tdProps: '', // the resolved tdProps from `getTdProps` for this cell
|
||||
columnProps: '', // the resolved column props from 'getProps' for this cell's column
|
||||
classes: '', // the resolved array of classes for this cell
|
||||
styles: '' // the resolved styles for this cell
|
||||
}
|
||||
```
|
||||
|
||||
## 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.
|
||||
@@ -390,16 +475,16 @@ If your data has a field/key with a dot (`.`) you will need to supply a custom a
|
||||
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',
|
||||
Header: 'Favorites',
|
||||
headerClassName: 'my-favorites-column-header-group'
|
||||
columns: [{
|
||||
header: 'Color',
|
||||
Header: 'Color',
|
||||
accessor: 'favorites.color'
|
||||
}, {
|
||||
header: 'Food',
|
||||
Header: 'Food',
|
||||
accessor: 'favorites.food'
|
||||
} {
|
||||
header: 'Actor',
|
||||
Header: 'Actor',
|
||||
accessor: 'favorites.actor'
|
||||
}]
|
||||
}]
|
||||
@@ -408,7 +493,7 @@ const columns = [{
|
||||
## 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
|
||||
- `row` - 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)
|
||||
@@ -420,9 +505,9 @@ You can use any react component or JSX to display content in column headers, cel
|
||||
// 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>,
|
||||
Header: () => <span><i className='fa-tasks' /> Progress</span>,
|
||||
accessor: 'progress',
|
||||
render: row => (
|
||||
Cell: row => (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
@@ -565,15 +650,15 @@ Naturally when grouping rows together, you may want to aggregate the rows inside
|
||||
```javascript
|
||||
// In this example, we use lodash to sum and average the values, but you can use whatever you want to aggregate.
|
||||
const columns = [{
|
||||
header: 'Age',
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
aggregate: (values, rows) => _.round(_.mean(values)),
|
||||
render: row => {
|
||||
Aggregated: row => {
|
||||
// You can even render the cell differently if it's an aggregated cell
|
||||
return <span>{row.aggregated ? `${row.value} (avg)` : row.value}</span>
|
||||
return <span>row.value (avg)</span>
|
||||
}
|
||||
}, {
|
||||
header: 'Visits',
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: (values, rows) => _.sum(values)
|
||||
}]
|
||||
@@ -604,8 +689,8 @@ If you want to handle pagination, sorting, and filtering on the server, `react-t
|
||||
|
||||
1. Feed React Table `data` from somewhere dynamic. eg. `state`, a redux store, etc...
|
||||
1. Add `manual` as a prop. This informs React Table that you'll be handling sorting and pagination server-side
|
||||
1. Subscribe to the `onChange` prop. This function is called at `compomentDidMount` and any time sorting or pagination is changed by the user
|
||||
1. In the `onChange` callback, request your data using the provided information in the params of the function (state and instance)
|
||||
1. Subscribe to the `onFetchData` prop. This function is called at `compomentDidMount` and any time sorting, pagination or filterting is changed in the table
|
||||
1. In the `onFetchData` callback, request your data using the provided information in the params of the function (current state and instance)
|
||||
1. Update your data with the rows to be displayed
|
||||
1. Optionally set how many pages there are total
|
||||
|
||||
@@ -616,15 +701,15 @@ If you want to handle pagination, sorting, and filtering on the server, `react-t
|
||||
pages={this.state.pages} // should default to -1 (which means we don't know how many pages we have)
|
||||
loading={this.state.loading}
|
||||
manual // informs React Table that you'll be handling sorting and pagination server-side
|
||||
onChange={(state, instance) => {
|
||||
onFetchData={(state, instance) => {
|
||||
// show the loading overlay
|
||||
this.setState({loading: true})
|
||||
// fetch your data
|
||||
Axios.post('mysite.com/data', {
|
||||
page: state.page,
|
||||
pageSize: state.pageSize,
|
||||
sorting: state.sorting,
|
||||
filtering: state.filtering
|
||||
sorted: state.sorted,
|
||||
filtered: state.filtered
|
||||
})
|
||||
.then((res) => {
|
||||
// Update react-table
|
||||
@@ -649,29 +734,37 @@ Here are the props and their corresponding callbacks that control the state of t
|
||||
// Props
|
||||
page={0} // the index of the page you wish to display
|
||||
pageSize={20} // the number of rows per page to be displayed
|
||||
sorting={[{
|
||||
sorted={[{ // the sorting model for the table
|
||||
id: 'lastName',
|
||||
desc: true
|
||||
}, {
|
||||
id: 'firstName',
|
||||
desc: true
|
||||
}]} // the sorting model for the table
|
||||
expandedRows={{
|
||||
}]}
|
||||
expandedRows={{ // The nested row indexes on the current page that should appear expanded
|
||||
1: true,
|
||||
4: true,
|
||||
5: {
|
||||
2: true,
|
||||
3: true
|
||||
}
|
||||
}} // The nested row indexes on the current page that should appear expanded
|
||||
}}
|
||||
filters={[{ // the current filters model
|
||||
id: 'lastName',
|
||||
value: 'linsley'
|
||||
}]}
|
||||
resizing={[{ // the current resized column model
|
||||
"id": "lastName",
|
||||
"value": 446.25
|
||||
}]}
|
||||
|
||||
// Callbacks
|
||||
onPageChange={(pageIndex) => {...}} // Called when the page index is changed by the user
|
||||
onPageSizeChange={(pageSize, pageIndex) => {...}} // Called when the pageSize is changed by the user. The resolve page is also sent to maintain approximate position in the data
|
||||
onSortingChange={(column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. 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)
|
||||
onSortedChange={(newSorted, 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
|
||||
onExpandedChange={(newExpanded, index, event) => {...}} // Called when an expander is clicked. Use this to manage `expandedRows`
|
||||
onFilteredChange={(column, value) => {...}} // Called when a user enters a value into a filter input field or the value passed to the onFiltersChange handler by the filterRender option.
|
||||
onResizedChange={(newResized, event) => {...}} // Called when a user clicks on a resizing component (the right edge of a column header)
|
||||
/>
|
||||
```
|
||||
|
||||
@@ -712,21 +805,57 @@ Accessing internal state and wrapping with more UI:
|
||||
|
||||
The possibilities are endless!
|
||||
|
||||
## Sorting
|
||||
Sorting comes built in with React-Table.
|
||||
- Click a column header to sort by its accessor.
|
||||
- Click it again to reverse the sort.
|
||||
- Set `defaultSortDesc` property to `true` to make the first sort direction default to descending.
|
||||
- Override a specific column's default sort direction by using the same `defaultSortDesc` property on a column, set to `true`
|
||||
|
||||
## 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.
|
||||
## Custom Sorting Algorithm
|
||||
To override the default sorting algorithm for the whole table use the `defaultSortMethod` prop.
|
||||
|
||||
If you don't want particular column to be filtered you can set the `hideFilter` option on the column.
|
||||
To override the sorting algorithm for a single column, use the `sortMethod` column property.
|
||||
|
||||
Supply a function that implements the native javascript [`Array.sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) interface. This is React Table's default sorting algorithm:
|
||||
- `a` the first value to compare
|
||||
- `b` the second value to compare
|
||||
- `dir` the
|
||||
```javascript
|
||||
defaultSortMethod = (a, b) => {
|
||||
// force null and undefined to the bottom
|
||||
a = (a === null || a === undefined) ? -Infinity : a
|
||||
b = (b === null || b === undefined) ? -Infinity : b
|
||||
// force any string values to lowercase
|
||||
a = typeof a === 'string' ? a.toLowerCase() : a
|
||||
b = typeof b === 'string' ? b.toLowerCase() : b
|
||||
// Return either 1 or -1 to indicate a sort priority
|
||||
if (a > b) {
|
||||
return 1
|
||||
}
|
||||
if (a < b) {
|
||||
return -1
|
||||
}
|
||||
// returning 0 or undefined will use any subsequent column sorting methods or the row index as a tiebreaker
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
## Filtering
|
||||
Filtering can be enabled by setting the `filterable` option on the table.
|
||||
|
||||
If you don't want particular column to be filtered you can set the `filterable={false}` 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.
|
||||
To completely override the filter that is shown, you can set the `Filter` column option. Using this option you can specify the JSX that is shown. The option is passed an `onChange` 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.
|
||||
See <a href="http://react-table.js.org/#/story/custom-filtering" 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:
|
||||
@@ -743,7 +872,10 @@ Object.assign(ReactTableDefaults, {
|
||||
TdComponent: component,
|
||||
TfootComponent: component,
|
||||
ExpanderComponent: component,
|
||||
AggregatedComponent: component,
|
||||
PivotValueComponent: component,
|
||||
PivotComponent: component,
|
||||
FilterComponent: component,
|
||||
PaginationComponent: component,
|
||||
PreviousComponent: undefined,
|
||||
NextComponent: undefined,
|
||||
@@ -768,19 +900,18 @@ 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
|
||||
- `$ yarn`
|
||||
- `$ yarn run storybook`
|
||||
- Install dependencies with `$ yarn`
|
||||
- Auto-build files as you edit with `$ yarn run watch`
|
||||
- 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
|
||||
- Run the <a href="https://github.com/tannerlinsley/react-story">React Story</a> locally with `$ yarn run docs`
|
||||
- View changes as you edit `docs/src`,
|
||||
- Submit PR for review
|
||||
|
||||
#### Scripts
|
||||
|
||||
- `$ yarn run storybook` Runs the storybook server
|
||||
- `$ yarn run watch` Watches files and builds via babel
|
||||
- `$ yarn run docs` 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
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
react-table.js.org
|
||||
+1623
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 7.1 KiB |
@@ -1,22 +0,0 @@
|
||||
|
||||
<!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.5f0262046fdd8adfb130.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
|
||||
<!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.2e7b063c56eb89efa1fe.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "new",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"react-scripts": "0.9.5",
|
||||
"standard": "^10.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"github-markdown-css": "^2.6.0",
|
||||
"marked": "^0.3.6",
|
||||
"namor": "^1.0.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-json-tree": "^0.10.9",
|
||||
"react-story": "^0.0.10"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tag above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React Table - A lightweight, fast and extendable datagrid built for React</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start`.
|
||||
To create a production bundle, use `npm run build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
import React from 'react'
|
||||
//
|
||||
import ReactStory, { defaultProps } from 'react-story'
|
||||
|
||||
import './stories/utils/prism.css'
|
||||
import '../../react-table.css'
|
||||
|
||||
import Readme from './stories/Readme.js'
|
||||
import Simple from './stories/Simple.js'
|
||||
import CellRenderers from './stories/CellRenderers.js'
|
||||
import DefaultSorting from './stories/DefaultSorting.js'
|
||||
import CustomSorting from './stories/CustomSorting.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 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'
|
||||
import PivotingOptions from './stories/PivotingOptions.js'
|
||||
import EditableTable from './stories/EditableTable.js'
|
||||
import FixedHeader from './stories/FixedHeader.js'
|
||||
import MultiplePagers from './stories/MultiplePagers';
|
||||
|
||||
export default class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ReactStory
|
||||
style={{
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}}
|
||||
pathPrefix="story/"
|
||||
StoryWrapper={props => (
|
||||
<defaultProps.StoryWrapper
|
||||
css={{
|
||||
padding: 0
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="//github.com/tannerlinsley/react-table"
|
||||
style={{
|
||||
display: 'block',
|
||||
textAlign: 'center',
|
||||
borderBottom: 'solid 3px #cccccc'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="//npmcdn.com/react-table/media/Banner.png"
|
||||
alt="React Table Logo"
|
||||
style={{
|
||||
width: '100px'
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
<div
|
||||
{...props}
|
||||
style={{
|
||||
padding: '10px'
|
||||
}}
|
||||
/>
|
||||
</defaultProps.StoryWrapper>
|
||||
)}
|
||||
stories={[
|
||||
{ name: 'Readme', component: Readme },
|
||||
{ name: 'Simple Table', component: Simple },
|
||||
{
|
||||
name: 'Cell Renderers & Custom Components',
|
||||
component: CellRenderers
|
||||
},
|
||||
{ name: 'Default Sorting', component: DefaultSorting },
|
||||
{ name: 'Custom Sorting', component: CustomSorting },
|
||||
{ name: 'Custom Column Widths', component: CustomWidths },
|
||||
{ name: 'Custom Component Props', component: CustomComponentProps },
|
||||
{ name: 'Server-side Data', component: ServerSide },
|
||||
{ name: 'Sub Components', component: SubComponents },
|
||||
{ name: 'Pivoting & Aggregation', component: Pivoting },
|
||||
{
|
||||
name: 'Pivoting & Aggregation w/ Sub Components',
|
||||
component: PivotingSubComponents
|
||||
},
|
||||
{
|
||||
name: '100k Rows w/ Pivoting & Sub Components',
|
||||
component: OneHundredKRows
|
||||
},
|
||||
{ name: 'Pivoting Options', component: PivotingOptions },
|
||||
{ name: 'Functional Rendering', component: FunctionalRendering },
|
||||
{
|
||||
name: 'Custom Expander Position',
|
||||
component: CustomExpanderPosition
|
||||
},
|
||||
{ name: 'Custom "No Data" Text', component: NoDataText },
|
||||
{ name: 'Footers', component: Footers },
|
||||
{ name: 'Custom Filtering', component: Filtering },
|
||||
{ name: 'Controlled Component', component: ControlledTable },
|
||||
{ name: 'Editable Table', component: EditableTable },
|
||||
{ name: 'Fixed Header w/ Vertical Scroll', component: FixedHeader },
|
||||
{ name: 'Multiple Pagers (Top and Bottom)', component: MultiplePagers}
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
html, body, #root {
|
||||
height: 100%
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
//
|
||||
import './index.css'
|
||||
import App from './App.js'
|
||||
|
||||
ReactDOM.render(
|
||||
<App />,
|
||||
document.getElementById('root')
|
||||
)
|
||||
@@ -0,0 +1,109 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
const statusChance = Math.random()
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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',
|
||||
Cell: 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',
|
||||
Cell: 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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./CellRenderers')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,89 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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 Story extends React.PureComponent {
|
||||
constructor () {
|
||||
super()
|
||||
this.state = {
|
||||
sorted: [],
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
expanded: {},
|
||||
resized: [],
|
||||
filtered: []
|
||||
}
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
pivotBy={['lastName']}
|
||||
filterable
|
||||
// Controlled Props
|
||||
sorted={this.state.sorted}
|
||||
page={this.state.page}
|
||||
pageSize={this.state.pageSize}
|
||||
expanded={this.state.expanded}
|
||||
resized={this.state.resized}
|
||||
filtered={this.state.filtered}
|
||||
// Callbacks
|
||||
onSortedChange={sorted => this.setState({sorted})}
|
||||
onPageChange={page => this.setState({page})}
|
||||
onPageSizeChange={(pageSize, page) => this.setState({page, pageSize})}
|
||||
onExpandedChange={expanded => this.setState({expanded})}
|
||||
onResizedChange={resized => this.setState({resized})}
|
||||
onFilteredChange={filtered => this.setState({filtered})}
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<pre><code><strong>this.state ===</strong> {JSON.stringify(this.state, null, 2)}</code></pre>
|
||||
<br />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./ControlledTable')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./CustomComponentProps')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,81 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({words: 1, numbers: 0}),
|
||||
lastName: namor.generate({words: 1, numbers: 0}),
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
|
||||
const columns = [{
|
||||
Header: 'Name',
|
||||
columns: [{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
Footer: () => <div style={{textAlign: 'center'}}>First Name</div>
|
||||
}, {
|
||||
Header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
Footer: () => <div style={{textAlign: 'center'}}>Last Name</div>
|
||||
}]
|
||||
}, {
|
||||
Header: 'Info',
|
||||
columns: [{
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
Footer: () => <div style={{textAlign: 'center'}}>Age</div>
|
||||
}]
|
||||
}, {
|
||||
Header: 'Expand',
|
||||
columns: [{
|
||||
expander: true,
|
||||
Header: () => (<strong>More</strong>),
|
||||
width: 65,
|
||||
Expander: ({isExpanded, ...rest}) => (
|
||||
<div>
|
||||
{isExpanded ? <span>⊙</span> : <span>⊕</span>}
|
||||
</div>
|
||||
),
|
||||
style: {cursor: 'pointer', fontSize: 25, padding: '0', textAlign: 'center', userSelect: 'none'},
|
||||
Footer: () => <span>♥</span>
|
||||
}]
|
||||
}]
|
||||
|
||||
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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./CustomExpanderPosition')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,78 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
|
||||
const columns = [{
|
||||
Header: 'Name',
|
||||
columns: [{
|
||||
Header: 'First Name (Sorted by Length, A-Z)',
|
||||
accessor: 'firstName',
|
||||
sortMethod: (a, b) => {
|
||||
if (a.length === b.length) {
|
||||
return a > b ? 1 : -1
|
||||
}
|
||||
return a.length > b.length ? 1 : -1
|
||||
}
|
||||
}, {
|
||||
Header: 'Last Name (Sorted in reverse, A-Z)',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
sortMethod: (a, b) => {
|
||||
if (a === b) {
|
||||
return 0
|
||||
}
|
||||
const aReverse = a.split('').reverse().join('')
|
||||
const bReverse = b.split('').reverse().join('')
|
||||
return aReverse > bReverse ? 1 : -1
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
Header: 'Info',
|
||||
columns: [{
|
||||
Header: 'Age',
|
||||
accessor: 'age'
|
||||
}]
|
||||
}]
|
||||
|
||||
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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./CustomSorting')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./CustomWidths')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,68 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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}
|
||||
defaultSorted={[{
|
||||
id: 'age',
|
||||
desc: true
|
||||
}]}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./DefaultSorting')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,70 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
constructor (props, context) {
|
||||
super(props, context)
|
||||
this.renderEditable = this.renderEditable.bind(this)
|
||||
|
||||
this.state = {
|
||||
data: [
|
||||
{ firstName: 'Lucy', lastName: 'Marks' },
|
||||
{ firstName: 'Bejamin', lastName: 'Pike' }
|
||||
]
|
||||
}
|
||||
|
||||
this.columns = [
|
||||
{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
Cell: this.renderEditable
|
||||
},
|
||||
{
|
||||
Header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
Cell: this.renderEditable
|
||||
},
|
||||
{
|
||||
Header: 'Full Name',
|
||||
id: 'full',
|
||||
accessor: d => d.firstName + ' ' + d.lastName
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
renderEditable (cellInfo) {
|
||||
return (<div style={{ backgroundColor: '#fafafa' }} contentEditable suppressContentEditableWarning onBlur={(e) => {
|
||||
const data = [...this.state.data]
|
||||
data[cellInfo.index][cellInfo.column.id] = e.target.textContent
|
||||
this.setState({data: data})
|
||||
}}>{this.state.data[cellInfo.index][cellInfo.column.id]}</div>)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className='table-wrap' style={{marginBottom: '20px'}}>
|
||||
<p>First two columns are editable just by clicking into them using the <code>contentEditable</code> attribute. Last column (Full Name) is computed from the first two.</p>
|
||||
<ReactTable
|
||||
data={this.state.data}
|
||||
columns={this.columns}
|
||||
defaultPageSize={2}
|
||||
showPageSizeOptions={false}
|
||||
showPagination={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./EditableTable')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -1,19 +1,20 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
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 {
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
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}),
|
||||
firstName: namor.generate({words: 1, numbers: 0}),
|
||||
lastName: namor.generate({words: 1, numbers: 0}),
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
@@ -28,7 +29,9 @@ class Filtering extends React.Component {
|
||||
collapseOnPageChange: true,
|
||||
collapseOnDataChange: true,
|
||||
freezeWhenExpanded: false,
|
||||
showFilters: true
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
resizable: true
|
||||
},
|
||||
data: data
|
||||
}
|
||||
@@ -38,27 +41,27 @@ class Filtering extends React.Component {
|
||||
|
||||
render () {
|
||||
const columns = [{
|
||||
header: 'Name',
|
||||
Header: 'Name',
|
||||
columns: [{
|
||||
header: 'First Name',
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
filterMethod: (filter, row) => (row[filter.id].startsWith(filter.value) && row[filter.id].endsWith(filter.value))
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
Header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
filterMethod: (filter, row) => (row[filter.id].includes(filter.value))
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
Header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
Header: 'Age',
|
||||
accessor: 'age'
|
||||
}, {
|
||||
header: 'Over 21',
|
||||
Header: 'Over 21',
|
||||
accessor: 'age',
|
||||
id: 'over',
|
||||
render: ({value}) => (value >= 21 ? 'Yes' : 'No'),
|
||||
Cell: ({value}) => (value >= 21 ? 'Yes' : 'No'),
|
||||
filterMethod: (filter, row) => {
|
||||
if (filter.value === 'all') {
|
||||
return true
|
||||
@@ -68,14 +71,15 @@ class Filtering extends React.Component {
|
||||
}
|
||||
return row[filter.id] < 21
|
||||
},
|
||||
filterRender: ({filter, onFilterChange}) => (
|
||||
Filter: ({filter, onChange}) => (
|
||||
<select
|
||||
onChange={event => onFilterChange(event.target.value)}
|
||||
onChange={event => onChange(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>
|
||||
value={filter ? filter.value : 'all'}
|
||||
>
|
||||
<option value='all' />
|
||||
<option value='true'>Can Drink</option>
|
||||
<option value='false'>Can't Drink</option>
|
||||
</select>
|
||||
)
|
||||
}
|
||||
@@ -84,31 +88,31 @@ class Filtering extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{float: 'left'}}>
|
||||
<div>
|
||||
<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>
|
||||
)
|
||||
})
|
||||
}
|
||||
{
|
||||
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}}>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={this.state.data}
|
||||
@@ -148,21 +152,20 @@ class Filtering extends React.Component {
|
||||
<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>
|
||||
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>
|
||||
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>
|
||||
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
|
||||
text.</p>
|
||||
<p>To completely override the filter that is shown, you can set the <strong>Filter</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
|
||||
an <strong>onChange</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>
|
||||
)
|
||||
}
|
||||
@@ -178,87 +181,14 @@ class Filtering extends React.Component {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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 />
|
||||
// Source Code
|
||||
const source = require('!raw!./Filtering')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,81 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render() {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
children: _.map(_.range(10), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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={20}
|
||||
style={{
|
||||
height: '400px' // This will force the table body to overflow and scroll, since there is not enough room
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./Simple')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,92 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./Footers')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,137 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
import JSONTree from 'react-json-tree'
|
||||
|
||||
import ReactTable from '../../../lib/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'
|
||||
}
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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>
|
||||
|
||||
<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 />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./FunctionalRendering')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,73 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
children: _.map(_.range(10), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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}
|
||||
showPagination
|
||||
showPaginationTop
|
||||
showPaginationBottom
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./MultiplePagers')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,57 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
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 (
|
||||
<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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./NoDataText')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,97 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(100000), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
visits: Math.floor(Math.random() * 100)
|
||||
}
|
||||
})
|
||||
|
||||
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',
|
||||
aggregate: vals => _.round(_.mean(vals)),
|
||||
Aggregated: row => {
|
||||
return <span>{row.value} (avg)</span>
|
||||
}
|
||||
}, {
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals)
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
className='-striped -highlight'
|
||||
defaultPageSize={10}
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
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>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./OneHundredKRows')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,74 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(1000), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
visits: Math.floor(Math.random() * 100)
|
||||
}
|
||||
})
|
||||
|
||||
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',
|
||||
aggregate: vals => _.round(_.mean(vals)),
|
||||
Aggregated: row => {
|
||||
return <span>{row.value} (avg)</span>
|
||||
}
|
||||
}, {
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals)
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
className='-striped -highlight'
|
||||
defaultPageSize={10}
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./Pivoting')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,132 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(1000), d => {
|
||||
return {
|
||||
firstName: namor.generate({words: 1, numbers: 0}),
|
||||
lastName: namor.generate({words: 1, numbers: 0}),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
visits: Math.floor(Math.random() * 100)
|
||||
}
|
||||
})
|
||||
|
||||
const columns = [{
|
||||
Header: 'Name',
|
||||
columns: [{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
PivotValue: ({value}) => <span style={{color: 'darkred'}}>{value}</span>
|
||||
}, {
|
||||
Header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
PivotValue: ({value}) => <span style={{color: 'darkblue'}}>{value}</span>,
|
||||
Footer: () => <div style={{textAlign: 'center'}}><strong>Pivot Column Footer</strong></div>
|
||||
}]
|
||||
}, {
|
||||
Header: 'Info',
|
||||
columns: [{
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
aggregate: vals => {
|
||||
return _.round(_.mean(vals))
|
||||
},
|
||||
Aggregated: row => <span>{row.value} (avg)</span>
|
||||
}, {
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals),
|
||||
filterable: false
|
||||
}]
|
||||
}, {
|
||||
pivot: true,
|
||||
Header: () => <strong>Overridden Pivot Column Header Group</strong>
|
||||
}, {
|
||||
expander: true
|
||||
}]
|
||||
|
||||
const subtableColumns = [{
|
||||
Header: 'Name',
|
||||
columns: [{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName'
|
||||
}, {
|
||||
Header: 'Last Name',
|
||||
id: 'lastName'
|
||||
}]
|
||||
}, {
|
||||
Header: 'Info',
|
||||
columns: [{
|
||||
Header: 'Age',
|
||||
accessor: 'age'
|
||||
}, {
|
||||
Header: 'Visits',
|
||||
accessor: 'visits'
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
defaultSorted={[{id: 'firstName', desc: false}, {id: 'lastName', desc: true}]}
|
||||
collapseOnSortingChange={false}
|
||||
filterable
|
||||
ExpanderComponent={({isExpanded, ...rest}) => (
|
||||
isExpanded ? <span> ➘ </span> : <span> ➙ </span>
|
||||
)}
|
||||
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={subtableColumns}
|
||||
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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./PivotingOptions')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,100 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(1000), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
visits: Math.floor(Math.random() * 100)
|
||||
}
|
||||
})
|
||||
|
||||
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',
|
||||
aggregate: vals => _.round(_.mean(vals)),
|
||||
Aggregated: row => {
|
||||
return <span>{row.value} (avg)</span>
|
||||
},
|
||||
filterMethod: (filter, row) => (filter.value === `${row[filter.id]} (avg)`)
|
||||
}, {
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals),
|
||||
filterable: false
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
filterable
|
||||
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>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./PivotingSubComponents')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import marked from 'marked'
|
||||
//
|
||||
import Readme from '!raw!../../../README.md'
|
||||
import 'github-markdown-css/github-markdown.css'
|
||||
|
||||
export default class Story extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<span
|
||||
className="markdown-body"
|
||||
dangerouslySetInnerHTML={{ __html: marked(Readme) }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
componentDidMount() {
|
||||
global.Prism.highlightAll()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
const rawData = _.map(_.range(3424), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
|
||||
// Now let's mock the server. It's job is simple: use the table model to sort and return the page data
|
||||
const requestData = (pageSize, page, sorted, filtered) => {
|
||||
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
|
||||
let filteredData = rawData
|
||||
if (filtered.length) {
|
||||
filteredData = filtered.reduce(
|
||||
(filteredSoFar, nextFilter) => {
|
||||
return filteredSoFar.filter(
|
||||
(row) => {
|
||||
return (row[nextFilter.id] + '').includes(nextFilter.value)
|
||||
})
|
||||
}
|
||||
, filteredData)
|
||||
}
|
||||
const sortedData = _.orderBy(filteredData, sorted.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]
|
||||
}
|
||||
}), sorted.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(filteredData.length / pageSize)
|
||||
}
|
||||
|
||||
// Here we'll simulate a server response with 500ms of delay.
|
||||
setTimeout(() => resolve(res), 500)
|
||||
})
|
||||
}
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
constructor () {
|
||||
super()
|
||||
this.state = {
|
||||
data: [],
|
||||
pages: null,
|
||||
loading: true
|
||||
}
|
||||
this.fetchData = this.fetchData.bind(this)
|
||||
}
|
||||
fetchData (state, instance) {
|
||||
// Whenever the table model changes, or the user sorts or changes pages, this method gets called and passed the current table model.
|
||||
// You can set the `loading` prop of the table to true to use the built-in one or show you're own loading bar if you want.
|
||||
this.setState({loading: true})
|
||||
// Request the data however you want. Here, we'll use our mocked service we created earlier
|
||||
requestData(state.pageSize, state.page, state.sorted, state.filtered)
|
||||
.then((res) => {
|
||||
// Now just get the rows of data to your React Table (and update anything else like total pages or loading)
|
||||
this.setState({
|
||||
data: res.rows,
|
||||
pages: res.pages,
|
||||
loading: false
|
||||
})
|
||||
})
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
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}
|
||||
filterable
|
||||
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
|
||||
onFetchData={this.fetchData} // Request new data when things change
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./ServerSide')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,77 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render() {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
children: _.map(_.range(10), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
lastName: namor.generate({ words: 1, numbers: 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}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./Simple')
|
||||
|
||||
export default () =>
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
@@ -0,0 +1,146 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
const data = _.map(_.range(5553), d => {
|
||||
return {
|
||||
firstName: namor.generate({words: 1, numbers: 0}),
|
||||
lastName: namor.generate({words: 1, numbers: 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,
|
||||
filterable: false,
|
||||
sortable: true,
|
||||
resizable: true
|
||||
},
|
||||
data: data
|
||||
}
|
||||
|
||||
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: 'Info',
|
||||
columns: [{
|
||||
Header: 'Age',
|
||||
accessor: 'age'
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<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'>
|
||||
<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>
|
||||
</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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./SubComponents')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,78 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import ReactTable from '../../../lib/index'
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
render () {
|
||||
const data = _.map(_.range(5553), d => {
|
||||
const children = _.map(_.range(10), d => {
|
||||
const grandChildren = _.map(_.range(10), d => {
|
||||
return {
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
children: grandChildren
|
||||
}
|
||||
})
|
||||
return {
|
||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
||||
firstName: children.map(d => d.firstName),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
children
|
||||
}
|
||||
})
|
||||
|
||||
const columns = [{
|
||||
Header: 'Name',
|
||||
columns: [{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
expander: true
|
||||
}, {
|
||||
Header: 'Last Name',
|
||||
accessor: 'lastName'
|
||||
}]
|
||||
}, {
|
||||
Header: 'Info',
|
||||
columns: [{
|
||||
Header: 'Age',
|
||||
accessor: 'age'
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
subRowsKey='children'
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Source Code
|
||||
const CodeHighlight = require('./components/codeHighlight').default
|
||||
const source = require('!raw!./SubRows')
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Story />
|
||||
<CodeHighlight>{() => source}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import '../utils/prism'
|
||||
|
||||
export default React.createClass({
|
||||
export default class CodeHighlight extends React.Component {
|
||||
render () {
|
||||
const { language, children } = this.props
|
||||
return (
|
||||
@@ -11,11 +11,11 @@ export default React.createClass({
|
||||
</code>
|
||||
</pre>
|
||||
)
|
||||
},
|
||||
}
|
||||
componentDidMount () {
|
||||
window.Prism.highlightAll()
|
||||
},
|
||||
}
|
||||
componentDidUpdate () {
|
||||
window.Prism.highlightAll()
|
||||
}
|
||||
})
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"static/manager.2e7b063c56eb89efa1fe.bundle.js","sources":["webpack:///static/manager.2e7b063c56eb89efa1fe.bundle.js"],"mappings":"AAAA;AAkuDA;AA84DA;AA28DA;AA00DA;AAsvEA;AAuhDA;AAsrDA;AAsiDA;AAg6DA;AA2nDA;AA++CA;AAkvDA;AAsnEA;AA2oDA;AAivCA;AA+nDA;AAkpDA;AA6lEA;AAs4DA;AAquDA;AA+pDA;AAoxDA;AAsrDA;AA+yDA;AA88GA;AAj6CA;AAopGA;AAsuFA;AA+zEA;AAtMA;AAizEA","sourceRoot":""}
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"static/preview.5f0262046fdd8adfb130.bundle.js","sources":["webpack:///static/preview.5f0262046fdd8adfb130.bundle.js"],"mappings":"AAAA;AAkuDA;AAmgEA;AA2jFA;AAinFA;AAgsPA;AA+6EA;AAytDA;AA+jEA;AAixDA;AAuzDA;AAk1DA;AA6pDA;AA2+CA;AA+4DA;AAqpDA;AA89CA;AA0nDA;AA2jEA;AA03DA;AA6gCA;AA80DA;AAooDA;AA2nEA;AAw1DA;AA64DA;AAixCA;AAyrFA;AAmjGA;AA4lDA;AA+zCA;AA8vCA;AAshCA;AAwpDA;AA+rCA;AAsCA;AA+jFA","sourceRoot":""}
|
||||
+5783
File diff suppressed because it is too large
Load Diff
+7
-13
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-table",
|
||||
"version": "5.6.0",
|
||||
"version": "6.3.0",
|
||||
"description": "A fast, lightweight, opinionated table and datagrid built on React",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/tannerlinsley/react-table#readme",
|
||||
@@ -34,38 +34,32 @@
|
||||
"build": "npm-run-all build:*",
|
||||
"prepublish": "npm run build && npm run umd",
|
||||
"postpublish": "git push --tags",
|
||||
"storybook": "start-storybook -p 8000 -c .storybook",
|
||||
"docs": "build-storybook -o docs && cp .storybook/CNAME docs/CNAME"
|
||||
"docs": "yarn watch & cd docs && yarn && yarn start",
|
||||
"docs:build": "yarn build && cd docs && yarn && yarn run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5"
|
||||
"classnames": "^2.2.5",
|
||||
"react-json-tree": "^0.10.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.x.x"
|
||||
},
|
||||
"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",
|
||||
"babel-preset-react": "6.11.1",
|
||||
"babel-preset-stage-2": "6.13.0",
|
||||
"github-markdown-css": "^2.4.1",
|
||||
"html-loader": "^0.4.4",
|
||||
"markdown-loader": "^0.1.7",
|
||||
"namor": "^0.3.0",
|
||||
"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",
|
||||
"standard": "^10.0.2",
|
||||
"stylus": "^0.54.5",
|
||||
"webpack": "^2.4.1"
|
||||
"webpack": "^2.5.1"
|
||||
},
|
||||
"standard": {
|
||||
"parser": "babel-eslint",
|
||||
|
||||
+98
-55
@@ -11,6 +11,8 @@ export default {
|
||||
data: [],
|
||||
loading: false,
|
||||
showPagination: true,
|
||||
showPaginationTop: false,
|
||||
showPaginationBottom: true,
|
||||
showPageSizeOptions: true,
|
||||
pageSizeOptions: [5, 10, 20, 25, 50, 100],
|
||||
defaultPageSize: 20,
|
||||
@@ -19,43 +21,69 @@ export default {
|
||||
collapseOnPageChange: true,
|
||||
collapseOnDataChange: true,
|
||||
freezeWhenExpanded: false,
|
||||
defaultSorting: [],
|
||||
showFilters: false,
|
||||
defaultFiltering: [],
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
filterable: false,
|
||||
defaultSortDesc: false,
|
||||
defaultSorted: [],
|
||||
defaultFiltered: [],
|
||||
defaultResized: [],
|
||||
defaultExpanded: {},
|
||||
defaultFilterMethod: (filter, row, column) => {
|
||||
const id = filter.pivotId || filter.id
|
||||
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
|
||||
return row[id] !== undefined
|
||||
? String(row[id]).startsWith(filter.value)
|
||||
: true
|
||||
},
|
||||
defaultSortMethod: (a, b) => {
|
||||
// force null and undefined to the bottom
|
||||
a = a === null || a === undefined ? '' : a
|
||||
b = b === null || b === undefined ? '' : b
|
||||
// force any string values to lowercase
|
||||
a = typeof a === 'string' ? a.toLowerCase() : a
|
||||
b = typeof b === 'string' ? b.toLowerCase() : b
|
||||
// Return either 1 or -1 to indicate a sort priority
|
||||
if (a > b) {
|
||||
return 1
|
||||
}
|
||||
if (a < b) {
|
||||
return -1
|
||||
}
|
||||
// returning 0, undefined or any falsey value will use subsequent sorts or the index as a tiebreaker
|
||||
return 0
|
||||
},
|
||||
resizable: true,
|
||||
defaultResizing: [],
|
||||
|
||||
// Controlled State Overrides
|
||||
// Controlled State Props
|
||||
// page: undefined,
|
||||
// pageSize: undefined,
|
||||
// sorting: undefined,
|
||||
// sorted: [],
|
||||
// filtered: [],
|
||||
// resized: [],
|
||||
// expanded: {},
|
||||
|
||||
// Controlled State Callbacks
|
||||
onExpandSubComponent: undefined,
|
||||
onPageChange: undefined,
|
||||
onPageSizeChange: undefined,
|
||||
onSortingChange: undefined,
|
||||
onFilteringChange: undefined,
|
||||
onResize: undefined,
|
||||
onSortedChange: undefined,
|
||||
onFilteredChange: undefined,
|
||||
onResizedChange: undefined,
|
||||
onExpandedChange: undefined,
|
||||
|
||||
// Pivoting
|
||||
pivotBy: undefined,
|
||||
|
||||
// Key Constants
|
||||
pivotValKey: '_pivotVal',
|
||||
pivotIDKey: '_pivotID',
|
||||
subRowsKey: '_subRows',
|
||||
aggregatedKey: '_aggregated',
|
||||
nestingLevelKey: '_nestingLevel',
|
||||
originalKey: '_original',
|
||||
indexKey: '_index',
|
||||
groupedByPivotKey: '_groupedByPivot',
|
||||
|
||||
// Pivoting State Overrides
|
||||
// expandedRows: {},
|
||||
|
||||
// Pivoting State Callbacks
|
||||
onExpandRow: undefined,
|
||||
|
||||
// General Callbacks
|
||||
onChange: () => null,
|
||||
// Server-side Callbacks
|
||||
onFetchData: () => null,
|
||||
|
||||
// Classes
|
||||
className: '',
|
||||
@@ -87,49 +115,49 @@ export default {
|
||||
|
||||
// Global Column Defaults
|
||||
column: {
|
||||
sortable: true,
|
||||
// Renderers
|
||||
Cell: undefined,
|
||||
Header: undefined,
|
||||
Footer: undefined,
|
||||
Aggregated: undefined,
|
||||
Pivot: undefined,
|
||||
PivotValue: undefined,
|
||||
Expander: undefined,
|
||||
Filter: undefined,
|
||||
// All Columns
|
||||
sortable: undefined, // use table default
|
||||
resizable: undefined, // use table default
|
||||
filterable: undefined, // use table default
|
||||
show: true,
|
||||
minWidth: 100,
|
||||
// Cells only
|
||||
render: undefined,
|
||||
className: '',
|
||||
style: {},
|
||||
getProps: emptyObj,
|
||||
// Pivot only
|
||||
aggregate: undefined,
|
||||
// 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)}
|
||||
/>
|
||||
)
|
||||
sortMethod: undefined,
|
||||
},
|
||||
|
||||
// Global Expander Column Defaults
|
||||
expanderDefaults: {
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
filterable: false,
|
||||
width: 35,
|
||||
hideFilter: true
|
||||
// render: will be overriden in methods.js to display ExpanderComponent
|
||||
},
|
||||
|
||||
// Global Pivot Column Defaults
|
||||
pivotDefaults: {
|
||||
filterRender: undefined
|
||||
// render: will be overriden in methods.js to display ExpanderComponent and PivotValueComponent
|
||||
// extend the defaults for pivoted columns here
|
||||
},
|
||||
|
||||
// Text
|
||||
@@ -147,7 +175,7 @@ export default {
|
||||
TbodyComponent: _.makeTemplateComponent('rt-tbody'),
|
||||
TrGroupComponent: _.makeTemplateComponent('rt-tr-group'),
|
||||
TrComponent: _.makeTemplateComponent('rt-tr'),
|
||||
ThComponent: ({toggleSort, className, children, ...rest}) => {
|
||||
ThComponent: ({ toggleSort, className, children, ...rest }) => {
|
||||
return (
|
||||
<div
|
||||
className={classnames(className, 'rt-th')}
|
||||
@@ -162,30 +190,45 @@ export default {
|
||||
},
|
||||
TdComponent: _.makeTemplateComponent('rt-td'),
|
||||
TfootComponent: _.makeTemplateComponent('rt-tfoot'),
|
||||
ExpanderComponent: ({isExpanded}) => (
|
||||
FilterComponent: ({ filter, onChange }) =>
|
||||
<input
|
||||
type='text'
|
||||
style={{
|
||||
width: '100%',
|
||||
}}
|
||||
value={filter ? filter.value : ''}
|
||||
onChange={event => onChange(event.target.value)}
|
||||
/>,
|
||||
ExpanderComponent: ({ isExpanded }) =>
|
||||
<div className={classnames('rt-expander', isExpanded && '-open')}>
|
||||
•
|
||||
</div>
|
||||
),
|
||||
PivotValueComponent: ({subRows, value}) => (
|
||||
<span>{value} {subRows && `(${subRows.length})`}</span>
|
||||
),
|
||||
</div>,
|
||||
PivotValueComponent: ({ subRows, value }) =>
|
||||
<span>{value} {subRows && `(${subRows.length})`}</span>,
|
||||
AggregatedComponent: ({ subRows, column }) => {
|
||||
const previewValues = subRows
|
||||
.filter(d => typeof d[column.id] !== 'undefined')
|
||||
.map((row, i) =>
|
||||
<span key={i}>
|
||||
{row[column.id]}{i < subRows.length - 1 ? ', ' : ''}
|
||||
</span>
|
||||
)
|
||||
return <span>{previewValues}</span>
|
||||
},
|
||||
PivotComponent: undefined, // this is a computed default generated using
|
||||
// the ExpanderComponent and PivotValueComponent at run-time in methods.js
|
||||
PaginationComponent: Pagination,
|
||||
PreviousComponent: undefined,
|
||||
NextComponent: undefined,
|
||||
LoadingComponent: ({className, loading, loadingText, ...rest}) => (
|
||||
<div className={classnames(
|
||||
'-loading',
|
||||
{'-active': loading},
|
||||
className
|
||||
)}
|
||||
LoadingComponent: ({ className, loading, loadingText, ...rest }) =>
|
||||
<div
|
||||
className={classnames('-loading', { '-active': loading }, className)}
|
||||
{...rest}
|
||||
>
|
||||
<div className='-loading-inner'>
|
||||
{loadingText}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
</div>,
|
||||
NoDataComponent: _.makeTemplateComponent('rt-noData'),
|
||||
ResizerComponent: _.makeTemplateComponent('rt-resizer')
|
||||
ResizerComponent: _.makeTemplateComponent('rt-resizer'),
|
||||
}
|
||||
|
||||
+482
-312
File diff suppressed because it is too large
Load Diff
+14
-3
@@ -4,10 +4,13 @@ $expandSize = 7px
|
||||
|
||||
.ReactTable
|
||||
position:relative
|
||||
display: flex
|
||||
flex-direction: column
|
||||
border: 1px solid alpha(black, .1)
|
||||
*
|
||||
box-sizing: border-box
|
||||
.rt-table
|
||||
flex: 1
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: stretch
|
||||
@@ -16,7 +19,9 @@ $expandSize = 7px
|
||||
overflow: auto
|
||||
|
||||
.rt-thead
|
||||
flex: 1 0 auto
|
||||
display: flex
|
||||
// overflow-y: scroll
|
||||
flex-direction: column
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
@@ -91,8 +96,10 @@ $expandSize = 7px
|
||||
margin-top: -10px
|
||||
|
||||
.rt-tbody
|
||||
flex: 99999 1 auto
|
||||
display: flex
|
||||
flex-direction: column
|
||||
overflow: auto
|
||||
// z-index:0
|
||||
.rt-tr-group
|
||||
border-bottom: solid 1px alpha(black, .05)
|
||||
@@ -102,13 +109,15 @@ $expandSize = 7px
|
||||
border-right:1px solid alpha(black, .02)
|
||||
&:last-child
|
||||
border-right:0
|
||||
.rt-pivot
|
||||
.rt-expandable
|
||||
cursor: pointer
|
||||
.rt-tr-group
|
||||
flex: 1 0 auto
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: stretch
|
||||
.rt-tr
|
||||
flex: 1 0 auto
|
||||
display: inline-flex
|
||||
.rt-th
|
||||
.rt-td
|
||||
@@ -191,7 +200,7 @@ $expandSize = 7px
|
||||
appearance:none
|
||||
display:block
|
||||
width:100%
|
||||
height: 100%
|
||||
height:100%
|
||||
border: 0
|
||||
border-radius: 3px
|
||||
padding: 6px
|
||||
@@ -296,6 +305,8 @@ $expandSize = 7px
|
||||
input:not([type="checkbox"]):not([type="radio"])
|
||||
select
|
||||
appearance: none
|
||||
&::-ms-expand
|
||||
display: none
|
||||
|
||||
.select-wrap
|
||||
position:relative
|
||||
@@ -318,4 +329,4 @@ $expandSize = 7px
|
||||
.rt-td
|
||||
transition: none!important
|
||||
cursor: col-resize
|
||||
user-select none
|
||||
user-select none
|
||||
|
||||
+100
-69
@@ -1,87 +1,118 @@
|
||||
export default Base => class extends Base {
|
||||
|
||||
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
|
||||
export default Base =>
|
||||
class extends Base {
|
||||
componentWillMount () {
|
||||
this.setStateWithData(this.getDataModel(this.getResolvedState()))
|
||||
}
|
||||
|
||||
if ((oldState.showFilters !== newState.showFilters) ||
|
||||
(oldState.showFilters !== newState.showFilters)) {
|
||||
newState.filtering = newState.defaultFiltering
|
||||
componentDidMount () {
|
||||
this.fireFetchData()
|
||||
}
|
||||
|
||||
// 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))
|
||||
componentWillReceiveProps (nextProps, nextState) {
|
||||
const oldState = this.getResolvedState()
|
||||
const newState = this.getResolvedState(nextProps, nextState)
|
||||
|
||||
// Do a deep compare of new and old `defaultOption` and
|
||||
// if they are different reset `option = defaultOption`
|
||||
const defaultableOptions = ['sorted', 'filtered', 'resized', 'expanded']
|
||||
defaultableOptions.forEach(x => {
|
||||
const defaultName = `default${x.charAt(0).toUpperCase() + x.slice(1)}`
|
||||
if (
|
||||
JSON.stringify(oldState[defaultName]) !==
|
||||
JSON.stringify(newState[defaultName])
|
||||
) {
|
||||
newState[x] = newState[defaultName]
|
||||
}
|
||||
})
|
||||
|
||||
// If they change these table options, we need to reset defaults
|
||||
// or else we could get into a state where the user has changed the UI
|
||||
// and then disabled the ability to change it back.
|
||||
// e.g. If `filterable` has changed, set `filtered = defaultFiltered`
|
||||
const resettableOptions = ['sortable', 'filterable', 'resizable']
|
||||
resettableOptions.forEach(x => {
|
||||
if (oldState[x] !== newState[x]) {
|
||||
const baseName = x.replace('able', '')
|
||||
const optionName = `${baseName}ed`
|
||||
const defaultName = `default${optionName.charAt(0).toUpperCase() +
|
||||
optionName.slice(1)}`
|
||||
newState[optionName] = newState[defaultName]
|
||||
}
|
||||
})
|
||||
|
||||
// Props that trigger a data update
|
||||
if (
|
||||
oldState.data !== newState.data ||
|
||||
oldState.columns !== newState.columns ||
|
||||
oldState.pivotBy !== newState.pivotBy ||
|
||||
oldState.sorted !== newState.sorted ||
|
||||
oldState.filtered !== newState.filtered
|
||||
) {
|
||||
this.setStateWithData(this.getDataModel(newState))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setStateWithData (newState, cb) {
|
||||
const oldState = this.getResolvedState()
|
||||
const newResolvedState = this.getResolvedState({}, newState)
|
||||
const {freezeWhenExpanded} = newResolvedState
|
||||
setStateWithData (newState, cb) {
|
||||
const oldState = this.getResolvedState()
|
||||
const newResolvedState = this.getResolvedState({}, newState)
|
||||
const { freezeWhenExpanded } = newResolvedState
|
||||
|
||||
// Default to unfrozen state
|
||||
newResolvedState.frozen = false
|
||||
// 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 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.expanded)
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (newResolvedState.expanded[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 the data isn't frozen and either the data or
|
||||
// sorting model has changed, update the data
|
||||
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)
|
||||
(oldState.frozen && !newResolvedState.frozen) ||
|
||||
oldState.sorted !== newResolvedState.sorted ||
|
||||
oldState.filtered !== newResolvedState.filtered ||
|
||||
oldState.showFilters !== newResolvedState.showFilters ||
|
||||
(!newResolvedState.frozen &&
|
||||
oldState.resolvedData !== newResolvedState.resolvedData)
|
||||
) {
|
||||
newResolvedState.expandedRows = {}
|
||||
// Handle collapseOnsortedChange & collapseOnDataChange
|
||||
if (
|
||||
(oldState.sorted !== newResolvedState.sorted &&
|
||||
this.props.collapseOnSortingChange) ||
|
||||
oldState.filtered !== newResolvedState.filtered ||
|
||||
oldState.showFilters !== newResolvedState.showFilters ||
|
||||
(!newResolvedState.frozen &&
|
||||
oldState.resolvedData !== newResolvedState.resolvedData &&
|
||||
this.props.collapseOnDataChange)
|
||||
) {
|
||||
newResolvedState.expanded = {}
|
||||
}
|
||||
|
||||
Object.assign(newResolvedState, this.getSortedData(newResolvedState))
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
return this.setState(newResolvedState, cb)
|
||||
}
|
||||
}
|
||||
|
||||
+625
-551
File diff suppressed because it is too large
Load Diff
+36
-39
@@ -3,9 +3,8 @@ import classnames from 'classnames'
|
||||
//
|
||||
// import _ from './utils'
|
||||
|
||||
const defaultButton = (props) => (
|
||||
const defaultButton = props =>
|
||||
<button type='button' {...props} className='-btn'>{props.children}</button>
|
||||
)
|
||||
|
||||
export default class ReactTablePagination extends Component {
|
||||
constructor (props) {
|
||||
@@ -16,12 +15,12 @@ export default class ReactTablePagination extends Component {
|
||||
this.applyPage = this.applyPage.bind(this)
|
||||
|
||||
this.state = {
|
||||
page: props.page
|
||||
page: props.page,
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
this.setState({page: nextProps.page})
|
||||
this.setState({ page: nextProps.page })
|
||||
}
|
||||
|
||||
getSafePage (page) {
|
||||
@@ -33,7 +32,7 @@ export default class ReactTablePagination extends Component {
|
||||
|
||||
changePage (page) {
|
||||
page = this.getSafePage(page)
|
||||
this.setState({page})
|
||||
this.setState({ page })
|
||||
if (this.props.page !== page) {
|
||||
this.props.onPageChange(page)
|
||||
}
|
||||
@@ -60,7 +59,7 @@ export default class ReactTablePagination extends Component {
|
||||
onPageSizeChange,
|
||||
className,
|
||||
PreviousComponent = defaultButton,
|
||||
NextComponent = defaultButton
|
||||
NextComponent = defaultButton,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
@@ -70,7 +69,7 @@ export default class ReactTablePagination extends Component {
|
||||
>
|
||||
<div className='-previous'>
|
||||
<PreviousComponent
|
||||
onClick={(e) => {
|
||||
onClick={e => {
|
||||
if (!canPrevious) return
|
||||
this.changePage(page - 1)
|
||||
}}
|
||||
@@ -81,53 +80,51 @@ export default class ReactTablePagination extends Component {
|
||||
</div>
|
||||
<div className='-center'>
|
||||
<span className='-pageInfo'>
|
||||
{this.props.pageText} {showPageJump ? (
|
||||
<div className='-pageJump'>
|
||||
<input
|
||||
type={this.state.page === '' ? 'text' : 'number'}
|
||||
onChange={e => {
|
||||
const val = e.target.value
|
||||
const page = val - 1
|
||||
if (val === '') {
|
||||
return this.setState({page: val})
|
||||
}
|
||||
this.setState({page: this.getSafePage(page)})
|
||||
}}
|
||||
value={this.state.page === '' ? '' : this.state.page + 1}
|
||||
onBlur={this.applyPage}
|
||||
onKeyPress={e => {
|
||||
if (e.which === 13 || e.keyCode === 13) {
|
||||
this.applyPage()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<span className='-currentPage'>{page + 1}</span>
|
||||
)} {this.props.ofText} <span className='-totalPages'>{pages}</span>
|
||||
{this.props.pageText}{' '}
|
||||
{showPageJump
|
||||
? <div className='-pageJump'>
|
||||
<input
|
||||
type={this.state.page === '' ? 'text' : 'number'}
|
||||
onChange={e => {
|
||||
const val = e.target.value
|
||||
const page = val - 1
|
||||
if (val === '') {
|
||||
return this.setState({ page: val })
|
||||
}
|
||||
this.setState({ page: this.getSafePage(page) })
|
||||
}}
|
||||
value={this.state.page === '' ? '' : this.state.page + 1}
|
||||
onBlur={this.applyPage}
|
||||
onKeyPress={e => {
|
||||
if (e.which === 13 || e.keyCode === 13) {
|
||||
this.applyPage()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
: <span className='-currentPage'>{page + 1}</span>}{' '}
|
||||
{this.props.ofText}{' '}
|
||||
<span className='-totalPages'>{pages || 1}</span>
|
||||
</span>
|
||||
{showPageSizeOptions && (
|
||||
{showPageSizeOptions &&
|
||||
<span className='select-wrap -pageSizeOptions'>
|
||||
<select
|
||||
onChange={(e) => onPageSizeChange(Number(e.target.value))}
|
||||
onChange={e => onPageSizeChange(Number(e.target.value))}
|
||||
value={pageSize}
|
||||
>
|
||||
{pageSizeOptions.map((option, i) => {
|
||||
return (
|
||||
<option
|
||||
key={i}
|
||||
value={option}>
|
||||
<option key={i} value={option}>
|
||||
{option} {this.props.rowsText}
|
||||
</option>
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
</span>
|
||||
)}
|
||||
</span>}
|
||||
</div>
|
||||
<div className='-next'>
|
||||
<NextComponent
|
||||
onClick={(e) => {
|
||||
onClick={e => {
|
||||
if (!canNext) return
|
||||
this.changePage(page + 1)
|
||||
}}
|
||||
|
||||
+35
-38
@@ -18,7 +18,7 @@ export default {
|
||||
splitProps,
|
||||
compactObject,
|
||||
isSortingDesc,
|
||||
normalizeComponent
|
||||
normalizeComponent,
|
||||
}
|
||||
|
||||
function get (obj, path, def) {
|
||||
@@ -64,23 +64,20 @@ function range (n) {
|
||||
return arr
|
||||
}
|
||||
|
||||
function orderBy (arr, funcs, dirs) {
|
||||
return arr.sort((a, b) => {
|
||||
function orderBy (arr, funcs, dirs, indexKey) {
|
||||
return arr.sort((rowA, rowB) => {
|
||||
for (let i = 0; i < funcs.length; i++) {
|
||||
const comp = funcs[i]
|
||||
const ca = comp(a)
|
||||
const cb = comp(b)
|
||||
const desc = dirs[i] === false || dirs[i] === 'desc'
|
||||
if (ca > cb) {
|
||||
return desc ? -1 : 1
|
||||
}
|
||||
if (ca < cb) {
|
||||
return desc ? 1 : -1
|
||||
const sortInt = comp(rowA, rowB)
|
||||
if (sortInt) {
|
||||
return desc ? -sortInt : sortInt
|
||||
}
|
||||
}
|
||||
// Use the row index for tie breakers
|
||||
return dirs[0]
|
||||
? a.__index - b.__index
|
||||
: b.__index - a.__index
|
||||
? rowA[indexKey] - rowB[indexKey]
|
||||
: rowB[indexKey] - rowA[indexKey]
|
||||
})
|
||||
}
|
||||
|
||||
@@ -97,12 +94,14 @@ function remove (a, b) {
|
||||
|
||||
function clone (a) {
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(a, (key, value) => {
|
||||
if (typeof value === 'function') {
|
||||
return value.toString()
|
||||
}
|
||||
return value
|
||||
}))
|
||||
return JSON.parse(
|
||||
JSON.stringify(a, (key, value) => {
|
||||
if (typeof value === 'function') {
|
||||
return value.toString()
|
||||
}
|
||||
return value
|
||||
})
|
||||
)
|
||||
} catch (e) {
|
||||
return a
|
||||
}
|
||||
@@ -123,14 +122,10 @@ function sum (arr) {
|
||||
}
|
||||
|
||||
function makeTemplateComponent (compClass) {
|
||||
return ({children, className, ...rest}) => (
|
||||
<div
|
||||
className={classnames(compClass, className)}
|
||||
{...rest}
|
||||
>
|
||||
return ({ children, className, ...rest }) =>
|
||||
<div className={classnames(compClass, className)} {...rest}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function groupBy (xs, key) {
|
||||
@@ -152,10 +147,10 @@ function isArray (a) {
|
||||
|
||||
function makePathArray (obj) {
|
||||
return flattenDeep(obj)
|
||||
.join('.')
|
||||
.replace('[', '.')
|
||||
.replace(']', '')
|
||||
.split('.')
|
||||
.join('.')
|
||||
.replace('[', '.')
|
||||
.replace(']', '')
|
||||
.split('.')
|
||||
}
|
||||
|
||||
function flattenDeep (arr, newArr = []) {
|
||||
@@ -169,18 +164,22 @@ function flattenDeep (arr, newArr = []) {
|
||||
return newArr
|
||||
}
|
||||
|
||||
function splitProps ({className, style, ...rest}) {
|
||||
function splitProps ({ className, style, ...rest }) {
|
||||
return {
|
||||
className,
|
||||
style,
|
||||
rest
|
||||
rest,
|
||||
}
|
||||
}
|
||||
|
||||
function compactObject (obj) {
|
||||
const newObj = {}
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key) && obj[key] !== undefined && typeof obj[key] !== 'undefined') {
|
||||
if (
|
||||
obj.hasOwnProperty(key) &&
|
||||
obj[key] !== undefined &&
|
||||
typeof obj[key] !== 'undefined'
|
||||
) {
|
||||
newObj[key] = obj[key]
|
||||
}
|
||||
}
|
||||
@@ -192,11 +191,9 @@ function isSortingDesc (d) {
|
||||
}
|
||||
|
||||
function normalizeComponent (Comp, params = {}, fallback = Comp) {
|
||||
return typeof Comp === 'function' ? (
|
||||
Object.getPrototypeOf(Comp).isReactComponent ? (
|
||||
<Comp
|
||||
{...params}
|
||||
/>
|
||||
) : Comp(params)
|
||||
) : fallback
|
||||
return typeof Comp === 'function'
|
||||
? Object.getPrototypeOf(Comp).isReactComponent
|
||||
? <Comp {...params} />
|
||||
: Comp(params)
|
||||
: fallback
|
||||
}
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
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}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
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 />
|
||||
@@ -1,106 +0,0 @@
|
||||
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
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
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: () => <div style={{textAlign: 'center'}}>First Name</div>
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
footer: () => <div style={{textAlign: 'center'}}>Last Name</div>
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
footer: () => <div style={{textAlign: 'center'}}>Age</div>
|
||||
}]
|
||||
}, {
|
||||
header: 'Expand',
|
||||
columns: [{
|
||||
expander: true,
|
||||
header: () => (<strong>More</strong>),
|
||||
width: 65,
|
||||
render: ({isExpanded, ...rest}) => (
|
||||
<div>
|
||||
{isExpanded ? <span>⊙</span> : <span>⊕</span>}
|
||||
</div>
|
||||
),
|
||||
style: {cursor: 'pointer', fontSize: 25, padding: '0', textAlign: 'center', userSelect: 'none'},
|
||||
footer: () => <span>♥</span>
|
||||
}]
|
||||
}]
|
||||
|
||||
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',
|
||||
footer: () => <div style={{textAlign: 'center'}}>First Name</div>
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
footer: () => <div style={{textAlign: 'center'}}>Last Name</div>
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age',
|
||||
footer: () => <div style={{textAlign: 'center'}}>Age</div>
|
||||
}]
|
||||
}, {
|
||||
header: 'Expand',
|
||||
columns: [{
|
||||
expander: true,
|
||||
header: () => (<strong>More</strong>),
|
||||
width: 65,
|
||||
render: ({isExpanded, ...rest}) => (
|
||||
<div>
|
||||
{isExpanded ? <span>⊙</span> : <span>⊕</span>}
|
||||
</div>
|
||||
),
|
||||
style: {cursor: 'pointer', fontSize: 25, padding: '0', textAlign: 'center', userSelect: 'none'},
|
||||
footer: () => <span>♥</span>
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
SubComponent={() => <span>Hello</span>}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
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}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
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
|
||||
}]}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
class MyTable extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.renderEditable = this.renderEditable.bind(this);
|
||||
|
||||
this.state = {
|
||||
data: [
|
||||
{ firstName: 'Lucy', lastName: 'Marks' },
|
||||
{ firstName: 'Bejamin', lastName: 'Pike' }
|
||||
]
|
||||
};
|
||||
|
||||
this.columns = [
|
||||
{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
render: this.renderEditable
|
||||
},
|
||||
{
|
||||
header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
render: this.renderEditable
|
||||
},
|
||||
{
|
||||
header: 'Full Name',
|
||||
id: 'full',
|
||||
accessor: d => d.firstName + ' ' + d.lastName
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
renderEditable(cellInfo) {
|
||||
return (<div style={{ backgroundColor: '#fafafa' }} contentEditable suppressContentEditableWarning onBlur={(e) => {
|
||||
const data = [...this.state.data];
|
||||
data[cellInfo.index][cellInfo.column.id] = e.target.textContent;
|
||||
this.setState({data: data});
|
||||
}}>{this.state.data[cellInfo.index][cellInfo.column.id]}</div>);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<ReactTable
|
||||
data={this.state.data}
|
||||
columns={this.columns}
|
||||
defaultPageSize={2}
|
||||
showPageSizeOptions={false}
|
||||
showPagination={false}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
|
||||
function getCode() {
|
||||
return `
|
||||
class MyTable extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.renderEditable = this.renderEditable.bind(this);
|
||||
|
||||
this.state = {
|
||||
data: [
|
||||
{ firstName: 'Lucy', lastName: 'Marks' },
|
||||
{ firstName: 'Bejamin', lastName: 'Pike' }
|
||||
]
|
||||
};
|
||||
|
||||
this.columns = [
|
||||
{
|
||||
header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
render: this.renderEditable
|
||||
},
|
||||
{
|
||||
header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
render: this.renderEditable
|
||||
},
|
||||
{
|
||||
header: 'Full Name',
|
||||
id: 'full',
|
||||
accessor: d => d.firstName + ' ' + d.lastName
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
renderEditable(cellInfo) {
|
||||
return (<div style={{ backgroundColor: '#fafafa' }} contentEditable suppressContentEditableWarning onBlur={(e) => {
|
||||
const data = [...this.state.data];
|
||||
data[cellInfo.index][cellInfo.column.id] = e.target.textContent;
|
||||
this.setState({data: data});
|
||||
}}>{this.state.data[cellInfo.index][cellInfo.column.id]}</div>);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<ReactTable
|
||||
data={this.state.data}
|
||||
columns={this.columns}
|
||||
defaultPageSize={2}
|
||||
showPageSizeOptions={false}
|
||||
showPagination={false}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export default () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>First two columns are editable just by clicking into them using the <code>contentEditable</code> attribute. Last column (Full Name) is computed from the first two.</p>
|
||||
<div className='table-wrap' style={{marginBottom: '20px'}}>
|
||||
<MyTable />
|
||||
</div>
|
||||
|
||||
<CodeHighlight>{() => getCode()}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
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}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
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}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
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(100000), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numLen: 0 }),
|
||||
lastName: namor.generate({ words: 1, numLen: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
visits: Math.floor(Math.random() * 100)
|
||||
}
|
||||
})
|
||||
|
||||
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',
|
||||
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)
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
className='-striped -highlight'
|
||||
defaultPageSize={10}
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
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>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</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',
|
||||
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)
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
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>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
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(10000), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numLen: 0 }),
|
||||
lastName: namor.generate({ words: 1, numLen: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
visits: Math.floor(Math.random() * 100)
|
||||
}
|
||||
})
|
||||
|
||||
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',
|
||||
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)
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
className='-striped -highlight'
|
||||
defaultPageSize={10}
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
/>
|
||||
</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',
|
||||
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)
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
className='-striped -highlight'
|
||||
defaultPageSize={10}
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
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(1000), d => {
|
||||
return {
|
||||
firstName: namor.generate({words: 1, numLen: 0}),
|
||||
lastName: namor.generate({words: 1, numLen: 0}),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
visits: Math.floor(Math.random() * 100)
|
||||
}
|
||||
})
|
||||
|
||||
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',
|
||||
aggregate: vals => {
|
||||
return _.round(_.mean(vals))
|
||||
},
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? `${row.value} (avg)` : row.value}</span>
|
||||
}
|
||||
}, {
|
||||
header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals),
|
||||
hideFilter: true
|
||||
}]
|
||||
}, {
|
||||
header: () => <strong>Overriden Pivot Column Header Group</strong>,
|
||||
expander: true,
|
||||
minWidth: 200,
|
||||
pivotRender: ({value}) => <span style={{color: 'darkred'}}>{value}</span>,
|
||||
footer: () => <div style={{textAlign: 'center'}}><strong>Overriden Pivot Column Footer</strong></div>
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
defaultSorting={[{id: 'firstName', desc: false}, {id: 'lastName', desc: true}]}
|
||||
collapseOnSortingChange={false}
|
||||
showFilters={true}
|
||||
ExpanderComponent={({isExpanded, ...rest}) => (
|
||||
isExpanded ? <span> ➘ </span> : <span> ➙ </span>
|
||||
)}
|
||||
PivotValueComponent={ ({subRows, value}) => (
|
||||
<span><span style={{color: 'darkred'}}>{value}</span> {subRows && `(${subRows.length} Last Names)`}</span>
|
||||
)}
|
||||
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.filter(x => !x.expander)}
|
||||
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>{() => 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',
|
||||
aggregate: vals => {
|
||||
return _.round(_.mean(vals))
|
||||
},
|
||||
render: row => {
|
||||
return <span>{row.aggregated ? \`\${row.value} (avg)\` : row.value}</span>
|
||||
}
|
||||
}, {
|
||||
header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals),
|
||||
hideFilter: true
|
||||
}]
|
||||
}, {
|
||||
header: () => <strong>Overriden Pivot Column Header Group</strong>,
|
||||
expander: true,
|
||||
minWidth: 200,
|
||||
pivotRender: ({value}) => <span style={{color: 'darkred'}}>{value}</span>,
|
||||
footer: () => <div style={{textAlign: 'center'}}><strong>Overriden Pivot Column Footer</strong></div>
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
defaultSorting={[{id: 'firstName', desc: false}, {id: 'lastName', desc: true}]}
|
||||
collapseOnSortingChange={false}
|
||||
showFilters={true}
|
||||
ExpanderComponent={({isExpanded, ...rest}) => (
|
||||
isExpanded ? <span> ➘ </span> : <span> ➙ </span>
|
||||
)}
|
||||
PivotValueComponent={ ({subRows, value}) => (
|
||||
<span><span style={{color: 'darkred'}}>{value}</span> {subRows && \`(\${subRows.length} Last Names)\`}</span>
|
||||
)}
|
||||
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.filter(x => !x.expander)}
|
||||
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>{() => getCode()}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
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(1000), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numLen: 0 }),
|
||||
lastName: namor.generate({ words: 1, numLen: 0 }),
|
||||
age: Math.floor(Math.random() * 30),
|
||||
visits: Math.floor(Math.random() * 100)
|
||||
}
|
||||
})
|
||||
|
||||
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',
|
||||
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
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
showFilters={true}
|
||||
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>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</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',
|
||||
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
|
||||
}]
|
||||
}]
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
className='-striped -highlight'
|
||||
pivotBy={['firstName', 'lastName']}
|
||||
showFilters={true}
|
||||
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>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
const rawData = _.map(_.range(3424), d => {
|
||||
return {
|
||||
firstName: namor.generate({ words: 1, numLen: 0 }),
|
||||
lastName: namor.generate({ words: 1, numLen: 0 }),
|
||||
age: Math.floor(Math.random() * 30)
|
||||
}
|
||||
})
|
||||
|
||||
// Now let's mock the server. It's job is simple: use the table model to sort and return the page data
|
||||
const requestData = (pageSize, page, sorting, 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
|
||||
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.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(filteredData.length / pageSize)
|
||||
}
|
||||
|
||||
// Here we'll simulate a server response with 500ms of delay.
|
||||
setTimeout(() => resolve(res), 500)
|
||||
})
|
||||
}
|
||||
|
||||
const ServerSide = React.createClass({
|
||||
getInitialState () {
|
||||
// To handle our data server-side, we need a few things in the state to help us out:
|
||||
return {
|
||||
data: [],
|
||||
pages: null,
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
fetchData (state, instance) {
|
||||
// Whenever the table model changes, or the user sorts or changes pages, this method gets called and passed the current table model.
|
||||
// You can set the `loading` prop of the table to true to use the built-in one or show you're own loading bar if you want.
|
||||
this.setState({loading: true})
|
||||
// Request the data however you want. Here, we'll use our mocked service we created earlier
|
||||
requestData(state.pageSize, state.page, state.sorting, 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,
|
||||
pages: res.pages,
|
||||
loading: false
|
||||
})
|
||||
})
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
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
|
||||
/>
|
||||
</div>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<br />
|
||||
<em>Tip: Hold shift when sorting to multi-sort!</em>
|
||||
</div>
|
||||
<CodeHighlight>{() => getCode()}</CodeHighlight>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default () => (
|
||||
<ServerSide />
|
||||
)
|
||||
|
||||
function getCode () {
|
||||
return `
|
||||
import ReactTable from 'react-table'
|
||||
import Axios from 'axios'
|
||||
|
||||
export default React.createClass({
|
||||
getInitialState () {
|
||||
// To handle our data server-side, we need to keep track of our table state
|
||||
return {
|
||||
data: [],
|
||||
pages: null,
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
fetchData (state, instance) {
|
||||
// Whenever the table model changes (sorting, pagination, etc), this method gets called and passed the current table model.
|
||||
// You can set the 'loading' prop of the table to true to use the built-in loading notice, or show you're own loading bar if you want.
|
||||
this.setState({loading: true})
|
||||
// Request the data from a server however you want! Be sure to send the bits of the table model that it may neeed.
|
||||
Axios.post('mysite.com/data', {
|
||||
pageSize: state.pageSize,
|
||||
page: state.page,
|
||||
sorting: state.sorting,
|
||||
filtering: state.filtering
|
||||
})
|
||||
.then((res) => {
|
||||
// Now update your state!
|
||||
this.setState({
|
||||
data: res.rows,
|
||||
pages: res.pages,
|
||||
loading: false
|
||||
})
|
||||
})
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<ReactTable
|
||||
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
|
||||
/>
|
||||
}
|
||||
})
|
||||
`
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
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}
|
||||
/>
|
||||
</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
|
||||
className='-striped -highlight'
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
/>
|
||||
)
|
||||
`
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import namor from 'namor'
|
||||
|
||||
import CodeHighlight from './components/codeHighlight'
|
||||
import ReactTable from '../src/index'
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
}]
|
||||
}]
|
||||
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
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'
|
||||
}, {
|
||||
header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
}]
|
||||
}, {
|
||||
header: 'Info',
|
||||
columns: [{
|
||||
header: 'Age',
|
||||
accessor: 'age'
|
||||
}]
|
||||
}]
|
||||
|
||||
export default (
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
{...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 () => <SubComponents/>
|
||||
Reference in New Issue
Block a user