Compare commits

..
10 Commits
Author SHA1 Message Date
Tanner Linsley e632dc314e 5.1.0 2017-02-10 16:47:09 -07:00
Tanner Linsley 46c4c6fdb2 Doc Updates 2017-02-10 16:46:54 -07:00
Tanner Linsley e702fc80ca defaultSorting changes and doc updates 2017-02-10 16:46:14 -07:00
Tanner Linsley beaa748187 5.0.4 2017-02-10 14:49:04 -07:00
Tanner Linsley cce77d24f2 Retain original data order if no sorting is found 2017-02-10 14:48:55 -07:00
Tanner Linsley 3984a540c1 5.0.3 2017-02-09 10:07:42 -07:00
Tanner Linsley 726fd52753 Adjust current page if available pages dips below current page
Fixes #69
2017-02-09 10:07:42 -07:00
Tanner LinsleyandGitHub 6a6d373526 Update README.md 2017-02-08 08:20:30 -07:00
TacticalCodingandTanner Linsley e11fdab457 Fix 'iterator must have unique key' warning on pivoting tables. (#56) (#66) 2017-02-07 10:31:40 -07:00
Jan ZandTanner Linsley 1e4f838326 'collapseOnChange' and 'freezeWhenExpanded' (#61)
* rough draft of "closeSubComponentOnDataChange" and "preventAutoSortWhenComponentIsOpen"

* changed flag name from ...component to ...subcomponent

* fixed problem with sorting not immediately being applied. passes tests now

* changed var names

* Cleanup and simplification

* Fix deps

* Better freezing strategy

* subcomponent reverse
2017-02-06 15:09:35 -07:00
19 changed files with 262 additions and 178 deletions
Vendored
BIN
View File
Binary file not shown.
+5 -2
View File
@@ -103,7 +103,7 @@ const columns = [{
}, {
header: 'Age',
accessor: 'age',
render: props => <span className='number'>props.value</span> // Custom cell components!
render: props => <span className='number'>{props.value}</span> // Custom cell components!
}, {
header: 'Friend Name',
accessor: d => d.friend.name // Custom value accessors!
@@ -135,6 +135,10 @@ These are all of the available props (and their default values) for the main `<R
defaultPageSize: 20,
showPageJump: true,
expanderColumnWidth: 35,
collapseOnSortingChange: false,
collapseOnPageChange: true,
freezeWhenExpanded: false,
defaultSorting: [],
// Controlled State Overrides (see Fully Controlled Component section)
page: undefined,
@@ -242,7 +246,6 @@ Or just define them as props
accessor: 'propertyName' or Accessor eg. (row) => row.propertyName,
id: 'myProperty', // Conditional - A unique ID is required if the accessor is not a string or if you would like to override the column name used in server-side calls
sortable: true,
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)
+1 -1
View File
@@ -16,7 +16,7 @@
<body>
<div id="root"></div>
<div id="error-display"></div>
<script src="static/preview.ee446fa2bd3fbfe7e38a.bundle.js"></script>
<script src="static/preview.2624d4fc5e8e37cbc8cc.bundle.js"></script>
</body>
</html>
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"version":3,"file":"static/preview.2624d4fc5e8e37cbc8cc.bundle.js","sources":["webpack:///static/preview.2624d4fc5e8e37cbc8cc.bundle.js"],"mappings":"AAAA;AAkuDA;AAkyDA;AA01EA;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;AAwsFA;AAyiGA;AA++CA;AA8yCA;AAgzCA;AAwhDA;AAiPA;AA+jFA","sourceRoot":""}
@@ -1 +0,0 @@
{"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":""}
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "react-table",
"version": "5.0.2",
"version": "5.1.0",
"description": "A fast, lightweight, opinionated table and datagrid built on React",
"license": "MIT",
"homepage": "https://github.com/tannerlinsley/react-table#readme",
@@ -62,6 +62,7 @@
"react-dom": "^15.4.2",
"react-json-tree": "^0.10.1",
"standard": "^8.0.0",
"storybook": "^0.0.0",
"stylus": "^0.54.5",
"uglifyify": "3.0.3"
},
+65 -74
View File
@@ -10,7 +10,9 @@ export default {
pivotValKey,
subRowsKey,
expanderColumnWidth,
SubComponent
SubComponent,
page,
pageSize
} = this.getResolvedState(nextProps, nextState)
// Determine Header Groups
@@ -212,29 +214,28 @@ export default {
resolvedData = groupRecursively(resolvedData, pivotBy)
}
const newPages = Math.ceil(resolvedData.length / pageSize)
return {
resolvedData,
pivotColumn,
allVisibleColumns,
headerGroups,
allDecoratedColumns,
hasHeaderGroups
hasHeaderGroups,
page: (page + 1) > newPages ? newPages - 1 : page
}
},
getSortedData (nextProps, nextState) {
getSortedData (resolvedState) {
const {
manual,
sorting,
allDecoratedColumns,
resolvedData
} = this.getResolvedState(nextProps, nextState)
const resolvedSorting = sorting.length ? sorting : this.getInitSorting(allDecoratedColumns)
} = resolvedState
// Resolve the data from either manual data or sorted data
return {
resolvedSorting,
sortedData: manual ? resolvedData : this.sortData(resolvedData, resolvedSorting)
sortedData: manual ? resolvedData : this.sortData(resolvedData, sorting)
}
},
@@ -247,27 +248,10 @@ export default {
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) {
if (!sorting.length) {
return data
}
const sorted = _.orderBy(data, sorting.map(sort => {
return row => {
if (row[sort.id] === null || row[sort.id] === undefined) {
@@ -275,7 +259,7 @@ export default {
}
return typeof row[sort.id] === 'string' ? row[sort.id].toLowerCase() : row[sort.id]
}
}), sorting.map(d => d.asc ? 'asc' : 'desc'))
}), sorting.map(d => !d.desc))
return sorted.map(row => {
if (!row[this.props.subRowsKey]) {
@@ -330,54 +314,25 @@ export default {
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 {
let newSorting = _.clone(sorting || []).map(d => {
d.desc = _.isSortingDesc(d)
return d
})
if (!_.isArray(column)) {
// Single-Sort
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 (existing.desc) {
if (additive) {
newSorting.splice(existingIndex, 1)
} else {
existing.asc = true
existing.desc = false
newSorting = [existing]
}
} else {
existing.desc = true
if (!additive) {
newSorting = [existing]
}
}
@@ -385,15 +340,51 @@ export default {
if (additive) {
newSorting.push({
id: column.id,
asc: true
desc: false
})
} else {
newSorting = [{
id: column.id,
asc: true
desc: false
}]
}
}
} else {
// Multi-Sort
const existingIndex = newSorting.findIndex(d => d.id === column[0].id)
// Existing Sorted Column
if (existingIndex > -1) {
const existing = newSorting[existingIndex]
if (existing.desc) {
if (additive) {
newSorting.splice(existingIndex, column.length)
} else {
column.forEach((d, i) => {
newSorting[existingIndex + i].desc = false
})
}
} else {
column.forEach((d, i) => {
newSorting[existingIndex + i].desc = true
})
}
if (!additive) {
newSorting = newSorting.slice(existingIndex, column.length)
}
} else {
// New Sort Column
if (additive) {
newSorting = newSorting.concat(column.map(d => ({
id: d.id,
desc: false
})))
} else {
newSorting = column.map(d => ({
id: d.id,
desc: false
}))
}
}
}
this.setStateWithData({
page: ((!sorting.length && newSorting.length) || !additive) ? 0 : this.state.page,
+57 -17
View File
@@ -18,6 +18,10 @@ export const ReactTableDefaults = {
defaultPageSize: 20,
showPageJump: true,
expanderColumnWidth: 35,
collapseOnSortingChange: false,
collapseOnPageChange: true,
freezeWhenExpanded: false,
defaultSorting: [],
// Controlled State Overrides
// page: undefined,
@@ -147,17 +151,17 @@ export default React.createClass({
return {
page: 0,
pageSize: this.props.defaultPageSize || 10,
sorting: [],
sorting: this.props.defaultSorting,
expandedRows: {}
}
},
getResolvedState (props, state) {
const resolvedState = {
...this.state,
...state,
...this.props,
...props
..._.compactObject(this.state),
..._.compactObject(state),
..._.compactObject(this.props),
..._.compactObject(props)
}
return resolvedState
},
@@ -173,6 +177,7 @@ export default React.createClass({
componentWillReceiveProps (nextProps, nextState) {
const oldState = this.getResolvedState()
const newState = this.getResolvedState(nextProps, nextState)
// Props that trigger a data update
if (
oldState.data !== newState.data ||
@@ -187,22 +192,53 @@ export default React.createClass({
setStateWithData (newState, cb) {
const oldState = this.getResolvedState()
const newResolvedState = this.getResolvedState({}, newState)
if (
oldState.resolvedData !== newResolvedState.resolvedData ||
oldState.sorting !== newResolvedState.sorting
) {
Object.assign(newState, this.getSortedData({}, 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) {
newState.pages = newResolvedState.manual ? newResolvedState.pages : Math.ceil(newResolvedState.resolvedData.length / newResolvedState.pageSize)
newResolvedState.pages = newResolvedState.manual ? newResolvedState.pages : Math.ceil(newResolvedState.resolvedData.length / newResolvedState.pageSize)
}
return this.setState(newState, cb)
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 ||
@@ -244,7 +280,7 @@ export default React.createClass({
loading,
pageSize,
page,
resolvedSorting,
sorting,
pages,
// Pivoting State
pivotValKey,
@@ -376,6 +412,7 @@ export default React.createClass({
if (column.pivotColumns) {
return (
<ThComponent
key={i}
className={classnames(
'rt-pivot-header',
classes
@@ -390,6 +427,7 @@ export default React.createClass({
}
return (
<ThComponent
key={i}
className={classnames(
'rt-expander-header',
classes
@@ -449,7 +487,7 @@ export default React.createClass({
}
const makeHeader = (column, i) => {
const sort = resolvedSorting.find(d => d.id === column.id)
const sort = sorting.find(d => d.id === column.id)
const show = typeof column.show === 'function' ? column.show() : column.show
const width = _.getFirstDefined(column.width, column.minWidth)
const maxWidth = _.getFirstDefined(column.width, column.maxWidth)
@@ -475,7 +513,7 @@ export default React.createClass({
if (column.expander) {
if (column.pivotColumns) {
const pivotSort = resolvedSorting.find(d => d.id === column.id)
const pivotSort = sorting.find(d => d.id === column.id)
return (
<ThComponent
key={i}
@@ -483,7 +521,7 @@ export default React.createClass({
'rt-pivot-header',
column.sortable && '-cursor-pointer',
classes,
pivotSort ? (pivotSort.asc ? '-sort-asc' : '-sort-desc') : ''
pivotSort ? (pivotSort.desc ? '-sort-asc' : '-sort-desc') : ''
)}
style={{
...styles,
@@ -536,7 +574,7 @@ export default React.createClass({
key={i}
className={classnames(
classes,
sort ? (sort.asc ? '-sort-asc' : '-sort-desc') : '',
sort ? (sort.desc ? '-sort-asc' : '-sort-desc') : '',
column.sortable && '-cursor-pointer',
!show && '-hidden',
)}
@@ -629,6 +667,7 @@ export default React.createClass({
const PivotCell = column.pivotRender
return (
<TdComponent
key={i2}
className={classnames(
'rt-pivot',
classes
@@ -669,6 +708,7 @@ export default React.createClass({
// Return the regular expander cell
return (
<TdComponent
key={i2}
className={classnames(
classes,
{hidden: !show}
+17 -1
View File
@@ -15,7 +15,9 @@ export default {
makeTemplateComponent,
groupBy,
isArray,
splitProps
splitProps,
compactObject,
isSortingDesc
}
function get (obj, path, def) {
@@ -173,3 +175,17 @@ function splitProps ({className, style, ...rest}) {
rest
}
}
function compactObject (obj) {
const newObj = {}
for (var key in obj) {
if (obj.hasOwnProperty(key) && obj[key] !== undefined && typeof obj[key] !== 'undefined') {
newObj[key] = obj[key]
}
}
return newObj
}
function isSortingDesc (d) {
return !!(d.sort === 'desc' || d.desc === true || d.asc === false)
}
+24 -4
View File
@@ -34,12 +34,24 @@ export default () => {
return (
<div>
<strong>Hey!</strong> Open your console! :)
<div className='table-wrap'>
<ReactTable
className='-striped -highlight'
data={data}
columns={columns}
defaultPageSize={10}
getTdProps={(state, rowInfo, column, instance) => {
return {
onMouseEnter: e => console.log('Cell - onMouseEnter', {
state,
rowInfo,
column,
instance,
event: e
})
}
}}
/>
</div>
<div style={{textAlign: 'center'}}>
@@ -53,9 +65,6 @@ export default () => {
function getCode () {
return `
import ReactTable from 'react-table'
// Create some column definitions
const columns = [{
header: 'Name',
columns: [{
@@ -74,12 +83,23 @@ const columns = [{
}]
}]
// Display your table!
return (
<ReactTable
className='-striped -highlight'
data={data}
columns={columns}
defaultPageSize={10}
getTdProps={(state, rowInfo, column, instance) => {
return {
onMouseEnter: e => console.log('Cell - onMouseEnter', {
state,
rowInfo,
column,
instance,
event: e
})
}
}}
/>
)
`
+2 -3
View File
@@ -79,8 +79,6 @@ const columns = [{
id: 'lastName',
accessor: d => d.lastName
}]
}, {
expander: true
}, {
header: 'Info',
columns: [{
@@ -91,6 +89,8 @@ const columns = [{
return <span>{row.aggregated ? \`$\{row.value} (avg)\` : row.value}</span>
}
}]
}, {
expander: true
}]
return (
@@ -100,7 +100,6 @@ return (
columns={columns}
defaultPageSize={10}
SubComponent={() => <span>Hello</span>}
pivotBy={['lastName']}
/>
)
`
+32 -28
View File
@@ -28,8 +28,7 @@ export default () => {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age',
sort: 'desc'
accessor: 'age'
}]
}]
@@ -41,6 +40,10 @@ export default () => {
data={data}
columns={columns}
defaultPageSize={10}
defaultSorting={[{
id: 'age',
desc: true
}]}
/>
</div>
<div style={{textAlign: 'center'}}>
@@ -54,34 +57,35 @@ export default () => {
function getCode () {
return `
import ReactTable from 'react-table'
// Create some column definitions
const columns = [{
header: 'Name',
columns: [{
header: 'First Name',
accessor: 'firstName'
const columns = [{
header: 'Name',
columns: [{
header: 'First Name',
accessor: 'firstName'
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
}]
}, {
header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
}]
}]
}, {
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
}]
}]
// Display your table!
return (
<ReactTable
data={data}
columns={columns}
defaultPageSize={10}
/>
)
return (
<ReactTable
className='-striped -highlight'
data={data}
columns={columns}
defaultPageSize={10}
defaultSorting={[{
id: 'age',
desc: true
}]}
/>
)
`
}
+11 -2
View File
@@ -100,15 +100,24 @@ const columns = [{
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
accessor: 'age',
aggregate: vals => _.round(_.mean(vals)),
render: row => {
return <span>{row.aggregated ? \`\${row.value} (avg)\` : row.value}</span>
}
}, {
header: 'Visits',
accessor: 'visits',
aggregate: vals => _.sum(vals)
}]
}]
export default (
return (
<ReactTable
data={data}
columns={columns}
defaultPageSize={10}
className='-striped -highlight'
pivotBy={['firstName', 'lastName']}
SubComponent={(row) => {
return (
+12 -31
View File
@@ -50,12 +50,6 @@ export default () => {
className='-striped -highlight'
defaultPageSize={10}
pivotBy={['firstName', 'lastName']}
// expandedRows={{
// 2: true,
// 3: {
// 2: true
// }
// }}
/>
</div>
<div style={{textAlign: 'center'}}>
@@ -83,38 +77,25 @@ const columns = [{
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
accessor: 'age',
aggregate: vals => _.round(_.mean(vals)),
render: row => {
return <span>{row.aggregated ? \`\${row.value} (avg)\` : row.value}</span>
}
}, {
header: 'Visits',
accessor: 'visits',
aggregate: vals => _.sum(vals)
}]
}]
export default (
return (
<ReactTable
data={data}
columns={columns}
className='-striped -highlight'
defaultPageSize={10}
SubComponent={(row) => {
return (
<div style={{padding: '20px'}}>
<em>You can put any component you want here, even another React Table!</em>
<br />
<br />
<ReactTable
data={data}
columns={columns}
defaultPageSize={3}
showPagination={false}
SubComponent={(row) => {
return (
<div style={{padding: '20px'}}>
<em>It even has access to the row data: </em>
<CodeHighlight>{() => JSON.stringify(row, null, 2)}</CodeHighlight>
</div>
)
}}
/>
</div>
)
}}
pivotBy={['firstName', 'lastName']}
/>
)
`
+11 -2
View File
@@ -100,15 +100,24 @@ const columns = [{
header: 'Info',
columns: [{
header: 'Age',
accessor: 'age'
accessor: 'age',
aggregate: vals => _.round(_.mean(vals)),
render: row => {
return <span>{row.aggregated ? \`\${row.value} (avg)\` : row.value}</span>
}
}, {
header: 'Visits',
accessor: 'visits',
aggregate: vals => _.sum(vals)
}]
}]
export default (
return (
<ReactTable
data={data}
columns={columns}
defaultPageSize={10}
className='-striped -highlight'
pivotBy={['firstName', 'lastName']}
SubComponent={(row) => {
return (
+7
View File
@@ -40,6 +40,13 @@ export default () => {
data={data}
columns={columns}
defaultPageSize={10}
getTdProps={() => {
return {
onClick: () => {
console.log('clicked')
}
}
}}
/>
</div>
<div style={{textAlign: 'center'}}>
+1 -1
View File
@@ -125,4 +125,4 @@ export default (
/>
)
`
}
}
+5 -1
View File
@@ -2998,7 +2998,7 @@ indexof@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
inflight@^1.0.4:
inflight@^1.0.4, inflight@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
dependencies:
@@ -5248,6 +5248,10 @@ standard@^8.0.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
storybook@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/storybook/-/storybook-0.0.0.tgz#b3b9508fb99fd83615674917583cfe9e1532931d"
stream-browserify@^2.0.0, stream-browserify@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"