mirror of
https://github.com/gosticks/react-table.git
synced 2026-08-19 16:30:21 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8aec06496 | ||
|
|
8efeef3b8b | ||
|
|
8004e7a85f | ||
|
|
84af88916a | ||
|
|
9846bf1905 | ||
|
|
bf88dda5dc | ||
|
|
7f5779a833 | ||
|
|
8792bd95a0 | ||
|
|
098b1fec51 | ||
|
|
750cfe3ac4 | ||
|
|
370f443347 | ||
|
|
67f8c9adf1 | ||
|
|
867cc25899 | ||
|
|
639630946c | ||
|
|
688afd2947 | ||
|
|
bbebf02a55 | ||
|
|
f4d3c2d7bb | ||
|
|
f8634e771e | ||
|
|
ba74ab3ce8 |
@@ -28,6 +28,11 @@
|
||||
<img alt="" src="https://img.shields.io/badge/%24-Donate-brightgreen.svg" />
|
||||
</a>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
[](https://app.codesponsor.io/link/zpmS8V9r31sBSCeVzP7Wm6Sr/tannerlinsley/react-table)
|
||||
|
||||
## Versions
|
||||
This document refers to version 6.x.x of react-table.
|
||||
|
||||
@@ -588,12 +593,21 @@ This makes it extremely easy to add, say... a row click callback!
|
||||
<ReactTable
|
||||
getTdProps={(state, rowInfo, column, instance) => {
|
||||
return {
|
||||
onClick: e => {
|
||||
onClick: (e, handleOriginal) => {
|
||||
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)
|
||||
|
||||
// IMPORTANT! React-Table uses onClick internally to trigger
|
||||
// events like expanding SubComponents and pivots.
|
||||
// By default a custom 'onClick' handler will override this functionality.
|
||||
// If you want to fire the original onClick handler, call the
|
||||
// 'handleOriginal' function.
|
||||
if (handleOriginal) {
|
||||
handleOriginal()
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -766,7 +780,7 @@ Here are the props and their corresponding callbacks that control the state of t
|
||||
onPageSizeChange={(pageSize, pageIndex) => {...}} // Called when the pageSize is changed by the user. The resolve page is also sent to maintain approximate position in the data
|
||||
onSortedChange={(newSorted, column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. If the column is a pivoted column, `column` will be an array of columns
|
||||
onExpandedChange={(newExpanded, index, event) => {...}} // Called when an expander is clicked. Use this to manage `expandedRows`
|
||||
onFilteredChange={(column, value) => {...}} // Called when a user enters a value into a filter input field or the value passed to the onFiltersChange handler by the filterRender option.
|
||||
onFilteredChange={(column, value) => {...}} // Called when a user enters a value into a filter input field or the value passed to the onFiltersChange handler by the Filter option.
|
||||
onResizedChange={(newResized, event) => {...}} // Called when a user clicks on a resizing component (the right edge of a column header)
|
||||
/>
|
||||
```
|
||||
@@ -858,7 +872,7 @@ If you want to override a particular column's filtering method, you can set the
|
||||
|
||||
By default, `filterMethod` is passed a single row of data at a time, and you are responsible for returning `true` or `false`, indicating whether it should be shown.
|
||||
|
||||
Alternatively, you can set `filterAll` to `true`, and `fitlerMethod` will be passed the entire array of rows to be filtered, and you will then be responsible for returning the new filtered array. This is extremely handy when you need to utilize a utility like fuzzy matching that requires the entire array of items.
|
||||
Alternatively, you can set `filterAll` to `true`, and `filterMethod` will be passed the entire array of rows to be filtered, and you will then be responsible for returning the new filtered array. This is extremely handy when you need to utilize a utility like fuzzy matching that requires the entire array of items.
|
||||
|
||||
To completely override the filter that is shown, you can set the `Filter` column option. Using this option you can specify the JSX that is shown. The option is passed an `onChange` method which must be called with the the value that you wan't to pass to the `filterMethod` option whenever the filter has changed.
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ const columns = [{
|
||||
}, {
|
||||
Header: 'Last Name',
|
||||
id: 'lastName',
|
||||
accessor: d => d.lastName
|
||||
accessor: d => d.lastName,
|
||||
width: 170
|
||||
}]
|
||||
}, {
|
||||
Header: 'Info',
|
||||
@@ -31,21 +32,31 @@ const columns = [{
|
||||
}]
|
||||
}]
|
||||
|
||||
function makeDefaultState()
|
||||
{
|
||||
return {
|
||||
sorted: [],
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
expanded: {},
|
||||
resized: [],
|
||||
filtered: []
|
||||
}
|
||||
}
|
||||
|
||||
class Story extends React.PureComponent {
|
||||
constructor () {
|
||||
super()
|
||||
this.state = {
|
||||
sorted: [],
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
expanded: {},
|
||||
resized: [],
|
||||
filtered: []
|
||||
}
|
||||
this.state = makeDefaultState()
|
||||
this.resetState = this.resetState.bind(this)
|
||||
}
|
||||
resetState(){
|
||||
this.setState(makeDefaultState())
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div className='table-wrap'>
|
||||
<ReactTable
|
||||
className='-striped -highlight'
|
||||
@@ -70,6 +81,7 @@ class Story extends React.PureComponent {
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<div style={{float:'right'}}><button onClick={this.resetState}>Reset State</button></div>
|
||||
<pre><code><strong>this.state ===</strong> {JSON.stringify(this.state, null, 2)}</code></pre>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-table",
|
||||
"version": "6.5.1",
|
||||
"version": "6.5.3",
|
||||
"description": "A fast, lightweight, opinionated table and datagrid built on React",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/tannerlinsley/react-table#readme",
|
||||
|
||||
+40
-26
@@ -261,8 +261,8 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
|
||||
const flexStyles = {
|
||||
flex: `${flex} 0 auto`,
|
||||
width: `${width}px`,
|
||||
maxWidth: `${maxWidth}px`,
|
||||
width: _.asPx(width),
|
||||
maxWidth: _.asPx(maxWidth),
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -352,8 +352,8 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
const isResizable = _.getFirstDefined(column.resizable, resizable, false)
|
||||
const resizer = isResizable
|
||||
? (<ResizerComponent
|
||||
onMouseDown={e => this.resizeColumnStart(column, e, false)}
|
||||
onTouchStart={e => this.resizeColumnStart(column, e, true)}
|
||||
onMouseDown={e => this.resizeColumnStart(e, column, false)}
|
||||
onTouchStart={e => this.resizeColumnStart(e, column, true)}
|
||||
{...resizerProps}
|
||||
/>)
|
||||
: null
|
||||
@@ -376,8 +376,8 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
style={{
|
||||
...styles,
|
||||
flex: `${width} 0 auto`,
|
||||
width: `${width}px`,
|
||||
maxWidth: `${maxWidth}px`,
|
||||
width: _.asPx(width),
|
||||
maxWidth: _.asPx(maxWidth),
|
||||
}}
|
||||
toggleSort={e => {
|
||||
isSortable && this.sortColumn(column, e.shiftKey)
|
||||
@@ -475,8 +475,8 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
style={{
|
||||
...styles,
|
||||
flex: `${width} 0 auto`,
|
||||
width: `${width}px`,
|
||||
maxWidth: `${maxWidth}px`,
|
||||
width: _.asPx(width),
|
||||
maxWidth: _.asPx(maxWidth),
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
@@ -574,7 +574,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
|
||||
const value = cellInfo.value
|
||||
|
||||
let interactionProps
|
||||
let useOnExpanderClick
|
||||
let isBranch
|
||||
let isPreview
|
||||
|
||||
@@ -626,17 +626,10 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
if (cellInfo.pivoted || cellInfo.expander) {
|
||||
// Make it expandable by defualt
|
||||
cellInfo.expandable = true
|
||||
interactionProps = {
|
||||
onClick: onExpanderClick,
|
||||
}
|
||||
useOnExpanderClick = true
|
||||
// If pivoted, has no subRows, and does not have a subComponent, do not make expandable
|
||||
if (cellInfo.pivoted) {
|
||||
if (!cellInfo.subRows) {
|
||||
if (!SubComponent) {
|
||||
cellInfo.expandable = false
|
||||
interactionProps = {}
|
||||
}
|
||||
}
|
||||
if (cellInfo.pivoted && !cellInfo.subRows && !SubComponent) {
|
||||
cellInfo.expandable = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,6 +686,27 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
}
|
||||
}
|
||||
|
||||
const resolvedOnExpanderClick = useOnExpanderClick
|
||||
? onExpanderClick
|
||||
: () => {}
|
||||
|
||||
// If there are multiple onClick events, make sure they don't override eachother. This should maybe be expanded to handle all function attributes
|
||||
const interactionProps = {
|
||||
onClick: resolvedOnExpanderClick,
|
||||
}
|
||||
|
||||
if (tdProps.rest.onClick) {
|
||||
interactionProps.onClick = e => {
|
||||
tdProps.rest.onClick(e, () => resolvedOnExpanderClick(e))
|
||||
}
|
||||
}
|
||||
|
||||
if (columnProps.rest.onClick) {
|
||||
interactionProps.onClick = e => {
|
||||
columnProps.rest.onClick(e, () => resolvedOnExpanderClick(e))
|
||||
}
|
||||
}
|
||||
|
||||
// Return the cell
|
||||
return (
|
||||
<TdComponent
|
||||
@@ -706,12 +720,12 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
style={{
|
||||
...styles,
|
||||
flex: `${width} 0 auto`,
|
||||
width: `${width}px`,
|
||||
maxWidth: `${maxWidth}px`,
|
||||
width: _.asPx(width),
|
||||
maxWidth: _.asPx(maxWidth),
|
||||
}}
|
||||
{...interactionProps}
|
||||
{...tdProps.rest}
|
||||
{...columnProps.rest}
|
||||
{...interactionProps}
|
||||
>
|
||||
{resolvedCell}
|
||||
</TdComponent>
|
||||
@@ -798,8 +812,8 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
style={{
|
||||
...styles,
|
||||
flex: `${flex} 0 auto`,
|
||||
width: `${width}px`,
|
||||
maxWidth: `${maxWidth}px`,
|
||||
width: _.asPx(width),
|
||||
maxWidth: _.asPx(maxWidth),
|
||||
}}
|
||||
{...tdProps.rest}
|
||||
>
|
||||
@@ -878,8 +892,8 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
style={{
|
||||
...styles,
|
||||
flex: `${width} 0 auto`,
|
||||
width: `${width}px`,
|
||||
maxWidth: `${maxWidth}px`,
|
||||
width: _.asPx(width),
|
||||
maxWidth: _.asPx(maxWidth),
|
||||
}}
|
||||
{...columnProps.rest}
|
||||
{...tFootTdProps.rest}
|
||||
|
||||
@@ -21,7 +21,6 @@ $expandSize = 7px
|
||||
.rt-thead
|
||||
flex: 1 0 auto
|
||||
display: flex
|
||||
// overflow-y: scroll
|
||||
flex-direction: column
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
@@ -303,28 +302,6 @@ $expandSize = 7px
|
||||
font-weight: normal
|
||||
outline:none
|
||||
|
||||
input:not([type="checkbox"]):not([type="radio"])
|
||||
select
|
||||
appearance: none
|
||||
&::-ms-expand
|
||||
display: none
|
||||
|
||||
.select-wrap
|
||||
position:relative
|
||||
display:inline-block
|
||||
select
|
||||
padding: 5px 15px 5px 7px
|
||||
min-width:100px
|
||||
&:after
|
||||
content: ''
|
||||
position: absolute
|
||||
right: 8px
|
||||
top: 50%
|
||||
transform: translate(0, -50%)
|
||||
border-color: #999 transparent transparent
|
||||
border-style: solid
|
||||
border-width: 5px 5px 2.5px
|
||||
|
||||
.rt-resizing
|
||||
.rt-th
|
||||
.rt-td
|
||||
|
||||
+16
-1
@@ -99,6 +99,11 @@ export default Base =>
|
||||
Object.assign(newResolvedState, this.getSortedData(newResolvedState))
|
||||
}
|
||||
|
||||
// Set page to 0 if filters change
|
||||
if (oldState.filtered !== newResolvedState.filtered) {
|
||||
newResolvedState.page = 0
|
||||
}
|
||||
|
||||
// Calculate pageSize all the time
|
||||
if (newResolvedState.sortedData) {
|
||||
newResolvedState.pages = newResolvedState.manual
|
||||
@@ -114,6 +119,16 @@ export default Base =>
|
||||
)
|
||||
}
|
||||
|
||||
return this.setState(newResolvedState, cb)
|
||||
return this.setState(newResolvedState, () => {
|
||||
cb && cb()
|
||||
if (
|
||||
oldState.page !== newResolvedState.page ||
|
||||
oldState.pageSize !== newResolvedState.pageSize ||
|
||||
oldState.sorted !== newResolvedState.sorted ||
|
||||
oldState.filtered !== newResolvedState.filtered
|
||||
) {
|
||||
this.fireFetchData()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+58
-42
@@ -55,7 +55,7 @@ export default Base =>
|
||||
columnsWithExpander = [expanderColumn, ...columnsWithExpander]
|
||||
}
|
||||
|
||||
const makeDecoratedColumn = column => {
|
||||
const makeDecoratedColumn = (column, parentColumn) => {
|
||||
let dcol
|
||||
if (column.expander) {
|
||||
dcol = {
|
||||
@@ -70,6 +70,16 @@ export default Base =>
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure minWidth is not greater than maxWidth if set
|
||||
if (dcol.maxWidth < dcol.minWidth) {
|
||||
dcol.minWidth = dcol.maxWidth
|
||||
}
|
||||
|
||||
if (parentColumn) {
|
||||
dcol.parentColumn = parentColumn
|
||||
}
|
||||
|
||||
// First check for string accessor
|
||||
if (typeof dcol.accessor === 'string') {
|
||||
dcol.id = dcol.id || dcol.accessor
|
||||
const accessorString = dcol.accessor
|
||||
@@ -77,6 +87,7 @@ export default Base =>
|
||||
return dcol
|
||||
}
|
||||
|
||||
// Fall back to functional accessor (but require an ID)
|
||||
if (dcol.accessor && !dcol.id) {
|
||||
console.warn(dcol)
|
||||
throw new Error(
|
||||
@@ -84,30 +95,26 @@ export default Base =>
|
||||
)
|
||||
}
|
||||
|
||||
// Fall back to an undefined accessor
|
||||
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)
|
||||
const decorateAndAddToAll = (column, parentColumn) => {
|
||||
const decoratedColumn = makeDecoratedColumn(column, parentColumn)
|
||||
allDecoratedColumns.push(decoratedColumn)
|
||||
return decoratedColumn
|
||||
}
|
||||
let allDecoratedColumns = []
|
||||
const allDecoratedColumns = []
|
||||
const decoratedColumns = columnsWithExpander.map((column, i) => {
|
||||
if (column.columns) {
|
||||
return {
|
||||
...column,
|
||||
columns: column.columns.map(decorateAndAddToAll),
|
||||
columns: column.columns.map(d => decorateAndAddToAll(d, column)),
|
||||
}
|
||||
} else {
|
||||
return decorateAndAddToAll(column)
|
||||
@@ -156,8 +163,17 @@ export default Base =>
|
||||
}
|
||||
})
|
||||
|
||||
let PivotParentColumn = pivotColumns.reduce(
|
||||
(prev, current) =>
|
||||
prev && prev === current.parentColumn && current.parentColumn,
|
||||
pivotColumns[0].parentColumn
|
||||
)
|
||||
|
||||
let PivotGroupHeader = hasHeaderGroups && PivotParentColumn.Header
|
||||
PivotGroupHeader = PivotGroupHeader || (() => <strong>Pivoted</strong>)
|
||||
|
||||
let pivotColumnGroup = {
|
||||
header: () => <strong>Group</strong>,
|
||||
Header: PivotGroupHeader,
|
||||
columns: pivotColumns.map(col => ({
|
||||
...this.props.pivotDefaults,
|
||||
...col,
|
||||
@@ -438,7 +454,6 @@ export default Base =>
|
||||
}
|
||||
this.setStateWithData(newState, () => {
|
||||
onPageChange && onPageChange(page)
|
||||
this.fireFetchData()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -457,7 +472,6 @@ export default Base =>
|
||||
},
|
||||
() => {
|
||||
onPageSizeChange && onPageSizeChange(newPageSize, newPage)
|
||||
this.fireFetchData()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -570,7 +584,6 @@ export default Base =>
|
||||
},
|
||||
() => {
|
||||
onSortedChange && onSortedChange(newSorted, column, additive)
|
||||
this.fireFetchData()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -599,12 +612,12 @@ export default Base =>
|
||||
},
|
||||
() => {
|
||||
onFilteredChange && onFilteredChange(newFiltering, column, value)
|
||||
this.fireFetchData()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
resizeColumnStart (column, event, isTouch) {
|
||||
resizeColumnStart (event, column, isTouch) {
|
||||
event.stopPropagation()
|
||||
const parentWidth = event.target.parentElement.getBoundingClientRect()
|
||||
.width
|
||||
|
||||
@@ -615,6 +628,7 @@ export default Base =>
|
||||
pageX = event.pageX
|
||||
}
|
||||
|
||||
this.trapEvents = true
|
||||
this.setStateWithData(
|
||||
{
|
||||
currentlyResizing: {
|
||||
@@ -637,33 +651,8 @@ export default Base =>
|
||||
)
|
||||
}
|
||||
|
||||
resizeColumnEnd (event) {
|
||||
let isTouch = event.type === 'touchend' || event.type === 'touchcancel'
|
||||
|
||||
if (isTouch) {
|
||||
document.removeEventListener('touchmove', this.resizeColumnMoving)
|
||||
document.removeEventListener('touchcancel', this.resizeColumnEnd)
|
||||
document.removeEventListener('touchend', this.resizeColumnEnd)
|
||||
}
|
||||
|
||||
// If its a touch event clear the mouse one's as well because sometimes
|
||||
// the mouseDown event gets called as well, but the mouseUp event doesn't
|
||||
document.removeEventListener('mousemove', this.resizeColumnMoving)
|
||||
document.removeEventListener('mouseup', this.resizeColumnEnd)
|
||||
document.removeEventListener('mouseleave', this.resizeColumnEnd)
|
||||
|
||||
// The touch events don't propagate up to the sorting's onMouseDown event so
|
||||
// no need to prevent it from happening or else the first click after a touch
|
||||
// event resize will not sort the column.
|
||||
if (!isTouch) {
|
||||
this.setStateWithData({
|
||||
skipNextSort: true,
|
||||
currentlyResizing: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resizeColumnMoving (event) {
|
||||
event.stopPropagation()
|
||||
const { onResizedChange } = this.props
|
||||
const { resized, currentlyResizing } = this.getResolvedState()
|
||||
|
||||
@@ -698,4 +687,31 @@ export default Base =>
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
resizeColumnEnd (event) {
|
||||
event.stopPropagation()
|
||||
let isTouch = event.type === 'touchend' || event.type === 'touchcancel'
|
||||
|
||||
if (isTouch) {
|
||||
document.removeEventListener('touchmove', this.resizeColumnMoving)
|
||||
document.removeEventListener('touchcancel', this.resizeColumnEnd)
|
||||
document.removeEventListener('touchend', this.resizeColumnEnd)
|
||||
}
|
||||
|
||||
// If its a touch event clear the mouse one's as well because sometimes
|
||||
// the mouseDown event gets called as well, but the mouseUp event doesn't
|
||||
document.removeEventListener('mousemove', this.resizeColumnMoving)
|
||||
document.removeEventListener('mouseup', this.resizeColumnEnd)
|
||||
document.removeEventListener('mouseleave', this.resizeColumnEnd)
|
||||
|
||||
// The touch events don't propagate up to the sorting's onMouseDown event so
|
||||
// no need to prevent it from happening or else the first click after a touch
|
||||
// event resize will not sort the column.
|
||||
if (!isTouch) {
|
||||
this.setStateWithData({
|
||||
skipNextSort: true,
|
||||
currentlyResizing: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-3
@@ -19,6 +19,7 @@ export default {
|
||||
compactObject,
|
||||
isSortingDesc,
|
||||
normalizeComponent,
|
||||
asPx,
|
||||
}
|
||||
|
||||
function get (obj, path, def) {
|
||||
@@ -142,6 +143,11 @@ function groupBy (xs, key) {
|
||||
}, {})
|
||||
}
|
||||
|
||||
function asPx (value) {
|
||||
value = Number(value)
|
||||
return Number.isNaN(value) ? null : value + 'px'
|
||||
}
|
||||
|
||||
function isArray (a) {
|
||||
return Array.isArray(a)
|
||||
}
|
||||
@@ -153,8 +159,8 @@ function isArray (a) {
|
||||
function makePathArray (obj) {
|
||||
return flattenDeep(obj)
|
||||
.join('.')
|
||||
.replace('[', '.')
|
||||
.replace(']', '')
|
||||
.replace(/\[/g, '.')
|
||||
.replace(/\]/g, '')
|
||||
.split('.')
|
||||
}
|
||||
|
||||
@@ -173,7 +179,7 @@ function splitProps ({ className, style, ...rest }) {
|
||||
return {
|
||||
className,
|
||||
style,
|
||||
rest,
|
||||
rest: rest || {},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user