react-table-6
Version:
A fast, lightweight, opinionated table and datagrid built on React
200 lines (171 loc) • 5.65 kB
JavaScript
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import set from 'lodash.set'
import get from 'lodash.get'
/*
AdvancedExpandTableHOC for ReactTable
HOC which allows any Cell in the row to toggle the row's
SubComponent. Also allows the SubComponent to toggle itself.
Expand functions available to any SubComponent or Column Cell:
toggleRowSubComponent
showRowSubComponent
hideRowSubComponent
Each Column Renderer (E.g. Cell ) gets the expand functions in its props
And Each SubComponent gets the expand functions in its props
Expand functions takes the `rowInfo` given to each
Column Renderer and SubComponent already by ReactTable.
*/
export const subComponentWithToggle = (SubComponent, expandFuncs) => props => (
<SubComponent {...props} {...expandFuncs} />
)
// each cell in the column gets passed the function to toggle a sub component
export const columnsWithToggle = (columns, expandFuncs) =>
columns.map(column => {
if (column.columns) {
return {
...column,
columns: columnsWithToggle(column.columns, expandFuncs),
}
}
return {
...column,
getProps () {
return {
...expandFuncs,
}
},
}
})
export const advancedExpandTableHOC = TableComponent =>
class AdvancedExpandTable extends Component {
// after initial render if we get new
// data, columns, page changes, etc.
// we reset expanded state.
static getDerivedStateFromProps () {
return {
expanded: {},
}
}
constructor (props) {
super(props)
this.state = {
expanded: {},
}
this.toggleRowSubComponent = this.toggleRowSubComponent.bind(this)
this.showRowSubComponent = this.showRowSubComponent.bind(this)
this.hideRowSubComponent = this.hideRowSubComponent.bind(this)
this.getTdProps = this.getTdProps.bind(this)
this.fireOnExpandedChange = this.fireOnExpandedChange.bind(this)
this.expandFuncs = {
toggleRowSubComponent: this.toggleRowSubComponent,
showRowSubComponent: this.showRowSubComponent,
hideRowSubComponent: this.hideRowSubComponent,
}
}
static propTypes = {
columns: PropTypes.array.isRequired,
SubComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element])
.isRequired,
onExpandedChange: PropTypes.func,
};
static defaultProps = {
onExpandedChange: null,
};
static DisplayName = 'AdvancedExpandTable';
// since we pass the expand functions to each Cell,
// we need to filter it out from being passed as an
// actual DOM attribute. See getProps in columnsWithToggle above.
static TdComponent ({
toggleRowSubComponent,
showRowSubComponent,
hideRowSubComponent,
...rest
}) {
return <TableComponent.defaultProps.TdComponent {...rest} />
}
fireOnExpandedChange (rowInfo, e) {
// fire callback once state has changed.
if (this.props.onExpandedChange) {
this.props.onExpandedChange(rowInfo, e)
}
}
resolveNewTableState (rowInfoOrNestingPath, e, expandType) {
// derive nestingPath if only rowInfo is passed
let nestingPath = rowInfoOrNestingPath
if (rowInfoOrNestingPath.nestingPath) {
nestingPath = rowInfoOrNestingPath.nestingPath
}
this.setState(
prevState => {
const isExpanded = get(prevState.expanded, nestingPath)
// since we do not support nested rows, a shallow clone is okay.
const newExpanded = { ...prevState.expanded }
switch (expandType) {
case 'show':
set(newExpanded, nestingPath, {})
break
case 'hide':
set(newExpanded, nestingPath, false)
break
default:
// toggle
set(newExpanded, nestingPath, isExpanded ? false : {})
}
return {
...prevState,
expanded: newExpanded,
}
},
() => this.fireOnExpandedChange(rowInfoOrNestingPath, e)
)
}
toggleRowSubComponent (rowInfo, e) {
this.resolveNewTableState(rowInfo, e)
}
showRowSubComponent (rowInfo, e) {
this.resolveNewTableState(rowInfo, e, 'show')
}
hideRowSubComponent (rowInfo, e) {
this.resolveNewTableState(rowInfo, e, 'hide')
}
getTdProps (tableState, rowInfo, column) {
const { expander } = column
if (!expander) {
// no overrides
return {}
}
return {
// only override onClick for column Td
onClick: e => {
this.toggleRowSubComponent(rowInfo, e)
},
}
}
getWrappedInstance () {
if (!this.wrappedInstance) { console.warn('AdvancedExpandTable - No wrapped instance') }
if (this.wrappedInstance.getWrappedInstance) {
return this.wrappedInstance.getWrappedInstance()
}
return this.wrappedInstance
}
render () {
const {
columns, SubComponent, onExpandedChange, ...rest
} = this.props
const wrappedColumns = columnsWithToggle(columns, this.expandFuncs)
const WrappedSubComponent = subComponentWithToggle(
SubComponent,
this.expandFuncs
)
return (
<TableComponent
{...rest}
columns={wrappedColumns}
expanded={this.state.expanded}
getTdProps={this.getTdProps}
SubComponent={WrappedSubComponent}
TdComponent={AdvancedExpandTable.TdComponent}
/>
)
}
}