Compare commits

...
16 Commits
Author SHA1 Message Date
Tanner Linsley 62cff3d6bc 5.0.3 2017-02-09 10:07:37 -07:00
Tanner Linsley 057a815337 Adjust current page if available pages dips below current page
Fixes #69
2017-02-09 10:07:33 -07:00
Tanner Linsley aa48ef57ee 5.0.2 2017-02-03 14:05:32 -07:00
Tanner Linsley 22f79fcd28 Added instance to all custom props callbacks 2017-02-03 14:05:25 -07:00
Tanner Linsley ff8e24760a 5.0.1 2017-02-03 11:07:51 -07:00
Tanner Linsley f1511bd27e Bug fixes and Doc Updates 2017-02-03 11:07:48 -07:00
Tanner Linsley 4733997c26 Updated Docs 2017-02-02 18:17:37 -07:00
Tanner Linsley c3a6e236ae 5.0.0 2017-02-02 18:13:48 -07:00
Tanner Linsley 83200d4e4c Rewrite of dynamic props and stable sorting algorithm 2017-02-02 18:13:40 -07:00
Tanner Linsley 5e16c2cb24 4.4.1 2017-01-27 14:34:59 -07:00
Tanner Linsley 66cee186a3 pivotBy prop now defaults to undefined 2017-01-27 14:34:50 -07:00
Tanner Linsley 55bbf4bf37 Updated Docs 2017-01-27 14:20:51 -07:00
Tanner Linsley 10ce93344c Updated Docs 2017-01-27 14:17:33 -07:00
Tanner Linsley 114621a359 4.4.0 2017-01-27 14:10:57 -07:00
Tanner Linsley 8de0fa420b Better default sorting, column.width support, expander column position 2017-01-27 14:10:47 -07:00
Tanner Linsley aa77193810 Update Docs 2017-01-26 21:26:58 -07:00
22 changed files with 1530 additions and 797 deletions
+8 -2
View File
@@ -12,13 +12,16 @@ import Readme from '../README.md'
//
import Simple from '../stories/Simple.js'
import CellRenderers from '../stories/CellRenderers.js'
import MaxWidths from '../stories/MaxWidths.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'
//
configure(() => {
storiesOf('1. Docs')
@@ -36,11 +39,14 @@ configure(() => {
storiesOf('2. Demos')
.add('Simple Table', Simple)
.add('Cell Renderers & Custom Components', CellRenderers)
.add('Max Widths', MaxWidths)
.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('Functional Rendering', FunctionalRendering)
.add('Custom Expander Position', CustomExpanderPosition)
}, module)
+232 -54
View File
@@ -43,8 +43,10 @@
- [Data](#data)
- [Props](#props)
- [Columns](#columns)
- [Column Header Groups](#column-header-groups)
- [Custom Cell & Header Rendering](#custom-cell--and-header-rendering)
- [Styles](#styles)
- [Header Groups](#header-groups)
- [Custom Props](#custom-props)
- [Pivoting & Aggregation](#pivoting--aggregation)
- [Sub Tables & Sub Components](#sub-tables--sub-components)
- [Server-side Data](#server-side-data)
@@ -52,6 +54,10 @@
- [Functional Rendering](#functional-rendering)
- [Multi-Sort](#multi-sort)
- [Component Overrides](#component-overrides)
- [Contributing](#contributing)
- [Scripts](#scripts)
- [Used By](#used-by)
## Installation
1. Install React Table as a dependency
@@ -121,55 +127,87 @@ These are all of the available props (and their default values) for the main `<R
```javascript
{
// General
loading: false, // Whether to show the loading overlay or not
defaultPageSize: 20, // The default page size (this can be changed by the user if `showPageSizeOptions` is enabled)
minRows: 0, // Ensure this many rows are always rendered, regardless of rows on page
showPagination: true, // Shows or hides the pagination component
showPageJump: true, // Shows or hides the pagination number input
showPageSizeOptions: true, // Enables the user to change the page size
pageSizeOptions: [5, 10, 20, 25, 50, 100], // The available page size options
expanderColumnWidth: 30, // default columnWidth for the expander column
data: [],
loading: false,
showPagination: true,
showPageSizeOptions: true,
pageSizeOptions: [5, 10, 20, 25, 50, 100],
defaultPageSize: 20,
showPageJump: true,
expanderColumnWidth: 35,
// Callbacks
onChange: (state, instance) => null, // Anytime the internal state of the table changes, this will fire
onTrClick: (row, event) => null, // Handler for row click events
// Controlled State Overrides (see Fully Controlled Component section)
page: undefined,
pageSize: undefined,
sorting: undefined
// Controlled State Callbacks
onExpandSubComponent: undefined,
onPageChange: undefined,
onPageSizeChange: undefined,
onSortingChange: undefined,
// Pivoting
pivotBy: undefined,
pivotColumnWidth: 200,
pivotValKey: '_pivotVal',
pivotIDKey: '_pivotID',
subRowsKey: '_subRows',
// Pivoting State Overrides (see Fully Controlled Component section)
expandedRows: {},
// Pivoting State Callbacks
onExpandRow: undefined,
// General Callbacks
onChange: () => null,
// Classes
className: '',
style: {},
// Component decorators
getProps: () => ({}),
getTableProps: () => ({}),
getTheadGroupProps: () => ({}),
getTheadGroupTrProps: () => ({}),
getTheadGroupThProps: () => ({}),
getTheadProps: () => ({}),
getTheadTrProps: () => ({}),
getTheadThProps: () => ({}),
getTbodyProps: () => ({}),
getTrGroupProps: () => ({}),
getTrProps: () => ({}),
getThProps: () => ({}),
getTdProps: () => ({}),
getPaginationProps: () => ({}),
getLoadingProps: () => ({}),
// 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...',
pageText: 'Page',
ofText: 'of',
rowsText: 'rows',
// Classes
className: '-striped -highlight', // The most top level className for the component
tableClassName: '', // ClassName for the `table` element
theadClassName: '', // ClassName for the `thead` element
tbodyClassName: '', // ClassName for the `tbody` element
trClassName: '', // ClassName for all `tr` elements
trClassCallback: row => null, // A call back to dynamically add classes (via the classnames module) to a row element
paginationClassName: '' // ClassName for `pagination` element
// Styles
style: {}, // Main style object for the component
tableStyle: {}, // style object for the `table` component
theadStyle: {}, // style object for the `thead` component
tbodyStyle: {}, // style object for the `tbody` component
trStyle: {}, // style object for the `tr` component
trStyleCallback: row => {}, // A call back to dynamically add styles to a row element
thStyle: {}, // style object for the `th` component
tdStyle: {}, // style object for the `td` component
paginationStyle: {}, // style object for the `paginination` component
// Controlled Props (see Using as a Fully Controlled Component below)
page: undefined,
pageSize: undefined,
sorting: undefined,
expandedRows: undefined,
// Controlled Callbacks
onExpandRow: undefined,
onPageChange: undefined,
onPageSizeChange: undefined,
}
```
@@ -185,7 +223,7 @@ Object.assign(ReactTableDefaults, {
})
```
Or just define them on the component per-instance
Or just define them as props
```javascript
<ReactTable
@@ -206,13 +244,18 @@ Or just define them on the component per-instance
sortable: true,
sort: 'asc' or 'desc', // used to determine the column sorting on init
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
expander: false // This option will override all data-related options and designates the column to be used
// for pivoting and sub-component expansion
// Cell Options
className: '', // Set the classname of the `td` element of the column
style: {}, // Set the style of the `td` element of the column
render: JSX eg. ({value, rowValues, row, index, viewIndex}) => <span>{value}</span>, // Provide a JSX element or stateless function to render whatever you want as the column's cell with access to the entire row
render: JSX eg. (rowInfo: {value, rowValues, row, index, viewIndex}) => <span>{value}</span>, // Provide a JSX element or stateless function to render whatever you want as the column's cell with access to the entire row
// value == the accessed value of the column
// rowValues == an object of all of the accessed values for the row
// row == the original row of data supplied to the table
@@ -230,19 +273,12 @@ Or just define them on the component per-instance
}]
```
## 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`.
- 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
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!
## Header Groups
To group columns with another header column, just nest your columns in a header column like so:
## Column Header Groups
To group columns with another header column, just nest your columns in a header column. Header columns utilize the same header properties as regular columns.
```javascript
const columns = [{
header: 'Favorites',
headerClassName: 'my-favorites-column-header-group'
columns: [{
header: 'Color',
accessor: 'favorites.color'
@@ -256,6 +292,148 @@ const columns = [{
}]
```
## Custom Cell & 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:
- `row` - Original row from your data
- `rowValues` - The post-accessed values from the original row
- `index` - The index of the row
- `viewIndex` - the index of the row relative to the current page
- `level` - The nesting depth (zero-indexed)
- `nestingPath` - The nesting path of the row
- `aggregated` - A boolean stating if the row is an aggregation row
- `subRows` - An array of any expandable sub-rows contained in this row
```javascript
// This column uses a stateless component to produce a different colored bar depending on the value
// You can also use stateful components or any other function that returns JSX
const columns = [{
header: () => <span><i className='fa-tasks' /> Progress</span>,
accessor: 'progress',
render: row => (
<div
style={{
width: '100%',
height: '100%',
backgroundColor: '#dadada',
borderRadius: '2px'
}}
>
<div
style={{
width: `${row.value}%`,
height: '100%',
backgroundColor: row.value > 66 ? '#85cc00'
: row.value > 33 ? '#ffbf00'
: '#ff2e00',
borderRadius: '2px',
transition: 'all .2s ease-out'
}}
/>
</div>
)
}]
```
## Styles
React-table ships with a minimal and clean stylesheet to get you on your feet quickly. It's located at `react-table/react-table.css`.
#### Built-in Styles
- 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
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.
## Custom Props
#### Built-in Components
Every single built-in component's props can be dynamically extended using any one of these prop-callbacks:
```javascript
<ReactTable
getProps={fn}
getTableProps={fn}
getTheadGroupProps={fn}
getTheadGroupTrProps={fn}
getTheadGroupThProps={fn}
getTheadProps={fn}
getTheadTrProps={fn}
getTheadThProps={fn}
getTbodyProps={fn}
getTrGroupProps={fn}
getTrProps={fn}
getThProps={fn}
getTdProps={fn}
getPaginationProps={fn}
getLoadingProps={fn}
/>
```
These callbacks are executed with each render of the element with three parameters:
1. Table State
1. RowInfo (undefined if not applicable)
1. Column (undefined if not applicable)
1. React Table Instance
This makes it extremely easy to add, say... a row click callback!
```javascript
// When any Td element is clicked, we'll log out some information
<ReactTable
getTdProps={(state, rowInfo, column, instance) => {
return {
onClick: e => {
console.log('A Td Element was clicked!')
console.log('it produced this event:', e)
console.log('It was in this column:', column)
console.log('It was in this row:', rowInfo)
console.log('It was in this table instance:', instance)
}
}
}}
/>
```
You can use these callbacks for dynamic styling as well!
```javascript
// Any Tr element will be green if its (row.age > 20)
<ReactTable
getTrProps={(state, rowInfo, column) => {
return {
style: {
background: rowInfo.age > 20 ? 'green' : 'red'
}
}
}}
/>
```
#### Column Components
Just as core components can have dynamic props, columns and column headers can too!
You can utilize either of these prop callbacks on columns:
```javascript
const columns = [{
getHeaderProps: () => (...),
getProps: () => (...)
}]
```
In a similar fashion these can be used to dynamically style just about anything!
```javascript
// This columns cells will be red if (row.name === Santa Clause)
const columns = [{
getProps: (state, rowInfo, column) => {
return {
style: {
background: rowInfo.name === 'Santa Clause' ? 'red' : null
}
}
}
}]
```
## Pivoting & Aggregation
Pivoting the table will group records together based on their accessed values and allow the rows in that group to be expanded underneath it.
To pivot, pass an array of `columnID`'s to `pivotBy`. Remember, a column's `id` is either the one that you assign it (when using a custom accessors) or its `accessor` string.
+1 -1
View File
@@ -16,7 +16,7 @@
<body>
<div id="root"></div>
<div id="error-display"></div>
<script src="static/preview.2dea77e688032cb54b25.bundle.js"></script>
<script src="static/preview.ee446fa2bd3fbfe7e38a.bundle.js"></script>
</body>
</html>
+2 -2
View File
@@ -4,7 +4,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="storybook-version" content="2.35.2">
<meta name="storybook-version" content="2.35.3">
<title>React Storybook</title>
<style>
/*
@@ -38,7 +38,7 @@
</head>
<body style="margin: 0;">
<div id="root"></div>
<script src="static/manager.6358a4421928765ac21b.bundle.js"></script>
<script src="static/manager.f127c5ae855df8d8558a.bundle.js"></script>
</body>
</html>
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
{"version":3,"file":"static/manager.6358a4421928765ac21b.bundle.js","sources":["webpack:///static/manager.6358a4421928765ac21b.bundle.js"],"mappings":"AAAA;AA4zDA;AAg4DA;AA4gEA;AAiiEA;AAk0EA;AA67CA;AA0qDA;AAy4DA;AA8lDA;AA4hDA;AA62DA;AA+/DA;AA6mDA;AAsyCA;AA2gDA;AA+2DA;AA+jEA;AAixDA;AA6lDA;AA+nDA;AA0qEA;AAinDA;AAw3DA;AA80GA;AAhvCA;AAuqGA;AAuqEA;AA6wEA;AAleA;AA+gEA;AA+mCA","sourceRoot":""}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"version":3,"file":"static/manager.f127c5ae855df8d8558a.bundle.js","sources":["webpack:///static/manager.f127c5ae855df8d8558a.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;AAwyDA;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.2dea77e688032cb54b25.bundle.js","sources":["webpack:///static/preview.2dea77e688032cb54b25.bundle.js"],"mappings":"AAAA;AAwxDA;AA45DA;AA4rDA;AAgjFA;AAy+FA;AAomJA;AAloHA;AAgsWA;AAm0DA;AA4kEA;AA+nDA;AAw8CA;AAkjEA;AA0oDA;AAq8CA;AAqoDA;AAksEA;AAwrDA;AAiuCA;AAopDA;AA0kDA;AAyqEA;AAu2DA;AAgpDA;AAwkDA;AAm5EA;AA0mHA;AAmrDA;AA6/BA;AAw9CA;AA6ZA;AAw8CA","sourceRoot":""}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"version":3,"file":"static/preview.ee446fa2bd3fbfe7e38a.bundle.js","sources":["webpack:///static/preview.ee446fa2bd3fbfe7e38a.bundle.js"],"mappings":"AAAA;AAkuDA;AAiwDA;AAs1EA;AA+gGA;AAo4IA;AAt5NA;AA4udA;AA2hEA;AA2uDA;AA29DA;AA2gDA;AAosDA;AAuhDA;AA+6DA;AAioDA;AA4+CA;AAwpDA;AAisEA;AAqoDA;AAqwCA;AA2oDA;AAowDA;AAokEA;AA0vDA;AA2rDA;AA8nDA;AAwrFA;AAyiGA;AA0/CA;AAqyCA;AA22CA;AAu/CA;AAyGA;AA+jFA","sourceRoot":""}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "react-table",
"version": "4.3.0",
"version": "5.0.3",
"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.
Binary file not shown.
+410
View File
@@ -0,0 +1,410 @@
import _ from './utils'
export default {
getDataModel (nextProps, nextState) {
const {
columns,
pivotBy = [],
data,
pivotIDKey,
pivotValKey,
subRowsKey,
expanderColumnWidth,
SubComponent,
page,
pageSize
} = this.getResolvedState(nextProps, nextState)
// Determine Header Groups
let hasHeaderGroups = false
columns.forEach(column => {
if (column.columns) {
hasHeaderGroups = true
}
})
// Build Header Groups
const headerGroups = []
let currentSpan = []
// A convenience function to add a header and reset the currentSpan
const addHeader = (columns, column = columns[0]) => {
headerGroups.push({
...this.props.column,
...column,
columns: columns
})
currentSpan = []
}
const noSubExpanderColumns = columns.map(col => {
return {
...col,
columns: col.columns ? col.columns.filter(d => !d.expander) : undefined
}
})
let expanderColumnIndex = columns.findIndex(col => col.expander)
const needsExpander = (SubComponent || pivotBy.length) && expanderColumnIndex === -1
const columnsWithExpander = needsExpander ? [{expander: true}, ...noSubExpanderColumns] : noSubExpanderColumns
if (needsExpander) {
expanderColumnIndex = 0
}
const makeDecoratedColumn = (column) => {
const dcol = {
...this.props.column,
...column
}
if (dcol.expander) {
dcol.width = expanderColumnWidth
return dcol
}
if (typeof dcol.accessor === 'string') {
dcol.id = dcol.id || dcol.accessor
const accessorString = dcol.accessor
dcol.accessor = row => _.get(row, accessorString)
return dcol
}
if (dcol.accessor && !dcol.id) {
console.warn(dcol)
throw new Error('A column id is required if using a non-string accessor for column above.')
}
if (!dcol.accessor) {
dcol.accessor = d => undefined
}
// Ensure minWidth is not greater than maxWidth if set
if (dcol.maxWidth < dcol.minWidth) {
dcol.minWidth = dcol.maxWidth
}
return dcol
}
// Decorate the columns
const decorateAndAddToAll = (col) => {
const decoratedColumn = makeDecoratedColumn(col)
allDecoratedColumns.push(decoratedColumn)
return decoratedColumn
}
let allDecoratedColumns = []
const decoratedColumns = columnsWithExpander.map((column, i) => {
if (column.columns) {
return {
...column,
columns: column.columns.map(decorateAndAddToAll)
}
} else {
return decorateAndAddToAll(column)
}
})
// Build the visible columns, headers and flat column list
let visibleColumns = decoratedColumns.slice()
let allVisibleColumns = []
visibleColumns = visibleColumns.map((column, i) => {
if (column.columns) {
const visibleSubColumns = column.columns.filter(d => pivotBy.indexOf(d.id) > -1 ? false : _.getFirstDefined(d.show, true))
return {
...column,
columns: visibleSubColumns
}
}
return column
})
visibleColumns = visibleColumns.filter(column => {
return column.columns ? column.columns.length : pivotBy.indexOf(column.id) > -1 ? false : _.getFirstDefined(column.show, true)
})
// Move the pivot columns into a single column if needed
if (pivotBy.length) {
const pivotColumns = []
for (var i = 0; i < allDecoratedColumns.length; i++) {
if (pivotBy.indexOf(allDecoratedColumns[i].id) > -1) {
pivotColumns.push(allDecoratedColumns[i])
}
}
const pivotColumn = {
...pivotColumns[0],
pivotColumns,
expander: true
}
visibleColumns[expanderColumnIndex] = pivotColumn
}
// Build flast list of allVisibleColumns and HeaderGroups
visibleColumns.forEach((column, i) => {
if (column.columns) {
allVisibleColumns = allVisibleColumns.concat(column.columns)
if (currentSpan.length > 0) {
addHeader(currentSpan)
}
addHeader(column.columns, column)
return
}
allVisibleColumns.push(column)
currentSpan.push(column)
})
if (hasHeaderGroups && currentSpan.length > 0) {
addHeader(currentSpan)
}
// Access the data
let resolvedData = data.map((d, i) => {
const row = {
__original: d,
__index: i
}
allDecoratedColumns.forEach(column => {
if (column.expander) return
row[column.id] = column.accessor(d)
})
return row
})
// If pivoting, recursively group the data
const aggregate = (rows) => {
const aggregationValues = {}
aggregatingColumns.forEach(column => {
const values = rows.map(d => d[column.id])
aggregationValues[column.id] = column.aggregate(values, rows)
})
return aggregationValues
}
let standardColumns = pivotBy.length ? allVisibleColumns.slice(1) : allVisibleColumns
const aggregatingColumns = standardColumns.filter(d => d.aggregate)
let pivotColumn
if (pivotBy.length) {
pivotColumn = allVisibleColumns[0]
const groupRecursively = (rows, keys, i = 0) => {
// This is the last level, just return the rows
if (i === keys.length) {
return rows
}
// Group the rows together for this level
let groupedRows = Object.entries(
_.groupBy(rows, keys[i]))
.map(([key, value]) => {
return {
[pivotIDKey]: keys[i],
[pivotValKey]: key,
[keys[i]]: key,
[subRowsKey]: value
}
}
)
// Recurse into the subRows
groupedRows = groupedRows.map(rowGroup => {
let subRows = groupRecursively(rowGroup[subRowsKey], keys, i + 1)
return {
...rowGroup,
[subRowsKey]: subRows,
...aggregate(subRows)
}
})
return groupedRows
}
resolvedData = groupRecursively(resolvedData, pivotBy)
}
const newPages = Math.ceil(resolvedData.length / pageSize)
return {
resolvedData,
pivotColumn,
allVisibleColumns,
headerGroups,
allDecoratedColumns,
hasHeaderGroups,
page: (page + 1) > newPages ? newPages - 1 : page
}
},
getSortedData (nextProps, nextState) {
const {
manual,
sorting,
allDecoratedColumns,
resolvedData
} = this.getResolvedState(nextProps, nextState)
const resolvedSorting = sorting.length ? sorting : this.getInitSorting(allDecoratedColumns)
// Resolve the data from either manual data or sorted data
return {
resolvedSorting,
sortedData: manual ? resolvedData : this.sortData(resolvedData, resolvedSorting)
}
},
fireOnChange () {
this.props.onChange(this.getResolvedState(), this)
},
getPropOrState (key) {
return _.getFirstDefined(this.props[key], this.state[key])
},
getStateOrProp (key) {
return _.getFirstDefined(this.state[key], this.props[key])
},
getInitSorting (columns) {
if (!columns) {
return []
}
const initSorting = columns.filter(d => {
return typeof d.sort !== 'undefined'
}).map(d => {
return {
id: d.id,
asc: d.sort === 'asc'
}
})
return initSorting
// return initSorting.length ? initSorting : [{
// id: columns.find(d => d.id).id,
// asc: true
// }]
},
sortData (data, sorting) {
const sorted = _.orderBy(data, sorting.map(sort => {
return row => {
if (row[sort.id] === null || row[sort.id] === undefined) {
return -Infinity
}
return typeof row[sort.id] === 'string' ? row[sort.id].toLowerCase() : row[sort.id]
}
}), sorting.map(d => d.asc ? 'asc' : 'desc'))
return sorted.map(row => {
if (!row[this.props.subRowsKey]) {
return row
}
return {
...row,
[this.props.subRowsKey]: this.sortData(row[this.props.subRowsKey], sorting)
}
})
},
getMinRows () {
return _.getFirstDefined(this.props.minRows, this.getStateOrProp('pageSize'))
},
// User actions
onPageChange (page) {
const { onPageChange } = this.props
if (onPageChange) {
return onPageChange(page)
}
this.setStateWithData({
expandedRows: {},
page
}, () => {
this.fireOnChange()
})
},
onPageSizeChange (newPageSize) {
const { onPageSizeChange } = this.props
const { pageSize, page } = this.getResolvedState()
// Normalize the page to display
const currentRow = pageSize * page
const newPage = Math.floor(currentRow / newPageSize)
if (onPageSizeChange) {
return onPageSizeChange(newPageSize, newPage)
}
this.setStateWithData({
pageSize: newPageSize,
page: newPage
}, () => {
this.fireOnChange()
})
},
sortColumn (column, additive) {
const { sorting } = this.getResolvedState()
const { onSortingChange } = this.props
if (onSortingChange) {
return onSortingChange(column, additive)
}
let newSorting = _.clone(sorting || [])
if (_.isArray(column)) {
const existingIndex = newSorting.findIndex(d => d.id === column[0].id)
if (existingIndex > -1) {
const existing = newSorting[existingIndex]
if (existing.asc) {
column.forEach((d, i) => {
newSorting[existingIndex + i].asc = false
})
} else {
if (additive) {
newSorting.splice(existingIndex, column.length)
} else {
column.forEach((d, i) => {
newSorting[existingIndex + i].asc = true
})
}
}
if (!additive) {
newSorting = newSorting.slice(existingIndex, column.length)
}
} else {
if (additive) {
newSorting = newSorting.concat(column.map(d => ({
id: d.id,
asc: true
})))
} else {
newSorting = column.map(d => ({
id: d.id,
asc: true
}))
}
}
} else {
const existingIndex = newSorting.findIndex(d => d.id === column.id)
if (existingIndex > -1) {
const existing = newSorting[existingIndex]
if (existing.asc) {
existing.asc = false
if (!additive) {
newSorting = [existing]
}
} else {
if (additive) {
newSorting.splice(existingIndex, 1)
} else {
existing.asc = true
newSorting = [existing]
}
}
} else {
if (additive) {
newSorting.push({
id: column.id,
asc: true
})
} else {
newSorting = [{
id: column.id,
asc: true
}]
}
}
}
this.setStateWithData({
page: ((!sorting.length && newSorting.length) || !additive) ? 0 : this.state.page,
sorting: newSorting
}, () => {
this.fireOnChange()
})
}
}
+506 -655
View File
File diff suppressed because it is too large Load Diff
+15 -9
View File
@@ -6,16 +6,16 @@ export default {
set,
takeRight,
last,
sortBy,
orderBy,
range,
remove,
clone,
getFirstDefined,
sum,
makeTemplateComponent,
prefixAll,
groupBy,
isArray
isArray,
splitProps
}
function get (obj, path, def) {
@@ -61,7 +61,7 @@ function range (n) {
return arr
}
function sortBy (arr, funcs, dirs) {
function orderBy (arr, funcs, dirs) {
return arr.sort((a, b) => {
for (let i = 0; i < funcs.length; i++) {
const comp = funcs[i]
@@ -75,7 +75,9 @@ function sortBy (arr, funcs, dirs) {
return desc ? 1 : -1
}
}
return 0
return dirs[0]
? a.__index - b.__index
: b.__index - b.__index
})
}
@@ -128,10 +130,6 @@ function makeTemplateComponent (compClass) {
)
}
function prefixAll (obj) {
return obj
}
function groupBy (xs, key) {
return xs.reduce((rv, x, i) => {
const resKey = typeof key === 'function' ? key(x, i) : x[key]
@@ -167,3 +165,11 @@ function flattenDeep (arr, newArr = []) {
}
return newArr
}
function splitProps ({className, style, ...rest}) {
return {
className,
style,
rest
}
}
+86
View File
@@ -0,0 +1,86 @@
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
data={data}
columns={columns}
defaultPageSize={10}
/>
)
`
}
+107
View File
@@ -0,0 +1,107 @@
import React from 'react'
import _ from 'lodash'
import namor from 'namor'
import CodeHighlight from './components/codeHighlight'
import ReactTable from '../src/index'
export default () => {
const data = _.map(_.range(5553), d => {
return {
firstName: namor.generate({ words: 1, numLen: 0 }),
lastName: namor.generate({ words: 1, numLen: 0 }),
age: Math.floor(Math.random() * 30)
}
})
const columns = [{
header: 'Name',
columns: [{
header: 'First Name',
accessor: 'firstName',
render: row => {
return <span>{row.aggregated ? '...' : row.value}</span>
}
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age',
aggregate: vals => _.round(_.mean(vals)),
render: row => {
return <span>{row.aggregated ? `${row.value} (avg)` : row.value}</span>
}
}]
}, {
expander: true
}]
return (
<div>
<div className='table-wrap'>
<ReactTable
className='-striped -highlight'
data={data}
columns={columns}
defaultPageSize={10}
SubComponent={() => <span>Hello</span>}
/>
</div>
<div style={{textAlign: 'center'}}>
<br />
<em>Tip: Hold shift when sorting to multi-sort!</em>
</div>
<CodeHighlight>{() => getCode()}</CodeHighlight>
</div>
)
}
function getCode () {
return `
import ReactTable from 'react-table'
// Create some column definitions
const columns = [{
header: 'Name',
columns: [{
header: 'First Name',
accessor: 'firstName',
render: row => {
return <span>{row.aggregated ? '...' : row.value}</span>
}
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
}]
}, {
expander: true
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age',
aggregate: vals => _.round(_.mean(vals)),
render: row => {
return <span>{row.aggregated ? \`$\{row.value} (avg)\` : row.value}</span>
}
}]
}]
return (
<ReactTable
className='-striped -highlight'
data={data}
columns={columns}
defaultPageSize={10}
SubComponent={() => <span>Hello</span>}
pivotBy={['lastName']}
/>
)
`
}
@@ -24,14 +24,14 @@ export default () => {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName,
maxWidth: 400
width: 300
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age',
maxWidth: 60
minWidth: 400
}]
}]
@@ -68,14 +68,14 @@ const columns = [{
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName,
maxWidth: 400
width: 300
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age',
maxWidth: 60
minWidth: 400
}]
}]
+87
View File
@@ -0,0 +1,87 @@
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',
sort: 'desc'
}]
}]
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}
/>
)
`
}