mirror of
https://github.com/gosticks/react-table.git
synced 2026-03-26 15:44:25 +00:00
Better custom sorting
This commit is contained in:
@@ -64,5 +64,5 @@ configure(() => {
|
||||
.add('Custom Filtering', Filtering)
|
||||
.add('Controlled Component', ControlledTable)
|
||||
.add('Editable table', EditableTable)
|
||||
.add('Sub Rows', SubRows)
|
||||
// .add('Sub Rows', SubRows)
|
||||
}, module)
|
||||
|
||||
29
README.md
29
README.md
@@ -166,6 +166,7 @@ These are all of the available props (and their default values) for the main `<R
|
||||
},
|
||||
resizable: true,
|
||||
defaultResizing: [],
|
||||
defaultSortMethod: undefined,
|
||||
|
||||
// Controlled State Overrides (see Fully Controlled Component section)
|
||||
page: undefined,
|
||||
@@ -770,6 +771,34 @@ Sorting comes built in with React-Table. Click column header to sort by its colu
|
||||
## 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!
|
||||
|
||||
## Custom Sorting Algorithm
|
||||
To override the default sorting algorithm for the whole table use the `defaultSortMethod` prop.
|
||||
To override the sorting algorithm for a single column, use the `sortMethod` column property.
|
||||
|
||||
Supply a function that implements the native javascript [`Array.sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) interface. This is React Table's default sorting algorithm:
|
||||
- `a` the first value to compare
|
||||
- `b` the second value to compare
|
||||
- `dir` the
|
||||
```javascript
|
||||
defaultSortMethod = (a, b) => {
|
||||
// force null and undefined to the bottom
|
||||
a = (a === null || a === undefined) ? -Infinity : a
|
||||
b = (b === null || b === undefined) ? -Infinity : b
|
||||
// force any string values to lowercase
|
||||
a = a === 'string' ? a.toLowerCase() : a
|
||||
b = b === 'string' ? b.toLowerCase() : b
|
||||
// Return either 1 or -1 to indicate a sort priority
|
||||
if (a > b) {
|
||||
return 1
|
||||
}
|
||||
if (a < b) {
|
||||
return -1
|
||||
}
|
||||
// returning 0 or undefined will use any subsequent column sorting methods or the row index as a tiebreaker
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
## Filtering
|
||||
Filtering can be enabled by setting the `showFilters` option on the table.
|
||||
|
||||
|
||||
@@ -26,6 +26,23 @@ export default {
|
||||
const id = filter.pivotId || filter.id
|
||||
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
|
||||
},
|
||||
defaultSortMethod: (a, b) => {
|
||||
// force null and undefined to the bottom
|
||||
a = (a === null || a === undefined) ? -Infinity : a
|
||||
b = (b === null || b === undefined) ? -Infinity : b
|
||||
// force any string values to lowercase
|
||||
a = a === 'string' ? a.toLowerCase() : a
|
||||
b = b === 'string' ? b.toLowerCase() : b
|
||||
// Return either 1 or -1 to indicate a sort priority
|
||||
if (a > b) {
|
||||
return 1
|
||||
}
|
||||
if (a < b) {
|
||||
return -1
|
||||
}
|
||||
// returning 0, undefined or any falsey value will use subsequent sorts or the index as a tiebreaker
|
||||
return 0
|
||||
},
|
||||
resizable: true,
|
||||
defaultResizing: [],
|
||||
|
||||
@@ -119,12 +136,6 @@ export default {
|
||||
footerStyle: {},
|
||||
getFooterProps: emptyObj,
|
||||
filterMethod: undefined,
|
||||
sortMethod: value => {
|
||||
if (value === null || value === undefined) {
|
||||
return -Infinity
|
||||
}
|
||||
return typeof value === 'string' ? value.toLowerCase() : value
|
||||
},
|
||||
hideFilter: false
|
||||
},
|
||||
|
||||
|
||||
26
src/index.js
26
src/index.js
@@ -561,22 +561,18 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
}
|
||||
} else if (cellInfo.aggregated) {
|
||||
resolvedCell = _.normalizeComponent(ResolvedAggregatedComponent, cellInfo, value)
|
||||
} else if (column.expander) {
|
||||
resolvedCell = _.normalizeComponent(ResolvedPivotComponent, cellInfo, value)
|
||||
if (cellInfo.subRows) {
|
||||
resolvedCell = null
|
||||
}
|
||||
|
||||
if (cellInfo.expander) {
|
||||
resolvedCell = _.normalizeComponent(ResolvedExpanderComponent, cellInfo, row[pivotValKey])
|
||||
if (pivotBy) {
|
||||
if (cellInfo.groupedByPivot) {
|
||||
resolvedCell = null
|
||||
}
|
||||
if (!cellInfo.subRows && !SubComponent) {
|
||||
resolvedCell = null
|
||||
}
|
||||
}
|
||||
if (!cellInfo.subRows && !SubComponent) {
|
||||
resolvedCell = null
|
||||
}
|
||||
// if (pivotBy) {
|
||||
// if (cellInfo.groupedByPivot) {
|
||||
// resolvedCell = null
|
||||
// }
|
||||
// if (!cellInfo.subRows && !SubComponent) {
|
||||
// resolvedCell = null
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// Return the cell
|
||||
|
||||
@@ -52,18 +52,18 @@ export default Base => class extends Base {
|
||||
|
||||
const makeDecoratedColumn = (column) => {
|
||||
let dcol
|
||||
// if (column.expander) {
|
||||
// dcol = {
|
||||
// ...this.props.column,
|
||||
// ...this.props.expanderDefaults,
|
||||
// ...column
|
||||
// }
|
||||
// } else {
|
||||
if (column.expander) {
|
||||
dcol = {
|
||||
...this.props.column,
|
||||
...this.props.expanderDefaults,
|
||||
...column
|
||||
}
|
||||
} else {
|
||||
dcol = {
|
||||
...this.props.column,
|
||||
...column
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
if (typeof dcol.accessor === 'string') {
|
||||
dcol.id = dcol.id || dcol.accessor
|
||||
@@ -279,12 +279,12 @@ export default Base => class extends Base {
|
||||
allDecoratedColumns
|
||||
} = resolvedState
|
||||
|
||||
const sortersByID = {}
|
||||
const sortMethodsByColumnID = {}
|
||||
|
||||
allDecoratedColumns
|
||||
.filter(col => col.sortMethod)
|
||||
.forEach(col => {
|
||||
sortersByID[col.id] = col.sortMethod
|
||||
sortMethodsByColumnID[col.id] = col.sortMethod
|
||||
})
|
||||
|
||||
// Resolve the data from either manual data or sorted data
|
||||
@@ -298,7 +298,7 @@ export default Base => class extends Base {
|
||||
allVisibleColumns
|
||||
),
|
||||
sorting,
|
||||
sortersByID
|
||||
sortMethodsByColumnID
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -366,32 +366,36 @@ export default Base => class extends Base {
|
||||
return filteredData
|
||||
}
|
||||
|
||||
sortData (data, sorting, sortersByID = {}) {
|
||||
sortData (data, sorting, sortMethodsByColumnID = {}) {
|
||||
if (!sorting.length) {
|
||||
return data
|
||||
}
|
||||
|
||||
const sorted = _.orderBy(data, sorting.map(sort => {
|
||||
// Support custom sorting methods for each column
|
||||
if (sortersByID[sort.id]) {
|
||||
return row => {
|
||||
return sortersByID[sort.id](row[sort.id])
|
||||
const sorted = (this.props.orderByMethod || _.orderBy)(
|
||||
data,
|
||||
sorting.map(sort => {
|
||||
// Support custom sorting methods for each column
|
||||
if (sortMethodsByColumnID[sort.id]) {
|
||||
return (a, b) => {
|
||||
return sortMethodsByColumnID[sort.id](a[sort.id], b[sort.id])
|
||||
}
|
||||
}
|
||||
}
|
||||
return row => {
|
||||
return this.props.sortMethod[sort.id](row[sort.id])
|
||||
}
|
||||
}), sorting.map(d => !d.desc))
|
||||
return (a, b) => {
|
||||
return this.props.defaultSortMethod(a[sort.id], b[sort.id])
|
||||
}
|
||||
}),
|
||||
sorting.map(d => !d.desc),
|
||||
this.props.indexKey
|
||||
)
|
||||
|
||||
return sorted.map(row => {
|
||||
sorted.forEach(row => {
|
||||
if (!row[this.props.subRowsKey]) {
|
||||
return row
|
||||
}
|
||||
return {
|
||||
...row,
|
||||
[this.props.subRowsKey]: this.sortData(row[this.props.subRowsKey], sorting, sortersByID)
|
||||
return
|
||||
}
|
||||
row[this.props.subRowsKey] = this.sortData(row[this.props.subRowsKey], sorting, sortMethodsByColumnID)
|
||||
})
|
||||
|
||||
return sorted
|
||||
}
|
||||
|
||||
getMinRows () {
|
||||
|
||||
19
src/utils.js
19
src/utils.js
@@ -64,23 +64,20 @@ function range (n) {
|
||||
return arr
|
||||
}
|
||||
|
||||
function orderBy (arr, funcs, dirs) {
|
||||
return arr.sort((a, b) => {
|
||||
function orderBy (arr, funcs, dirs, indexKey) {
|
||||
return arr.sort((rowA, rowB) => {
|
||||
for (let i = 0; i < funcs.length; i++) {
|
||||
const comp = funcs[i]
|
||||
const ca = comp(a)
|
||||
const cb = comp(b)
|
||||
const desc = dirs[i] === false || dirs[i] === 'desc'
|
||||
if (ca > cb) {
|
||||
return desc ? -1 : 1
|
||||
}
|
||||
if (ca < cb) {
|
||||
return desc ? 1 : -1
|
||||
const sortInt = comp(rowA, rowB)
|
||||
if (sortInt) {
|
||||
return desc ? -sortInt : sortInt
|
||||
}
|
||||
}
|
||||
// Use the row index for tie breakers
|
||||
return dirs[0]
|
||||
? a.__index - b.__index
|
||||
: b.__index - a.__index
|
||||
? rowA[indexKey] - rowB[indexKey]
|
||||
: rowB[indexKey] - rowA[indexKey]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -17,17 +17,25 @@ export default () => {
|
||||
const columns = [{
|
||||
Header: 'Name',
|
||||
columns: [{
|
||||
Header: 'First Name (Sorted by Length)',
|
||||
Header: 'First Name (Sorted by Length, A-Z)',
|
||||
accessor: 'firstName',
|
||||
sortMethod: value => {
|
||||
return value.length
|
||||
sortMethod: (a, b) => {
|
||||
if (a.length === b.length) {
|
||||
return a > b ? 1 : -1
|
||||
}
|
||||
return a.length > b.length ? 1 : -1
|
||||
}
|
||||
}, {
|
||||
Header: 'Last Name (Sorted by last letter)',
|
||||
Header: 'Last Name (Sorted in reverse, A-Z)',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName,
|
||||
sortMethod: value => {
|
||||
return value.substring([value.length - 1])
|
||||
sortMethod: (a, b) => {
|
||||
if (a === b) {
|
||||
return 0
|
||||
}
|
||||
const aReverse = a.split('').reverse().join('')
|
||||
const bReverse = b.split('').reverse().join('')
|
||||
return aReverse > bReverse ? 1 : -1
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
|
||||
Reference in New Issue
Block a user