Compare commits

..
3 Commits
Author SHA1 Message Date
Tanner Linsley b629d3ebbb 5.3.0 2017-02-16 10:41:46 -07:00
Tanner Linsley 1ebea1cac8 Added more customization for functional rendering 2017-02-16 10:41:35 -07:00
Tanner Linsley b16eee1bba Added support for Column Footers 2017-02-16 09:29:12 -07:00
22 changed files with 903 additions and 461 deletions
+2
View File
@@ -23,6 +23,7 @@ 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'
//
configure(() => {
storiesOf('1. Docs')
@@ -51,4 +52,5 @@ configure(() => {
.add('Functional Rendering', FunctionalRendering)
.add('Custom Expander Position', CustomExpanderPosition)
.add('Custom "No Data" Text', NoDataText)
.add('Footers', Footers)
}, module)
+4
View File
@@ -1,3 +1,7 @@
body{
padding: 20px;
}
strong {
font-weight: bold
}
+81 -35
View File
@@ -24,6 +24,9 @@
<a href="https://twitter.com/tannerlinsley" target="\_parent">
<img alt="" src="https://img.shields.io/twitter/follow/tannerlinsley.svg?style=social&label=Follow" />
</a>
<a href="https://cash.me/$tannerlinsley" target="\_parent">
<img alt="" src="https://img.shields.io/badge/%24-Donate-brightgreen.svg" />
</a>
## Features
@@ -185,6 +188,9 @@ These are all of the available props (and their default values) for the main `<R
getTrProps: () => ({}),
getThProps: () => ({}),
getTdProps: () => ({}),
getTfootProps: () => ({}),
getTfootTrProps: () => ({}),
getTfootThProps: () => ({}),
getPaginationProps: () => ({}),
getLoadingProps: () => ({}),
getNoDataProps: () => ({}),
@@ -204,6 +210,11 @@ These are all of the available props (and their default values) for the main `<R
headerClassName: '',
headerStyle: {},
getHeaderProps: () => ({})
// Footers only
footer: undefined,
footerClassName: '',
footerStyle: {},
getFooterProps: () => ({})
},
// Text
@@ -268,13 +279,20 @@ Or just define them as props
// viewIndex == the index of the row in the current page
// Header & HeaderGroup Options
header: 'Header Name' or JSX eg. ({data, column}) => <div>Header Name</div>,
header: 'Header Name', a function that returns a primitive, or JSX / React Component eg. ({data, column}) => <div>Header Name</div>,
headerClassName: '', // Set the classname of the `th` element of the column
headerStyle: {}, // Set the style of the `th` element of the column
getHeaderProps: (state, rowInfo, column, instance) => ({}) // a function that returns props to decorate the `th` element of the column
// Header Groups only
columns: [...] // See Header Groups section below
// Footer
footer: 'Header Name' or JSX eg. ({data, column}) => <div>Header 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
}]
```
@@ -297,8 +315,8 @@ const columns = [{
}]
```
## Custom Cell and Header Rendering
You can use any react component or JSX to display column headers or cells. Any component you use will be passed the following props:
## Custom Cell, Header and Footer Rendering
You can use any react component or JSX to display content in column headers, cells and footers. Any component you use will be passed the following props (if available):
- `row` - Original row from your data
- `rowValues` - The post-accessed values from the original row
- `index` - The index of the row
@@ -340,17 +358,19 @@ const columns = [{
```
## Styles
React-table ships with a minimal and clean stylesheet to get you on your feet quickly. It's located at `react-table/react-table.css`.
- React-table ships with a minimal and clean stylesheet to get you on your feet quickly.
- The stylesheet is located at `react-table/react-table.css`.
- There are countless ways to import a stylesheet. If you have questions on how to do so, consult the documentation of your build system.
#### Built-in Styles
#### Classes
- Adding a `-striped` className to ReactTable will slightly color odd numbered rows for legibility
- Adding a `-highlight` className to ReactTable will highlight any row as you hover over it
#### CSS Styles
#### CSS
We think the default styles looks great! But, if you prefer a more custom look, all of the included styles are easily overridable. Every single component contains a unique class that makes it super easy to customize. Just go for it!
#### JS Styles
Every single react-table element and `get[ComponentName]Props` callback support classes (powered by `classname` and js styles.
Every single react-table element and `get[ComponentName]Props` callback supports `classname` and `style` props.
## Custom Props
@@ -561,51 +581,77 @@ Here are the props and their corresponding callbacks that control the state of t
```
## Functional Rendering
Possibly one of the coolest features of React-Table is its ability to expose internal state for custom render logic. The easiest way to do this is to optionally pass a function as a child of `<ReactTable />`.
Possibly one of the coolest features of React-Table is its ability to expose internal components and state for custom render logic. The easiest way to do this is to pass a function as the child of `<ReactTable />`.
The function you pass will be called with the following items:
- Fully-resolved state of the table
- The standard table generator
- The standard table component (including individual partials as properties of this component)
- The instance of the component
You can then return any JSX or react you want! This turns out to be perfect for:
- Accessing the internal state of the table before rendering the table
- Decorating the table with more UI
- Building your own 100% custom display logic, while utilizing the state and methods of the table component
- Reordering the internal components of the table
- Accessing the internal state of the table without a `ref`
- Decorating the table or extending it with your own UI
- Building your own custom display logic
Example:
Custom pagination order:
```javascript
<ReactTable
columns={columns}
data={data}
...
columns={columns}
>
{(state, makeTable, instance) => {
// Now you have full access to the state of the table!
state.decoratedColumns === [...] // all of the columns (with id's and meta)
state.visibleColumns === [...] // all of the columns (with id's and meta)
state.visibleColumns === [...] // all of the columns (with id's and meta)
// etc.
// `makeTable` is a function that returns the standard table markup
return makeTable()
// So add some decoration!
{(state, {
Root,
Table,
HeaderGroups,
Headers,
Rows,
Footers,
Pagination,
NoData,
Loading
}, instance) => {
return (
<div>
<customPivotBySelect />
<customColumnHideShow />
<customAnything />
{makeTable()}
</div>
<Root>
<Pagination />
<Table>
<HeaderGroups />
<Headers />
<Rows />
<Footers />
</Table>
<NoData />
<Loading />
</Root>
)
// The possibilities are endless!!!
}}
</ReactTable>
```
Accessing internal state and wrapping with more UI:
```javascript
<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>
```
The possibilities are endless!
## Multi-Sort
When clicking on a column header, hold shift to multi-sort! You can toggle `ascending` `descending` and `none` for multi-sort columns. Clicking on a header without holding shift will clear the multi-sort and replace it with the single sort of that column. It's quite handy!
+1 -1
View File
@@ -16,7 +16,7 @@
<body>
<div id="root"></div>
<div id="error-display"></div>
<script src="static/preview.1dfdb2ebd0085aa535c8.bundle.js"></script>
<script src="static/preview.fe48f7ea9cf295bed07c.bundle.js"></script>
</body>
</html>
+1 -1
View File
@@ -38,7 +38,7 @@
</head>
<body style="margin: 0;">
<div id="root"></div>
<script src="static/manager.d3baea6e6f08c3c0279c.bundle.js"></script>
<script src="static/manager.3f690da6fe3fd9729ee0.bundle.js"></script>
</body>
</html>
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"version":3,"file":"static/manager.3f690da6fe3fd9729ee0.bundle.js","sources":["webpack:///static/manager.3f690da6fe3fd9729ee0.bundle.js"],"mappings":"AAAA;AAkuDA;AA84DA;AA28DA;AA00DA;AAsyEA;AA89CA;AA+rDA;AAsiDA;AAg6DA;AA2nDA;AA++CA;AAkvDA;AAsnEA;AA2oDA;AAivCA;AA+nDA;AAkpDA;AA6lEA;AAs4DA;AAquDA;AA+pDA;AAoxDA;AAsrDA;AA4yDA;AA88GA;AAj6CA;AAopGA;AAsuFA;AA+zEA;AAtMA;AAizEA","sourceRoot":""}
@@ -1 +0,0 @@
{"version":3,"file":"static/manager.d3baea6e6f08c3c0279c.bundle.js","sources":["webpack:///static/manager.d3baea6e6f08c3c0279c.bundle.js"],"mappings":"AAAA;AAkuDA;AA84DA;AA28DA;AA00DA;AAsyEA;AA89CA;AA8rDA;AAsiDA;AAg6DA;AA2nDA;AA++CA;AAkvDA;AAsnEA;AA2oDA;AAivCA;AA+nDA;AAkpDA;AA6lEA;AAs4DA;AAquDA;AA+pDA;AAqxDA;AAsrDA;AAyyDA;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.1dfdb2ebd0085aa535c8.bundle.js","sources":["webpack:///static/preview.1dfdb2ebd0085aa535c8.bundle.js"],"mappings":"AAAA;AAkuDA;AAu+CA;AA0+FA;AAmpFA;AAkxLA;AA7sBA;AAojOA;AAkjEA;AAivDA;AAy7DA;AAqlDA;AAmvDA;AAu8CA;AA6+DA;AAwlDA;AA2+CA;AAimDA;AA29DA;AA04DA;AAsxCA;AAynDA;AAurDA;AAgiEA;AA03DA;AAwwCA;AA8tDA;AA02FA;AA+tGA;AAy+CA;AAuzCA;AAkuCA;AAikDA;AA8dA;AA+jFA","sourceRoot":""}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"version":3,"file":"static/preview.fe48f7ea9cf295bed07c.bundle.js","sources":["webpack:///static/preview.fe48f7ea9cf295bed07c.bundle.js"],"mappings":"AAAA;AAkuDA;AA48CA;AA0sGA;AAigFA;AAyiNA;AA0rFA;AAk4FA;AAsiEA;AAwuDA;AAu9DA;AA6gDA;AAixDA;AA69CA;AAk+DA;AAqoDA;AAs9CA;AAgoDA;AAwsEA;AAuoDA;AAmvCA;AAioDA;AAkpDA;AA6lEA;AAs4DA;AAuyDA;AAokDA;AA0mFA;AAyiGA;AA2gDA;AAm2CA;AAy/BA;AA4uDA;AAy4BA;AA0XA;AA6zEA","sourceRoot":""}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "react-table",
"version": "5.2.0",
"version": "5.3.0",
"description": "A fast, lightweight, opinionated table and datagrid built on React",
"license": "MIT",
"homepage": "https://github.com/tannerlinsley/react-table#readme",
Binary file not shown.
+152
View File
@@ -0,0 +1,152 @@
import React from 'react'
import classnames from 'classnames'
//
import _ from './utils'
import Pagination from './pagination'
const emptyObj = () => ({})
export default {
// General
data: [],
loading: false,
showPagination: true,
showPageSizeOptions: true,
pageSizeOptions: [5, 10, 20, 25, 50, 100],
defaultPageSize: 20,
showPageJump: true,
expanderColumnWidth: 35,
collapseOnSortingChange: false,
collapseOnPageChange: true,
freezeWhenExpanded: false,
defaultSorting: [],
// Controlled State Overrides
// page: undefined,
// pageSize: undefined,
// sorting: undefined,
// Controlled State Callbacks
onExpandSubComponent: undefined,
onPageChange: undefined,
onPageSizeChange: undefined,
onSortingChange: undefined,
// Pivoting
pivotBy: undefined,
pivotColumnWidth: 200,
pivotValKey: '_pivotVal',
pivotIDKey: '_pivotID',
subRowsKey: '_subRows',
// Pivoting State Overrides
// expandedRows: {},
// Pivoting State Callbacks
onExpandRow: undefined,
// General Callbacks
onChange: () => null,
// Classes
className: '',
style: {},
// Component decorators
getProps: emptyObj,
getTableProps: emptyObj,
getTheadGroupProps: emptyObj,
getTheadGroupTrProps: emptyObj,
getTheadGroupThProps: emptyObj,
getTheadProps: emptyObj,
getTheadTrProps: emptyObj,
getTheadThProps: emptyObj,
getTbodyProps: emptyObj,
getTrGroupProps: emptyObj,
getTrProps: emptyObj,
getTdProps: emptyObj,
getTfootProps: emptyObj,
getTfootTrProps: emptyObj,
getTfootTdProps: emptyObj,
getPaginationProps: emptyObj,
getLoadingProps: emptyObj,
getNoDataProps: emptyObj,
// Global Column Defaults
column: {
sortable: true,
show: true,
minWidth: 100,
// Cells only
render: undefined,
className: '',
style: {},
getProps: emptyObj,
// Headers only
header: undefined,
headerClassName: '',
headerStyle: {},
getHeaderProps: emptyObj,
// Footers only
footer: undefined,
footerClassName: '',
footerStyle: {},
getFooterProps: emptyObj
},
// Text
previousText: 'Previous',
nextText: 'Next',
loadingText: 'Loading...',
noDataText: 'No rows found',
pageText: 'Page',
ofText: 'of',
rowsText: 'rows',
// Components
TableComponent: _.makeTemplateComponent('rt-table'),
TheadComponent: _.makeTemplateComponent('rt-thead'),
TbodyComponent: _.makeTemplateComponent('rt-tbody'),
TrGroupComponent: _.makeTemplateComponent('rt-tr-group'),
TrComponent: _.makeTemplateComponent('rt-tr'),
ThComponent: ({toggleSort, className, children, ...rest}) => {
return (
<div
className={classnames(className, 'rt-th')}
onClick={e => {
toggleSort && toggleSort(e)
}}
{...rest}
>
{children}
</div>
)
},
TdComponent: _.makeTemplateComponent('rt-td'),
TfootComponent: _.makeTemplateComponent('rt-tfoot'),
ExpanderComponent: ({isExpanded, ...rest}) => {
return (
<div
className={classnames('rt-expander', isExpanded && '-open')}
{...rest}
>&bull;</div>
)
},
PaginationComponent: Pagination,
PreviousComponent: undefined,
NextComponent: undefined,
LoadingComponent: ({className, loading, loadingText, ...rest}) => (
<div className={classnames(
'-loading',
{'-active': loading},
className
)}
{...rest}
>
<div className='-loading-inner'>
{loadingText}
</div>
</div>
),
NoDataComponent: _.makeTemplateComponent('rt-noData')
}
+219 -314
View File
@@ -2,257 +2,12 @@ import React from 'react'
import classnames from 'classnames'
//
import _ from './utils'
import componentMethods from './componentMethods'
import Pagination from './pagination'
const emptyObj = () => ({})
export const ReactTableDefaults = {
// General
data: [],
loading: false,
showPagination: true,
showPageSizeOptions: true,
pageSizeOptions: [5, 10, 20, 25, 50, 100],
defaultPageSize: 20,
showPageJump: true,
expanderColumnWidth: 35,
collapseOnSortingChange: false,
collapseOnPageChange: true,
freezeWhenExpanded: false,
defaultSorting: [],
// Controlled State Overrides
// page: undefined,
// pageSize: undefined,
// sorting: undefined,
// Controlled State Callbacks
onExpandSubComponent: undefined,
onPageChange: undefined,
onPageSizeChange: undefined,
onSortingChange: undefined,
// Pivoting
pivotBy: undefined,
pivotColumnWidth: 200,
pivotValKey: '_pivotVal',
pivotIDKey: '_pivotID',
subRowsKey: '_subRows',
// Pivoting State Overrides
// expandedRows: {},
// Pivoting State Callbacks
onExpandRow: undefined,
// General Callbacks
onChange: () => null,
// Classes
className: '',
style: {},
// Component decorators
getProps: emptyObj,
getTableProps: emptyObj,
getTheadGroupProps: emptyObj,
getTheadGroupTrProps: emptyObj,
getTheadGroupThProps: emptyObj,
getTheadProps: emptyObj,
getTheadTrProps: emptyObj,
getTheadThProps: emptyObj,
getTbodyProps: emptyObj,
getTrGroupProps: emptyObj,
getTrProps: emptyObj,
getThProps: emptyObj,
getTdProps: emptyObj,
getPaginationProps: emptyObj,
getLoadingProps: emptyObj,
getNoDataProps: emptyObj,
// Global Column Defaults
column: {
sortable: true,
show: true,
minWidth: 100,
// Cells only
render: undefined,
className: '',
style: {},
getProps: () => ({}),
// Headers only
header: undefined,
headerClassName: '',
headerStyle: {},
getHeaderProps: () => ({})
},
// Text
previousText: 'Previous',
nextText: 'Next',
loadingText: 'Loading...',
noDataText: 'No rows found',
pageText: 'Page',
ofText: 'of',
rowsText: 'rows',
// Components
TableComponent: _.makeTemplateComponent('rt-table'),
TheadComponent: _.makeTemplateComponent('rt-thead'),
TbodyComponent: _.makeTemplateComponent('rt-tbody'),
TrGroupComponent: _.makeTemplateComponent('rt-tr-group'),
TrComponent: _.makeTemplateComponent('rt-tr'),
ThComponent: ({toggleSort, className, children, ...rest}) => {
return (
<div
className={classnames(className, 'rt-th')}
onClick={e => {
toggleSort && toggleSort(e)
}}
{...rest}
>
{children}
</div>
)
},
TdComponent: _.makeTemplateComponent('rt-td'),
ExpanderComponent: ({isExpanded, ...rest}) => {
return (
<div
className={classnames('rt-expander', isExpanded && '-open')}
{...rest}
>&bull;</div>
)
},
PaginationComponent: Pagination,
PreviousComponent: undefined,
NextComponent: undefined,
LoadingComponent: ({className, loading, loadingText, ...rest}) => (
<div className={classnames(
'-loading',
{'-active': loading},
className
)}
{...rest}
>
<div className='-loading-inner'>
{loadingText}
</div>
</div>
),
NoDataComponent: _.makeTemplateComponent('rt-noData')
}
import lifecycle from './lifecycle'
import methods from './methods'
export default React.createClass({
getDefaultProps () {
return ReactTableDefaults
},
getInitialState () {
return {
page: 0,
pageSize: this.props.defaultPageSize || 10,
sorting: this.props.defaultSorting,
expandedRows: {}
}
},
getResolvedState (props, state) {
const resolvedState = {
..._.compactObject(this.state),
..._.compactObject(state),
..._.compactObject(this.props),
..._.compactObject(props)
}
return resolvedState
},
componentWillMount () {
this.setStateWithData(this.getDataModel())
},
componentDidMount () {
this.fireOnChange()
},
componentWillReceiveProps (nextProps, nextState) {
const oldState = this.getResolvedState()
const newState = this.getResolvedState(nextProps, nextState)
// Props that trigger a data update
if (
oldState.data !== newState.data ||
oldState.columns !== newState.columns ||
oldState.pivotBy !== newState.pivotBy ||
oldState.sorting !== newState.sorting
) {
this.setStateWithData(this.getDataModel(nextProps, nextState))
}
},
setStateWithData (newState, cb) {
const oldState = this.getResolvedState()
const newResolvedState = this.getResolvedState({}, newState)
const { freezeWhenExpanded } = newResolvedState
// Default to unfrozen state
newResolvedState.frozen = false
// If freezeWhenExpanded is set, check for frozen conditions
if (freezeWhenExpanded) {
// if any rows are expanded, freeze the existing data and sorting
const keys = Object.keys(newResolvedState.expandedRows)
for (var i = 0; i < keys.length; i++) {
if (newResolvedState.expandedRows[keys[i]]) {
newResolvedState.frozen = true
break
}
}
}
// If the data isn't frozen and either the data or
// sorting model has changed, update the data
if (
(oldState.frozen && !newResolvedState.frozen) ||
oldState.sorting !== newResolvedState.sorting ||
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData)
) {
// Handle collapseOnSortingChange & collapseOnPageChange
if (
(oldState.sorting !== newResolvedState.sorting && this.props.collapseOnSortingChange) ||
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData && this.props.collapseOnPageChange)
) {
newResolvedState.expandedRows = {}
}
Object.assign(newResolvedState, this.getSortedData(newResolvedState))
}
// Calculate pageSize all the time
if (newResolvedState.resolvedData) {
newResolvedState.pages = newResolvedState.manual ? newResolvedState.pages : Math.ceil(newResolvedState.resolvedData.length / newResolvedState.pageSize)
}
return this.setState(newResolvedState, cb)
},
shouldComponentUpdate (nextProps, nextState) {
const oldState = this.getResolvedState()
const newState = this.getResolvedState(nextProps, nextState)
// State changes that trigger a render
if (
oldState.sortedData !== newState.sortedData ||
oldState.page !== newState.page ||
oldState.pageSize !== newState.pageSize ||
oldState.expandedRows !== newState.expandedRows
) {
return true
}
return false
},
...lifecycle,
...methods,
render () {
const resolvedState = this.getResolvedState()
@@ -271,8 +26,10 @@ export default React.createClass({
getTbodyProps,
getTrGroupProps,
getTrProps,
getThProps,
getTdProps,
getTfootProps,
getTfootTrProps,
getTfootTdProps,
getPaginationProps,
getLoadingProps,
getNoDataProps,
@@ -300,6 +57,7 @@ export default React.createClass({
TrComponent,
ThComponent,
TdComponent,
TfootComponent,
ExpanderComponent,
PaginationComponent,
LoadingComponent,
@@ -314,9 +72,6 @@ export default React.createClass({
sortedData
} = resolvedState
// Determine the flex percentage for each column
// const columnPercentage = 100 / allVisibleColumns.length
// Pagination
const startRow = pageSize * page
const endRow = startRow + pageSize
@@ -326,6 +81,8 @@ export default React.createClass({
: minRows ? _.range(Math.max(minRows - pageRows.length, 0))
: []
const hasColumnFooter = allVisibleColumns.some(d => d.footer)
const recurseRowsViewIndex = (rows, path = [], index = -1) => {
rows.forEach((row, i) => {
index++
@@ -354,6 +111,7 @@ export default React.createClass({
pageRows,
minRows,
padRows,
hasColumnFooter,
canPrevious,
canNext,
rowMinWidth
@@ -765,7 +523,7 @@ export default React.createClass({
const makePadRow = (row, i) => {
const trGroupProps = getTrGroupProps(finalState, undefined, undefined, this)
const trProps = _.splitProps(getTrProps(finalState, undefined, undefined, this))
const thProps = _.splitProps(getThProps(finalState, undefined, undefined, this))
const tdProps = _.splitProps(getTdProps(finalState, undefined, undefined, this))
return (
<TrGroupComponent
key={i}
@@ -782,14 +540,14 @@ export default React.createClass({
<ThComponent
className={classnames(
'rt-expander-header',
thProps.className
tdProps.className
)}
style={{
...thProps.style,
...tdProps.style,
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
}}
{...thProps.rest}
{...tdProps.rest}
/>
)}
{allVisibleColumns.map((column, i2) => {
@@ -835,13 +593,123 @@ export default React.createClass({
)
}
const makeTable = () => {
const rootProps = _.splitProps(getProps(finalState, undefined, undefined, this))
const tableProps = _.splitProps(getTableProps(finalState, undefined, undefined, this))
const tBodyProps = _.splitProps(getTbodyProps(finalState, undefined, undefined, this))
const paginationProps = _.splitProps(getPaginationProps(finalState, undefined, undefined, this))
const loadingProps = getLoadingProps(finalState, undefined, undefined, this)
const noDataProps = getNoDataProps(finalState, undefined, undefined, this)
const makeColumnFooters = () => {
const tFootProps = getTfootProps(finalState, undefined, undefined, this)
const tFootTrProps = _.splitProps(getTfootTrProps(finalState, undefined, undefined, this))
return (
<TfootComponent
className={tFootProps.className}
style={{
...tFootProps.style,
minWidth: `${rowMinWidth}px`
}}
{...tFootProps.rest}
>
<TrComponent
className={classnames(
tFootTrProps.className
)}
style={tFootTrProps.style}
{...tFootTrProps.rest}
>
{allVisibleColumns.map((column, i2) => {
const show = typeof column.show === 'function' ? column.show() : column.show
const width = _.getFirstDefined(column.width, column.minWidth)
const maxWidth = _.getFirstDefined(column.width, column.maxWidth)
const tFootTdProps = _.splitProps(getTfootTdProps(finalState, undefined, undefined, this))
const columnProps = _.splitProps(column.getProps(finalState, undefined, column, this))
const columnFooterProps = _.splitProps(column.getFooterProps(finalState, undefined, column, this))
const classes = [
tFootTdProps.className,
column.className,
columnProps.className,
columnFooterProps.className
]
const styles = {
...tFootTdProps.style,
...column.style,
...columnProps.style,
...columnFooterProps.style
}
if (column.expander) {
if (column.pivotColumns) {
return (
<TdComponent
key={i2}
className={classnames(
'rt-pivot',
classes
)}
style={{
...styles,
flex: `${width} 0 auto`,
width: `${width}px`,
maxWidth: `${maxWidth}px`
}}
{...columnProps.rest}
{...tFootTdProps.rest}
{...columnFooterProps.rest}
>
{_.normalizeComponent(column.footer)}
</TdComponent>
)
}
// Return the regular expander cell
return (
<TdComponent
key={i2}
className={classnames(
classes,
{hidden: !show}
)}
style={{
...styles,
flex: `0 0 auto`,
width: `${expanderColumnWidth}px`
}}
/>
)
}
// Return regular cell
return (
<TdComponent
key={i2}
className={classnames(
classes,
!show && 'hidden'
)}
style={{
...styles,
flex: `${width} 0 auto`,
width: `${width}px`,
maxWidth: `${maxWidth}px`
}}
{...columnProps.rest}
{...tFootTdProps.rest}
{...columnFooterProps.rest}
>
{_.normalizeComponent(column.footer)}
</TdComponent>
)
})}
</TrComponent>
</TfootComponent>
)
}
const rootProps = _.splitProps(getProps(finalState, undefined, undefined, this))
const tableProps = _.splitProps(getTableProps(finalState, undefined, undefined, this))
const tBodyProps = _.splitProps(getTbodyProps(finalState, undefined, undefined, this))
const paginationProps = _.splitProps(getPaginationProps(finalState, undefined, undefined, this))
const loadingProps = getLoadingProps(finalState, undefined, undefined, this)
const noDataProps = getNoDataProps(finalState, undefined, undefined, this)
const Root = ({children}) => {
return (
<div
className={classnames(
@@ -855,59 +723,96 @@ export default React.createClass({
}}
{...rootProps.rest}
>
<TableComponent
className={classnames(tableProps.className)}
style={tableProps.style}
{...tableProps.rest}
>
{hasHeaderGroups && makeHeaderGroups()}
{makeHeaders()}
<TbodyComponent
className={classnames(tBodyProps.className)}
style={{
...tBodyProps.style,
minWidth: `${rowMinWidth}px`
}}
{...tBodyProps.rest}
>
{pageRows.map((d, i) => makePageRow(d, i))}
{padRows.map(makePadRow)}
</TbodyComponent>
</TableComponent>
{showPagination && (
<PaginationComponent
{...resolvedState}
pages={pages}
canPrevious={canPrevious}
canNext={canNext}
onPageChange={this.onPageChange}
onPageSizeChange={this.onPageSizeChange}
className={paginationProps.className}
style={paginationProps.style}
{...paginationProps.rest}
/>
)}
{!pageRows.length && (
<NoDataComponent
{...noDataProps}
>
{_.normalizeComponent(noDataText)}
</NoDataComponent>
)}
<LoadingComponent
loading={loading}
loadingText={loadingText}
{...loadingProps}
/>
{children}
</div>
)
}
const Table = ({children}) => (
<TableComponent
className={classnames(tableProps.className)}
style={tableProps.style}
{...tableProps.rest}
>
{children}
</TableComponent>
)
const HeaderGroups = () => hasHeaderGroups ? makeHeaderGroups() : null
const Headers = () => makeHeaders()
const Rows = ({children}) => (
<TbodyComponent
className={classnames(tBodyProps.className)}
style={{
...tBodyProps.style,
minWidth: `${rowMinWidth}px`
}}
{...tBodyProps.rest}
>
{pageRows.map((d, i) => makePageRow(d, i))}
{padRows.map(makePadRow)}
</TbodyComponent>
)
const Footers = () => hasColumnFooter ? makeColumnFooters() : null
const Pagination = () => showPagination ? (
<PaginationComponent
{...resolvedState}
pages={pages}
canPrevious={canPrevious}
canNext={canNext}
onPageChange={this.onPageChange}
onPageSizeChange={this.onPageSizeChange}
className={paginationProps.className}
style={paginationProps.style}
{...paginationProps.rest}
/>
) : null
const NoData = () => !pageRows.length ? (
<NoDataComponent
{...noDataProps}
>
{_.normalizeComponent(noDataText)}
</NoDataComponent>
) : null
const Loading = () => (
<LoadingComponent
loading={loading}
loadingText={loadingText}
{...loadingProps}
/>
)
const StandardTable = () => (
<Root>
<Table>
<HeaderGroups />
<Headers />
<Rows />
<Footers />
</Table>
<Pagination />
<NoData />
<Loading />
</Root>
)
Object.assign(StandardTable, {
Root,
Table,
HeaderGroups,
Headers,
Rows,
Footers,
Pagination,
NoData,
Loading
})
// childProps are optionally passed to a function-as-a-child
return children ? children(finalState, makeTable, this) : makeTable()
},
// Helpers
...componentMethods
return children ? children(finalState, StandardTable, this) : <StandardTable />
}
})
+10 -1
View File
@@ -30,7 +30,6 @@ $expandSize = 7px
.rt-th
.rt-td
color: black
border-right: 1px solid alpha(black, .05)
transition box-shadow .3s $easeOutBack
box-shadow:inset 0 0 0 0 transparent
@@ -100,6 +99,15 @@ $expandSize = 7px
cursor: pointer
&.-open:after
transform: translate(-50%, -50%) rotate(0deg)
.rt-tfoot
display: flex
flex-direction: column
box-shadow: 0 0px 15px 0px alpha(black, .15)
.rt-td
border-right:1px solid alpha(black, .05)
&:last-child
border-right:0
&.-striped
.rt-tr.-odd
@@ -117,6 +125,7 @@ $expandSize = 7px
flex-wrap: wrap
padding: 3px
box-shadow: 0 0px 15px 0px alpha(black, .1)
border-top: 2px solid alpha(black, .1)
.-btn
appearance:none
+96
View File
@@ -0,0 +1,96 @@
import _ from './utils'
import defaultProps from './defaultProps'
export default {
getDefaultProps () {
return defaultProps
},
getInitialState () {
return {
page: 0,
pageSize: this.props.defaultPageSize || 10,
sorting: this.props.defaultSorting,
expandedRows: {}
}
},
getResolvedState (props, state) {
const resolvedState = {
..._.compactObject(this.state),
..._.compactObject(state),
..._.compactObject(this.props),
..._.compactObject(props)
}
return resolvedState
},
componentWillMount () {
this.setStateWithData(this.getDataModel())
},
componentDidMount () {
this.fireOnChange()
},
componentWillReceiveProps (nextProps, nextState) {
const oldState = this.getResolvedState()
const newState = this.getResolvedState(nextProps, nextState)
// Props that trigger a data update
if (
oldState.data !== newState.data ||
oldState.columns !== newState.columns ||
oldState.pivotBy !== newState.pivotBy ||
oldState.sorting !== newState.sorting
) {
this.setStateWithData(this.getDataModel(nextProps, nextState))
}
},
setStateWithData (newState, cb) {
const oldState = this.getResolvedState()
const newResolvedState = this.getResolvedState({}, newState)
const { freezeWhenExpanded } = newResolvedState
// Default to unfrozen state
newResolvedState.frozen = false
// If freezeWhenExpanded is set, check for frozen conditions
if (freezeWhenExpanded) {
// if any rows are expanded, freeze the existing data and sorting
const keys = Object.keys(newResolvedState.expandedRows)
for (var i = 0; i < keys.length; i++) {
if (newResolvedState.expandedRows[keys[i]]) {
newResolvedState.frozen = true
break
}
}
}
// If the data isn't frozen and either the data or
// sorting model has changed, update the data
if (
(oldState.frozen && !newResolvedState.frozen) ||
oldState.sorting !== newResolvedState.sorting ||
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData)
) {
// Handle collapseOnSortingChange & collapseOnPageChange
if (
(oldState.sorting !== newResolvedState.sorting && this.props.collapseOnSortingChange) ||
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData && this.props.collapseOnPageChange)
) {
newResolvedState.expandedRows = {}
}
Object.assign(newResolvedState, this.getSortedData(newResolvedState))
}
// Calculate pageSize all the time
if (newResolvedState.resolvedData) {
newResolvedState.pages = newResolvedState.manual ? newResolvedState.pages : Math.ceil(newResolvedState.resolvedData.length / newResolvedState.pageSize)
}
return this.setState(newResolvedState, cb)
}
}
+1 -1
View File
@@ -191,7 +191,7 @@ function isSortingDesc (d) {
return !!(d.sort === 'desc' || d.desc === true || d.asc === false)
}
function normalizeComponent (Comp, params, fallback = Comp) {
function normalizeComponent (Comp, params = {}, fallback = Comp) {
return typeof Comp === 'function' ? (
Comp.prototype.isReactComponent ? (
<Comp
+114
View File
@@ -0,0 +1,114 @@
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'
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
}]
}]
// Display your table!
return (
<ReactTable
data={data}
columns={columns}
defaultPageSize={10}
/>
)
`
}
+156 -43
View File
@@ -40,22 +40,169 @@ export default () => {
header: 'Name',
columns: [{
header: 'First Name',
accessor: 'firstName'
accessor: 'firstName',
footer: 'Footer'
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
accessor: d => d.lastName,
footer: 'Footer'
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: '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 />
Whether it's <strong>completely custom</strong>, or even just <strong>rearranging the order of the table's elements</strong>, this is how you can do it.
<br />
<br />
<br />
<br />
<strong>Pagination at the top using partials:</strong>
<br />
<br />
<div className='table-wrap'>
<ReactTable
data={data}
columns={columns}
>
{(state, {
Root,
Table,
HeaderGroups,
Headers,
Rows,
Footers,
Pagination,
NoData,
Loading
}, instance) => {
return (
<Root>
<Pagination />
<Table>
<HeaderGroups />
<Headers />
<Rows />
<Footers />
</Table>
<NoData />
<Loading />
</Root>
)
}}
</ReactTable>
</div>
<CodeHighlight>{() => `
import ReactTable from 'react-table'
return (
<ReactTable
data={data}
columns={columns}
>
{(state, {
Root,
Table,
HeaderGroups,
Headers,
Rows,
Footers,
Pagination,
NoData,
Loading
}, instance) => {
return (
<Root>
<Pagination />
<Table>
<HeaderGroups />
<Headers />
<Rows />
<Footers />
</Table>
<NoData />
<Loading />
</Root>
)
}}
</ReactTable>
)
`}</CodeHighlight>
<br />
<br />
<strong>Wrapping the standard table output</strong>
<br />
<br />
<div className='table-wrap'>
<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>
</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'
@@ -63,54 +210,23 @@ export default () => {
columns={columns}
defaultPageSize={10}
>
{(state, makeTable, instance) => {
console.log(state)
{(state, StandardTable, instance) => {
return (
<div>
Look! This is the entire table state and component instance at your disposal!
<JSONTree
data={Object.assign({}, state, {children: 'function () {...}'})}
theme={JSONtheme}
invertTheme
/>
<br />
<br />
{makeTable()}
</div>
)
}}
</ReactTable>
</div>
<br />
<CodeHighlight>{() => getCode()}</CodeHighlight>
</div>
)
}
function getCode () {
return `
<CodeHighlight>{() => `
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'
@@ -118,23 +234,20 @@ return (
columns={columns}
defaultPageSize={10}
>
{(state, makeTable, instance) => {
console.log(state)
{(state, StandardTable, instance) => {
return (
<div>
Look! This is the entire table state and component instance at your disposal!
<JSONTree
data={Object.assign({}, state, {children: 'function () {...}'})}
theme={JSONtheme}
invertTheme
/>
<br />
<br />
{makeTable()}
</div>
)
}}
</ReactTable>
)
`
`}</CodeHighlight>
</div>
)
}