Compare commits

...
8 Commits
5 changed files with 205 additions and 179 deletions
+68 -10
View File
@@ -52,19 +52,77 @@
## Sponsors
| | |
| --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![Platinum Sponsors](https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/platinum.png)](https://patreon.com/tannerlinsley) | [![Retool](https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/sponsor-retool.png)](https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table) |
**React Table v7** is mostly planned and I (@tannerlinsley) am looking for Patreon support to make it a reality. It will require a decent time commitment on my part to not only implement it, but also help people migrate and continue to maintain it. If you would like to contribute to my Patreon goal for v7 and beyond, [visit my Patreon and help me out!](https://patreon.com/tannerlinsley).
| | | |
| ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| [![Gold Sponsors](https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/gold.png)](https://patreon.com/tannerlinsley) | [![Retool](https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/gold-placeholder.png)](https://patreon.com/tannerlinsley) | [![Retool](https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/gold-placeholder.png)](https://patreon.com/tannerlinsley) |
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://patreon.com/tannerlinsley" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/platinum.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/sponsor-retool.png">
</a>
</td>
</tr>
</tbody>
</table>
| | | | |
| ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![Silver Sponsors](https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver.png)](https://patreon.com/tannerlinsley) | [![Retool](https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver-placeholder.png)](https://patreon.com/tannerlinsley) | [![Retool](https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver-placeholder.png)](https://patreon.com/tannerlinsley) | [![Retool](https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver-placeholder.png)](https://patreon.com/tannerlinsley) |
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://patreon.com/tannerlinsley" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/gold.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/gold-placeholder.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/gold-placeholder.png">
</a>
</td>
</tr>
</tbody>
</table>
**React Table v7** is mostly planned and I (@tannerlinsley) am looking for Patreon support to make it a reality. It will require a decent time commitment on my part to not only implement it, but also help people migrate and maintain it. If you would like to contribute to my Patreon goal for v7, [visit my Patreon and help me out!](https://patreon.com/tannerlinsley). Gold
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://patreon.com/tannerlinsley" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver-placeholder.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver-placeholder.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver-placeholder.png">
</a>
</td>
</tr>
</tbody>
</table>
## Issues, Questions and Support
Github issues are temporarily disabled until v7 is out. Most if not all issues that were or will be opened are likely to be resolved or uneccessary after v7 is released (mostly due to the headless approach of the new component). If you have an issue or a question, feel free to use the [React Table Spectrum Community/Forum](https://spectrum.chat/react-table)!
## Table of Contents
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "react-table",
"version": "6.9.0",
"version": "6.9.2",
"description": "A fast, lightweight, opinionated table and datagrid built on React",
"license": "MIT",
"homepage": "https://github.com/react-tools/react-table#readme",
+64 -70
View File
@@ -124,7 +124,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
PadRowComponent,
// Data model
resolvedData,
visibleColumns,
allVisibleColumns,
headerGroups,
hasHeaderGroups,
// Sorted Data
@@ -139,8 +139,8 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
const minRows = this.getMinRows()
const padRows = _.range(Math.max(minRows - pageRows.length, 0))
const hasColumnFooter = visibleColumns.some(d => d.Footer)
const hasFilters = filterable || visibleColumns.some(d => d.filterable)
const hasColumnFooter = allVisibleColumns.some(d => d.Footer)
const hasFilters = filterable || allVisibleColumns.some(d => d.filterable)
const recurseRowsViewIndex = (rows, path = [], index = -1) => [
rows.map((row, i) => {
@@ -167,7 +167,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
const canNext = page + 1 < pages
const rowMinWidth = _.sum(
visibleColumns.map(d => {
allVisibleColumns.map(d => {
const resizedColumn = resized.find(x => x.id === d.id) || {}
return _.getFirstDefined(resizedColumn.value, d.width, d.minWidth)
})
@@ -198,16 +198,14 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
const makeHeaderGroup = (column, i) => {
const resizedValue = col => (resized.find(x => x.id === col.id) || {}).value
const leafColumns = _.leaves(column, 'columns')
const flex = _.sum(
leafColumns.map(col => (col.width || resizedValue(col) ? 0 : col.minWidth))
column.columns.map(col => (col.width || resizedValue(col) ? 0 : col.minWidth))
)
const width = _.sum(
leafColumns.map(col => _.getFirstDefined(resizedValue(col), col.width, col.minWidth))
column.columns.map(col => _.getFirstDefined(resizedValue(col), col.width, col.minWidth))
)
const maxWidth = _.sum(
leafColumns.map(col => _.getFirstDefined(resizedValue(col), col.width, col.maxWidth))
column.columns.map(col => _.getFirstDefined(resizedValue(col), col.width, col.maxWidth))
)
const theadGroupThProps = _.splitProps(
@@ -258,16 +256,11 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
)
}
const makeHeaderGroups = (row, i) => {
const theadGroupProps = _.splitProps(
getTheadGroupProps(finalState, undefined, row, this)
)
const theadGroupTrProps = _.splitProps(
getTheadGroupTrProps(finalState, undefined, row, this)
)
const makeHeaderGroups = () => {
const theadGroupProps = _.splitProps(getTheadGroupProps(finalState, undefined, undefined, this))
const theadGroupTrProps = _.splitProps(getTheadGroupTrProps(finalState, undefined, undefined, this))
return (
<TheadComponent
key={`${i}-${row.id}`}
className={classnames('-headerGroups', theadGroupProps.className)}
style={{
...theadGroupProps.style,
@@ -280,7 +273,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
style={theadGroupTrProps.style}
{...theadGroupTrProps.rest}
>
{row.map(makeHeaderGroup)}
{headerGroups.map(makeHeaderGroup)}
</TrComponent>
</TheadComponent>
)
@@ -371,7 +364,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
style={theadTrProps.style}
{...theadTrProps.rest}
>
{visibleColumns.map(makeHeader)}
{allVisibleColumns.map(makeHeader)}
</TrComponent>
</TheadComponent>
)
@@ -459,7 +452,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
style={theadFilterTrProps.style}
{...theadFilterTrProps.rest}
>
{visibleColumns.map(makeFilter)}
{allVisibleColumns.map(makeFilter)}
</TrComponent>
</TheadComponent>
)
@@ -489,7 +482,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
style={trProps.style}
{...trProps.rest}
>
{visibleColumns.map((column, i2) => {
{allVisibleColumns.map((column, i2) => {
const resizedCol = resized.find(x => x.id === column.id) || {}
const show = typeof column.show === 'function' ? column.show() : column.show
const width = _.getFirstDefined(resizedCol.value, column.width, column.minWidth)
@@ -668,11 +661,14 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
{rowInfo.subRows &&
isExpanded &&
rowInfo.subRows.map((d, i) => makePageRow(d, i, rowInfo.nestingPath))}
{SubComponent && !rowInfo.subRows && isExpanded && SubComponent(rowInfo, () => {
const newExpanded = _.clone(expanded)
{SubComponent &&
!rowInfo.subRows &&
isExpanded &&
SubComponent(rowInfo, () => {
const newExpanded = _.clone(expanded)
_.set(newExpanded, cellInfo.nestingPath, false)
})}
_.set(newExpanded, rowInfo.nestingPath, false)
})}
</TrGroupComponent>
)
}
@@ -724,7 +720,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
)}
style={trProps.style || {}}
>
{visibleColumns.map(makePadColumn)}
{allVisibleColumns.map(makePadColumn)}
</TrComponent>
</TrGroupComponent>
)
@@ -794,13 +790,13 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
style={tFootTrProps.style}
{...tFootTrProps.rest}
>
{visibleColumns.map(makeColumnFooter)}
{allVisibleColumns.map(makeColumnFooter)}
</TrComponent>
</TfootComponent>
)
}
const makePagination = (isTop) => {
const makePagination = isTop => {
const paginationProps = _.splitProps(
getPaginationProps(finalState, undefined, undefined, this)
)
@@ -820,50 +816,48 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
)
}
const makeTable = () => {
return (
<div
className={classnames('ReactTable', className, rootProps.className)}
style={{
...style,
...rootProps.style,
}}
{...rootProps.rest}
const makeTable = () => (
<div
className={classnames('ReactTable', className, rootProps.className)}
style={{
...style,
...rootProps.style,
}}
{...rootProps.rest}
>
{showPagination && showPaginationTop ? (
<div className="pagination-top">{makePagination(true)}</div>
) : null}
<TableComponent
className={classnames(tableProps.className, currentlyResizing ? 'rt-resizing' : '')}
style={tableProps.style}
{...tableProps.rest}
>
{showPagination && showPaginationTop ? (
<div className="pagination-top">{makePagination(true)}</div>
) : null}
<TableComponent
className={classnames(tableProps.className, currentlyResizing ? 'rt-resizing' : '')}
style={tableProps.style}
{...tableProps.rest}
{hasHeaderGroups ? makeHeaderGroups() : null}
{makeHeaders()}
{hasFilters ? makeFilters() : null}
<TbodyComponent
className={classnames(tBodyProps.className)}
style={{
...tBodyProps.style,
minWidth: `${rowMinWidth}px`,
}}
{...tBodyProps.rest}
>
{hasHeaderGroups ? headerGroups.map(makeHeaderGroups) : null}
{makeHeaders()}
{hasFilters ? makeFilters() : null}
<TbodyComponent
className={classnames(tBodyProps.className)}
style={{
...tBodyProps.style,
minWidth: `${rowMinWidth}px`,
}}
{...tBodyProps.rest}
>
{pageRows.map((d, i) => makePageRow(d, i))}
{padRows.map(makePadRow)}
</TbodyComponent>
{hasColumnFooter ? makeColumnFooters() : null}
</TableComponent>
{showPagination && showPaginationBottom ? (
<div className="pagination-bottom">{makePagination(false)}</div>
) : null}
{!pageRows.length && (
<NoDataComponent {...noDataProps}>{_.normalizeComponent(noDataText)}</NoDataComponent>
)}
<LoadingComponent loading={loading} loadingText={loadingText} {...loadingProps} />
</div>
)
}
{pageRows.map((d, i) => makePageRow(d, i))}
{padRows.map(makePadRow)}
</TbodyComponent>
{hasColumnFooter ? makeColumnFooters() : null}
</TableComponent>
{showPagination && showPaginationBottom ? (
<div className="pagination-bottom">{makePagination(false)}</div>
) : null}
{!pageRows.length && (
<NoDataComponent {...noDataProps}>{_.normalizeComponent(noDataText)}</NoDataComponent>
)}
<LoadingComponent loading={loading} loadingText={loadingText} {...loadingProps} />
</div>
)
// childProps are optionally passed to a function-as-a-child
return children ? children(finalState, makeTable, this) : makeTable()
+72 -78
View File
@@ -31,17 +31,27 @@ export default Base =>
} = newState
// Determine if there are Header Groups
const hasHeaderGroups = columns.some(column => column.columns)
let hasHeaderGroups = false
columns.forEach(column => {
if (column.columns) {
hasHeaderGroups = true
}
})
// Find the expander column which could be deep in tree of columns
const allColumns = _.iterTree(columns, 'columns')
const expanderColumn = _.getFirstDefined(allColumns)
let columnsWithExpander = [...columns]
let expanderColumn = columns.find(
col => col.expander || (col.columns && col.columns.some(col2 => col2.expander))
)
if (expanderColumn && !expanderColumn.expander) {
expanderColumn = expanderColumn.columns.find(col => col.expander)
}
// If we have SubComponent's we need to make sure we have an expander column
const hasSubComponentAndNoExpanderColumn = SubComponent && !expanderColumn
const columnsWithExpander = hasSubComponentAndNoExpanderColumn
? [{ expander: true }, ...columns]
: [...columns]
if (SubComponent && !expanderColumn) {
expanderColumn = { expander: true }
columnsWithExpander = [expanderColumn, ...columnsWithExpander]
}
const makeDecoratedColumn = (column, parentColumn) => {
let dcol
@@ -53,7 +63,6 @@ export default Base =>
}
} else {
dcol = {
...this.props.column,
...this.props.column,
...column,
}
@@ -95,31 +104,40 @@ export default Base =>
const allDecoratedColumns = []
// Decorate the columns
const decorateAndAddToAll = (columns, parentColumn) => columns.map(column => {
const decorateAndAddToAll = (column, parentColumn) => {
const decoratedColumn = makeDecoratedColumn(column, parentColumn)
if (column.columns) {
decoratedColumn.columns = decorateAndAddToAll(column.columns, column)
}
allDecoratedColumns.push(decoratedColumn)
return decoratedColumn
}
const decoratedColumns = columnsWithExpander.map(column => {
if (column.columns) {
return {
...column,
columns: column.columns.map(d => decorateAndAddToAll(d, column)),
}
}
return decorateAndAddToAll(column)
})
const decoratedColumns = decorateAndAddToAll(columnsWithExpander)
const mapVisibleColumns = columns => columns.map(column => {
// Build the visible columns, headers and flat column list
let visibleColumns = decoratedColumns.slice()
let allVisibleColumns = []
visibleColumns = visibleColumns.map(column => {
if (column.columns) {
const visibleSubColumns = column.columns.filter(
d => (pivotBy.indexOf(d.id) > -1 ? false : _.getFirstDefined(d.show, true))
)
return {
...column,
columns: mapVisibleColumns(visibleSubColumns),
columns: visibleSubColumns,
}
}
return column
})
const filterVisibleColumns = columns => columns.filter(
visibleColumns = visibleColumns.filter(
column =>
column.columns
? column.columns.length
@@ -128,12 +146,8 @@ export default Base =>
: _.getFirstDefined(column.show, true)
)
// Build the full array of visible columns - this is an array that contains all columns that
// are not hidden via pivoting
const allVisibleColumns = filterVisibleColumns(mapVisibleColumns(decoratedColumns.slice()))
// Find any custom pivot location
const pivotIndex = allVisibleColumns.findIndex(col => col.pivot)
const pivotIndex = visibleColumns.findIndex(col => col.pivot)
// Handle Pivot Columns
if (pivotBy.length) {
@@ -166,66 +180,46 @@ export default Base =>
// Place the pivotColumns back into the visibleColumns
if (pivotIndex >= 0) {
pivotColumnGroup = {
...allVisibleColumns[pivotIndex],
...visibleColumns[pivotIndex],
...pivotColumnGroup,
}
allVisibleColumns.splice(pivotIndex, 1, pivotColumnGroup)
visibleColumns.splice(pivotIndex, 1, pivotColumnGroup)
} else {
allVisibleColumns.unshift(pivotColumnGroup)
visibleColumns.unshift(pivotColumnGroup)
}
}
// Build Visible Columns and Header Groups
const allColumnHeaders = []
// Build Header Groups
const headerGroups = []
let currentSpan = []
const addHeader = column => {
let level = 0
// A convenience function to add a header and reset the currentSpan
const addHeader = (columns, column) => {
headerGroups.push({
...this.props.column,
...column,
columns,
})
currentSpan = []
}
// If this column has children, push them first and add this column to the next level
// Build flat list of allVisibleColumns and HeaderGroups
visibleColumns.forEach(column => {
if (column.columns) {
const childLevels = column.columns.map(addHeader)
level = Math.max(...childLevels) + 1
}
// Add spans above columns without parents (orphans) to fill the space above them
if (allColumnHeaders.length <= level) allColumnHeaders.push([])
if (level > 0) {
// The spans need to contain the shifted headers as children. This finds all of the
// columns in the lower level between the first child of this column and the last child
// of the preceding column (if there is one)
const lowerLevel = allColumnHeaders[level - 1]
const precedingColumn = _.last(allColumnHeaders[level])
const indexOfFirstChildInLowerLevel = lowerLevel.indexOf(column.columns[0])
const indexAfterLastChildInPrecedingColumn = precedingColumn
? lowerLevel.indexOf(_.last(precedingColumn.columns)) + 1
: 0
// If there are ophans, add a span above them
const orphans = lowerLevel.slice(
indexAfterLastChildInPrecedingColumn,
indexOfFirstChildInLowerLevel
)
if (orphans.length) {
allColumnHeaders[level].push({
...this.props.column,
columns: orphans,
})
allVisibleColumns = allVisibleColumns.concat(column.columns)
if (currentSpan.length > 0) {
addHeader(currentSpan)
}
addHeader(column.columns, column)
return
}
allColumnHeaders[level].push(column)
return level
allVisibleColumns.push(column)
currentSpan.push(column)
})
if (hasHeaderGroups && currentSpan.length > 0) {
addHeader(currentSpan)
}
allVisibleColumns.forEach(addHeader)
// visibleColumns is an array containing column definitions for the bottom row of TH elements
const visibleColumns = allColumnHeaders.shift()
const headerGroups = allColumnHeaders.reverse()
// Access the data
const accessRow = (d, i, level = 0) => {
const row = {
@@ -255,7 +249,7 @@ export default Base =>
resolvedData = resolvedData.map((d, i) => accessRow(d, i))
// TODO: Make it possible to fabricate nested rows without pivoting
const aggregatingColumns = visibleColumns.filter(d => !d.expander && d.aggregate)
const aggregatingColumns = allVisibleColumns.filter(d => !d.expander && d.aggregate)
// If pivoting, recursively group the data
const aggregate = rows => {
@@ -299,7 +293,7 @@ export default Base =>
return {
...newState,
resolvedData,
visibleColumns,
allVisibleColumns,
headerGroups,
allDecoratedColumns,
hasHeaderGroups,
@@ -313,7 +307,7 @@ export default Base =>
filtered,
defaultFilterMethod,
resolvedData,
visibleColumns,
allVisibleColumns,
allDecoratedColumns,
} = resolvedState
@@ -328,7 +322,7 @@ export default Base =>
sortedData: manual
? resolvedData
: this.sortData(
this.filterData(resolvedData, filtered, defaultFilterMethod, allDecoratedColumns),
this.filterData(resolvedData, filtered, defaultFilterMethod, allVisibleColumns),
sorted,
sortMethodsByColumnID
),
@@ -342,7 +336,7 @@ export default Base =>
page: this.getStateOrProp('page'),
pageSize: this.getStateOrProp('pageSize'),
filter: this.getStateOrProp('filter'),
};
}
this.props.onFetchData(currentState, this)
}
@@ -355,12 +349,12 @@ export default Base =>
return _.getFirstDefined(this.state[key], this.props[key])
}
filterData (data, filtered, defaultFilterMethod, visibleColumns) {
filterData (data, filtered, defaultFilterMethod, allVisibleColumns) {
let filteredData = data
if (filtered.length) {
filteredData = filtered.reduce((filteredSoFar, nextFilter) => {
const column = visibleColumns.find(x => x.id === nextFilter.id)
const column = allVisibleColumns.find(x => x.id === nextFilter.id)
// Don't filter hidden columns or columns that have had their filters disabled
if (!column || column.filterable === false) {
@@ -389,7 +383,7 @@ export default Base =>
row[this.props.subRowsKey],
filtered,
defaultFilterMethod,
visibleColumns
allVisibleColumns
),
}
})
-20
View File
@@ -10,8 +10,6 @@ export default {
range,
remove,
clone,
leaves,
iterTree,
getFirstDefined,
sum,
makeTemplateComponent,
@@ -110,24 +108,6 @@ function clone (a) {
}
}
function leaves (root, childProperty) {
if (Object.prototype.hasOwnProperty.call(root, childProperty)) {
const children = root[childProperty].map(child => leaves(child, childProperty))
return [].concat(...children)
}
return [root]
}
function iterTree (root, childProperty) {
if (Object.prototype.hasOwnProperty.call(root, childProperty)) {
const children = root[childProperty].map(child => leaves(child, childProperty))
return [].concat(root, ...children)
}
return [root]
}
function getFirstDefined (...args) {
for (let i = 0; i < args.length; i += 1) {
if (typeof args[i] !== 'undefined') {