mirror of
https://github.com/gosticks/react-table.git
synced 2025-10-16 11:55:36 +00:00
Examples Refactor + multiSort flag (#619)
* chore: Update the devDependencies for the linter * A few HOC examples for react-table. Not really integrated with the whole codesandbox.io approach. * Missing dependency - shortid * Refactor HOCs to /src/hoc Still have to write the HOCReadme.md (still just a placeholder for now) * Refactor complete May need to remove some redundant code * Text change for the HOC samples * Introduced a 'multiSort' flag Defaults to 'true' A 'false' value will turn multi-sort off. * refactor: Fix defaultProps.js linter errors * refactor: Fix lifecycle.js linter errors * refactor: Fix pagination.js linter errors * refactor: Fix propTypes.js linter errors * refactor: Fix utils.js linter errors * refactor: Fix methods.js linter errors * refactor: Fix index.js linter errors * Fix for linter changes + CHANGELOG update
This commit is contained in:
parent
1cf9e49952
commit
f74ba3cc16
55
CHANGELOG.md
55
CHANGELOG.md
@ -1,3 +1,58 @@
|
||||
## 6.7.4
|
||||
#### Fixes & Optimizations
|
||||
- Fix Prop types for columns
|
||||
|
||||
## 6.7.3
|
||||
#### Fixes & Optimizations
|
||||
- Fix the rest of the proptypes
|
||||
|
||||
## 6.7.2
|
||||
#### Fixes & Optimizations
|
||||
- `getPropTypes` proptype check
|
||||
|
||||
## 6.7.1
|
||||
#### Fixes & Optimizations
|
||||
- `eslint-config` moved to dev deps
|
||||
|
||||
## 6.7.0
|
||||
## 6.7.0-alpha-0
|
||||
#### New Features
|
||||
- Expose page/pageSize to rows/cells
|
||||
- Supply sort direction to custom sort methods
|
||||
|
||||
#### Fixes & Optimizations
|
||||
- README updates
|
||||
- Linter cleanup
|
||||
- Added PropTypes node module
|
||||
- Deps, linting and style upgrades
|
||||
|
||||
## 6.6.0
|
||||
#### Fixes & Optimizations
|
||||
- moved repo to react-tools
|
||||
- Doc examples moved to codesandbox.io
|
||||
- README updates
|
||||
- CSS refacting for rt-tfoot to match rt-thead
|
||||
- CSS more specific for input and select
|
||||
|
||||
## 6.5.3
|
||||
#### Fixes & Optimizations
|
||||
- `onClick` proxying and eslint
|
||||
|
||||
## 6.5.2
|
||||
#### New Features
|
||||
- Provide onClick handleOriginal function - #406
|
||||
|
||||
#### Fixes & Optimizations
|
||||
- README updates
|
||||
- `makePathArray` in utils - #326
|
||||
- Various fixes: #294, #376, #398, #415,
|
||||
|
||||
## 6.5.1
|
||||
#### Fixes & Optimizations
|
||||
- `defaultExpanded` now works correctly - #372
|
||||
- `column.getProps().rest` props are now applied correctly
|
||||
- `makeTemplateComponent` now supports `displayName` - #289
|
||||
|
||||
## 6.5.0
|
||||
##### New Features
|
||||
- `column.filterAll` - defaults to `false`, but when set to `true` will provide the entire array of rows to `filterMethod` as opposed to one row at a time. This allows for more fine-grained filtering using any method you can dream up. See the [Custom Filtering example](https://react-table.js.org/#/story/custom-filtering) for more info.
|
||||
|
||||
@ -171,6 +171,7 @@ These are all of the available props (and their default values) for the main `<R
|
||||
collapseOnDataChange: true,
|
||||
freezeWhenExpanded: false,
|
||||
sortable: true,
|
||||
multiSort: true,
|
||||
resizable: true,
|
||||
filterable: false,
|
||||
defaultSortDesc: false,
|
||||
@ -833,6 +834,9 @@ Sorting comes built in with React-Table.
|
||||
## 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!
|
||||
|
||||
You can set the `multiSort` prop to `false` to disable this feature (which may be useful for server-side sorting when you are not
|
||||
going to sort multiple columns).
|
||||
|
||||
## Custom Sorting Algorithm
|
||||
To override the default sorting algorithm for the whole table use the `defaultSortMethod` prop.
|
||||
|
||||
|
||||
@ -10,6 +10,15 @@
|
||||
"babel-preset-react": "6.11.1",
|
||||
"babel-preset-stage-2": "6.13.0",
|
||||
"eslint": "^4.1.1",
|
||||
"eslint-config-standard": "^10.2.1",
|
||||
"eslint-plugin-class-property": "^1.0.6",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.2",
|
||||
"eslint-plugin-node": "^5.2.1",
|
||||
"eslint-plugin-promise": "^3.6.0",
|
||||
"eslint-plugin-react": "^7.4.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"html-element-attributes": "^1.3.0",
|
||||
"match-sorter": "^1.8.0",
|
||||
"npm-run-all": "^3.1.1",
|
||||
"onchange": "^3.0.2",
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import React from 'react'
|
||||
//
|
||||
import ReactStory, { defaultProps } from 'react-story'
|
||||
@ -6,6 +8,18 @@ import './stories/utils/prism.css'
|
||||
import '../../react-table.css'
|
||||
|
||||
import Readme from './stories/Readme.js'
|
||||
import HOCReadme from './stories/HOCReadme.js'
|
||||
|
||||
// import Tester from './examples/expander';
|
||||
|
||||
// import { TreeTable, SelectTable, SelectTreeTable } from './examples/index'
|
||||
//
|
||||
// const exampleStories = [
|
||||
// // examples
|
||||
// { name: 'TreeTable', component: TreeTable },
|
||||
// { name: 'SelectTable', component: SelectTable },
|
||||
// { name: 'SelectTreeTable', component: SelectTreeTable },
|
||||
// ]
|
||||
|
||||
import { TreeTable, CheckboxTable } from './examples/index'
|
||||
|
||||
@ -17,7 +31,9 @@ const exampleStories = [
|
||||
|
||||
const stories = [
|
||||
{ name: 'Readme', component: Readme },
|
||||
{ name: 'HOC Readme', component: HOCReadme },
|
||||
|
||||
// { name: 'Tester', component: Tester },
|
||||
{ name: 'Simple Table', component: CodeSandbox('X6npLXPRW') },
|
||||
{
|
||||
name: 'Cell Renderers & Custom Components',
|
||||
@ -60,10 +76,9 @@ const stories = [
|
||||
name: 'Multiple Pagers (Top and Bottom)',
|
||||
component: CodeSandbox('VEZ8OgvX'),
|
||||
},
|
||||
|
||||
// other examples
|
||||
...exampleStories,
|
||||
|
||||
{ name: 'Tree Table (HOC)', component: CodeSandbox('lxmr4wynzq') },
|
||||
{ name: 'Select Table (HOC)', component: CodeSandbox('7yq5ylw09j') },
|
||||
{ name: 'Select Tree Table (HOC)', component: CodeSandbox('2p7jp4klwp') },
|
||||
]
|
||||
|
||||
export default class App extends React.Component {
|
||||
|
||||
54
docs/src/examples/expander/index.js
Normal file
54
docs/src/examples/expander/index.js
Normal file
@ -0,0 +1,54 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import ReactTable from '../../../../lib/index'
|
||||
import '../../../../react-table.css'
|
||||
|
||||
const data = [
|
||||
{one:"1.1",two:"1.2"},
|
||||
{one:"2.1",two:"2.2"},
|
||||
{one:"3.1",two:"3.2"},
|
||||
{one:"4.1",two:"4.2"},
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{accessor:'one', Header: 'One'},
|
||||
{accessor:'two', Header: 'Two'},
|
||||
]
|
||||
|
||||
class ExpanderComponent extends React.Component {
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<div className={`rt-expander ${this.props.isExpanded ? '-open' : ''}`}>
|
||||
•
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SubComponent extends React.Component {
|
||||
render()
|
||||
{
|
||||
return <div>Nothing</div>
|
||||
}
|
||||
}
|
||||
|
||||
export default class ComponentTest extends React.Component {
|
||||
render()
|
||||
{
|
||||
const rtProps = {
|
||||
data,
|
||||
columns,
|
||||
ExpanderComponent: (props)=><ExpanderComponent {...props} />,
|
||||
SubComponent: (props)=><SubComponent {...props} />,
|
||||
multiSort: false,
|
||||
}
|
||||
return (
|
||||
<ReactTable
|
||||
{...rtProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,11 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import TreeTable from './treetable';
|
||||
import CheckboxTable from './checkbox';
|
||||
import TreeTable from './treetable'
|
||||
import SelectTable from './selecttable'
|
||||
import SelectTreeTable from './selecttreetable'
|
||||
|
||||
export {
|
||||
TreeTable,
|
||||
CheckboxTable,
|
||||
SelectTable,
|
||||
SelectTreeTable,
|
||||
}
|
||||
|
||||
174
docs/src/examples/selecttable/index.js
Normal file
174
docs/src/examples/selecttable/index.js
Normal file
@ -0,0 +1,174 @@
|
||||
|
||||
import React from 'react';
|
||||
import shortid from 'shortid';
|
||||
|
||||
import ReactTable from '../../../../lib/index'
|
||||
import '../../../../react-table.css'
|
||||
|
||||
import selectTableHOC from '../../../../lib/hoc/selectTable'
|
||||
|
||||
const SelectTable = selectTableHOC(ReactTable);
|
||||
|
||||
async function getData()
|
||||
{
|
||||
const result = await ( await fetch('/au_500_tree.json') ).json();
|
||||
// we are adding a unique ID to the data for tracking the selected records
|
||||
return result.map((item)=>{
|
||||
const _id = shortid.generate();
|
||||
return {
|
||||
_id,
|
||||
...item,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getColumns(data)
|
||||
{
|
||||
const columns = [];
|
||||
const sample = data[0];
|
||||
for(let key in sample)
|
||||
{
|
||||
if(key==='_id') continue;
|
||||
columns.push({
|
||||
accessor: key,
|
||||
Header: key,
|
||||
})
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
export class ComponentTest extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state =
|
||||
{
|
||||
data: null,
|
||||
columns: null,
|
||||
selection: [],
|
||||
selectAll: false,
|
||||
selectType: 'checkbox',
|
||||
};
|
||||
}
|
||||
componentDidMount()
|
||||
{
|
||||
getData().then((data)=>{
|
||||
const columns = getColumns(data);
|
||||
this.setState({ data, columns });
|
||||
});
|
||||
}
|
||||
toggleSelection = (key,shift,row) => {
|
||||
/*
|
||||
Implementation of how to manage the selection state is up to the developer.
|
||||
This implementation uses an array stored in the component state.
|
||||
Other implementations could use object keys, a Javascript Set, or Redux... etc.
|
||||
*/
|
||||
// start off with the existing state
|
||||
if (this.state.selectType === 'radio') {
|
||||
let selection = [];
|
||||
if (selection.indexOf(key)<0) selection.push(key);
|
||||
this.setState({selection});
|
||||
} else {
|
||||
let selection = [
|
||||
...this.state.selection
|
||||
];
|
||||
const keyIndex = selection.indexOf(key);
|
||||
// check to see if the key exists
|
||||
if(keyIndex>=0) {
|
||||
// it does exist so we will remove it using destructing
|
||||
selection = [
|
||||
...selection.slice(0,keyIndex),
|
||||
...selection.slice(keyIndex+1)
|
||||
]
|
||||
} else {
|
||||
// it does not exist so add it
|
||||
selection.push(key);
|
||||
}
|
||||
// update the state
|
||||
this.setState({selection});
|
||||
}
|
||||
}
|
||||
toggleAll = () => {
|
||||
/*
|
||||
'toggleAll' is a tricky concept with any filterable table
|
||||
do you just select ALL the records that are in your data?
|
||||
OR
|
||||
do you only select ALL the records that are in the current filtered data?
|
||||
|
||||
The latter makes more sense because 'selection' is a visual thing for the user.
|
||||
This is especially true if you are going to implement a set of external functions
|
||||
that act on the selected information (you would not want to DELETE the wrong thing!).
|
||||
|
||||
So, to that end, access to the internals of ReactTable are required to get what is
|
||||
currently visible in the table (either on the current page or any other page).
|
||||
|
||||
The HOC provides a method call 'getWrappedInstance' to get a ref to the wrapped
|
||||
ReactTable and then get the internal state and the 'sortedData'.
|
||||
That can then be iterrated to get all the currently visible records and set
|
||||
the selection state.
|
||||
*/
|
||||
const selectAll = this.state.selectAll?false:true;
|
||||
const selection = [];
|
||||
if(selectAll)
|
||||
{
|
||||
// we need to get at the internals of ReactTable
|
||||
const wrappedInstance = this.selectTable.getWrappedInstance();
|
||||
// the 'sortedData' property contains the currently accessible records based on the filter and sort
|
||||
const currentRecords = wrappedInstance.getResolvedState().sortedData;
|
||||
// we just push all the IDs onto the selection array
|
||||
currentRecords.forEach((item)=>{
|
||||
if(item._original)
|
||||
{
|
||||
selection.push(item._original._id);
|
||||
}
|
||||
})
|
||||
}
|
||||
this.setState({selectAll,selection})
|
||||
}
|
||||
isSelected = (key) => {
|
||||
/*
|
||||
Instead of passing our external selection state we provide an 'isSelected'
|
||||
callback and detect the selection state ourselves. This allows any implementation
|
||||
for selection (either an array, object keys, or even a Javascript Set object).
|
||||
*/
|
||||
return this.state.selection.includes(key);
|
||||
}
|
||||
logSelection = () => {
|
||||
console.log('selection:',this.state.selection);
|
||||
}
|
||||
toggleType = () => {
|
||||
this.setState({ selectType: this.state.selectType === 'radio' ? 'checkbox' : 'radio', selection: [], selectAll: false, });
|
||||
}
|
||||
render(){
|
||||
const { toggleSelection, toggleAll, isSelected, logSelection, toggleType } = this;
|
||||
const { data, columns, selectAll, selectType } = this.state;
|
||||
const extraProps =
|
||||
{
|
||||
selectAll,
|
||||
isSelected,
|
||||
toggleAll,
|
||||
toggleSelection,
|
||||
selectType,
|
||||
}
|
||||
return (
|
||||
<div style={{ padding: '10px'}}>
|
||||
<h1>react-table - Select Table</h1>
|
||||
<button onClick={toggleType}>Select Type: <strong>{selectType}</strong></button>
|
||||
<button onClick={logSelection}>Log Selection to Console</button>
|
||||
{` (${this.state.selection.length}) selected`}
|
||||
{
|
||||
data?
|
||||
<SelectTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
ref={(r)=>this.selectTable = r}
|
||||
className="-striped -highlight"
|
||||
{...extraProps}
|
||||
/>
|
||||
:null
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ComponentTest;
|
||||
216
docs/src/examples/selecttreetable/index.js
Normal file
216
docs/src/examples/selecttreetable/index.js
Normal file
@ -0,0 +1,216 @@
|
||||
|
||||
import React from 'react';
|
||||
import shortid from 'shortid';
|
||||
|
||||
import ReactTable from '../../../../lib/index'
|
||||
import '../../../../react-table.css'
|
||||
|
||||
import selectTableHOC from '../../../../lib/hoc/selectTable'
|
||||
import treeTableHOC from '../../../../lib/hoc/treeTable'
|
||||
|
||||
const SelectTreeTable = selectTableHOC(treeTableHOC(ReactTable));
|
||||
|
||||
async function getData()
|
||||
{
|
||||
const result = await ( await fetch('/au_500_tree.json') ).json();
|
||||
// we are adding a unique ID to the data for tracking the selected records
|
||||
return result.map((item)=>{
|
||||
const _id = shortid.generate();
|
||||
return {
|
||||
_id,
|
||||
...item,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getColumns(data)
|
||||
{
|
||||
const columns = [];
|
||||
const sample = data[0];
|
||||
for(let key in sample)
|
||||
{
|
||||
if(key==='_id') continue;
|
||||
columns.push({
|
||||
accessor: key,
|
||||
Header: key,
|
||||
})
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
function getNodes(data,node=[])
|
||||
{
|
||||
data.forEach((item)=>{
|
||||
if(item.hasOwnProperty('_subRows') && item._subRows)
|
||||
{
|
||||
node = getNodes(item._subRows,node);
|
||||
} else {
|
||||
node.push(item._original);
|
||||
}
|
||||
});
|
||||
return node;
|
||||
}
|
||||
|
||||
export class ComponentTest extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state =
|
||||
{
|
||||
data: null,
|
||||
columns: null,
|
||||
selection: [],
|
||||
selectAll: false,
|
||||
selectType: 'checkbox',
|
||||
};
|
||||
}
|
||||
componentDidMount()
|
||||
{
|
||||
getData().then((data)=>{
|
||||
const columns = getColumns(data);
|
||||
const pivotBy = ['state','post'];
|
||||
this.setState({ data, columns, pivotBy });
|
||||
});
|
||||
}
|
||||
toggleSelection = (key,shift,row) => {
|
||||
/*
|
||||
Implementation of how to manage the selection state is up to the developer.
|
||||
This implementation uses an array stored in the component state.
|
||||
Other implementations could use object keys, a Javascript Set, or Redux... etc.
|
||||
*/
|
||||
// start off with the existing state
|
||||
if (this.state.selectType === 'radio') {
|
||||
let selection = [];
|
||||
if (selection.indexOf(key)<0) selection.push(key);
|
||||
this.setState({selection});
|
||||
} else {
|
||||
let selection = [
|
||||
...this.state.selection
|
||||
];
|
||||
const keyIndex = selection.indexOf(key);
|
||||
// check to see if the key exists
|
||||
if(keyIndex>=0) {
|
||||
// it does exist so we will remove it using destructing
|
||||
selection = [
|
||||
...selection.slice(0,keyIndex),
|
||||
...selection.slice(keyIndex+1)
|
||||
]
|
||||
} else {
|
||||
// it does not exist so add it
|
||||
selection.push(key);
|
||||
}
|
||||
// update the state
|
||||
this.setState({selection});
|
||||
}
|
||||
}
|
||||
toggleAll = () => {
|
||||
/*
|
||||
'toggleAll' is a tricky concept with any filterable table
|
||||
do you just select ALL the records that are in your data?
|
||||
OR
|
||||
do you only select ALL the records that are in the current filtered data?
|
||||
|
||||
The latter makes more sense because 'selection' is a visual thing for the user.
|
||||
This is especially true if you are going to implement a set of external functions
|
||||
that act on the selected information (you would not want to DELETE the wrong thing!).
|
||||
|
||||
So, to that end, access to the internals of ReactTable are required to get what is
|
||||
currently visible in the table (either on the current page or any other page).
|
||||
|
||||
The HOC provides a method call 'getWrappedInstance' to get a ref to the wrapped
|
||||
ReactTable and then get the internal state and the 'sortedData'.
|
||||
That can then be iterrated to get all the currently visible records and set
|
||||
the selection state.
|
||||
*/
|
||||
const selectAll = this.state.selectAll?false:true;
|
||||
const selection = [];
|
||||
if(selectAll)
|
||||
{
|
||||
// we need to get at the internals of ReactTable
|
||||
const wrappedInstance = this.selectTable.getWrappedInstance();
|
||||
// the 'sortedData' property contains the currently accessible records based on the filter and sort
|
||||
const currentRecords = wrappedInstance.getResolvedState().sortedData;
|
||||
// we need to get all the 'real' (original) records out to get at their IDs
|
||||
const nodes = getNodes(currentRecords);
|
||||
// we just push all the IDs onto the selection array
|
||||
nodes.forEach((item)=>{
|
||||
selection.push(item._id);
|
||||
})
|
||||
}
|
||||
this.setState({selectAll,selection})
|
||||
}
|
||||
isSelected = (key) => {
|
||||
/*
|
||||
Instead of passing our external selection state we provide an 'isSelected'
|
||||
callback and detect the selection state ourselves. This allows any implementation
|
||||
for selection (either an array, object keys, or even a Javascript Set object).
|
||||
*/
|
||||
return this.state.selection.includes(key);
|
||||
}
|
||||
logSelection = () => {
|
||||
console.log('selection:',this.state.selection);
|
||||
}
|
||||
toggleType = () => {
|
||||
this.setState({ selectType: this.state.selectType === 'radio' ? 'checkbox' : 'radio', selection: [], selectAll: false, });
|
||||
}
|
||||
toggleTree = () => {
|
||||
if(this.state.pivotBy.length) {
|
||||
this.setState({pivotBy:[],expanded:{}});
|
||||
} else {
|
||||
this.setState({pivotBy:['state','post'],expanded:{}});
|
||||
}
|
||||
}
|
||||
onExpandedChange = (expanded) => {
|
||||
this.setState({expanded});
|
||||
}
|
||||
render(){
|
||||
const { toggleSelection, toggleAll, isSelected, logSelection, toggleType, toggleTree, onExpandedChange, } = this;
|
||||
const { data, columns, selectAll, selectType, pivotBy, expanded, } = this.state;
|
||||
const extraProps =
|
||||
{
|
||||
selectAll,
|
||||
isSelected,
|
||||
toggleAll,
|
||||
toggleSelection,
|
||||
selectType,
|
||||
pivotBy,
|
||||
expanded,
|
||||
onExpandedChange,
|
||||
pageSize: 5,
|
||||
}
|
||||
return (
|
||||
<div style={{ padding: '10px'}}>
|
||||
<h1>react-table - Select Tree Table</h1>
|
||||
<p>This example combines two HOCs (the TreeTable and the SelectTable) to make a composite component.</p>
|
||||
<p>We'll call it SelectTreeTable!</p>
|
||||
<p>Here is what the buttons do:</p>
|
||||
<ul>
|
||||
<li><strong>Toggle Tree:</strong> enables or disabled the pivotBy on the table.</li>
|
||||
<li><strong>Select Type:</strong> changes from 'checkbox' to 'radio' and back again.</li>
|
||||
<li><strong>Log Selection to Console:</strong> open your console to see what has been selected.</li>
|
||||
</ul>
|
||||
<p>
|
||||
<strong>NOTE:</strong> the selection is maintained when toggling the tree on and off but is cleared
|
||||
when switching between select types (radio, checkbox).
|
||||
</p>
|
||||
<button onClick={toggleTree}>Toggle Tree [{pivotBy && pivotBy.length ? pivotBy.join(', ') : ''}]</button>
|
||||
<button onClick={toggleType}>Select Type: <strong>{selectType}</strong></button>
|
||||
<button onClick={logSelection}>Log Selection to Console</button>
|
||||
{` (${this.state.selection.length}) selected`}
|
||||
{
|
||||
data?
|
||||
<SelectTreeTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
ref={(r)=>this.selectTable = r}
|
||||
className="-striped -highlight"
|
||||
freezeWhenExpanded={true}
|
||||
{...extraProps}
|
||||
/>
|
||||
:null
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ComponentTest;
|
||||
@ -4,7 +4,7 @@ import React from 'react';
|
||||
import ReactTable from '../../../../lib/index'
|
||||
import '../../../../react-table.css'
|
||||
|
||||
import treeTableHOC from './treeTableHOC';
|
||||
import treeTableHOC from '../../../../lib/hoc/treeTable'
|
||||
|
||||
async function getData()
|
||||
{
|
||||
|
||||
23
docs/src/stories/HOCReadme.js
Normal file
23
docs/src/stories/HOCReadme.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
import React from 'react'
|
||||
import marked from 'marked'
|
||||
//
|
||||
import HOCReadme from '!raw!../../../src/hoc/README.md'
|
||||
import 'github-markdown-css/github-markdown.css'
|
||||
import './utils/prism.js'
|
||||
|
||||
export default class HOCStory extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<div style={{ padding: '10px' }}>
|
||||
<span
|
||||
className='markdown-body'
|
||||
dangerouslySetInnerHTML={{ __html: marked(HOCReadme) }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
componentDidMount () {
|
||||
global.Prism && global.Prism.highlightAll()
|
||||
}
|
||||
}
|
||||
@ -2417,7 +2417,7 @@ eslint-config-standard-jsx@4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-4.0.2.tgz#009e53c4ddb1e9ee70b4650ffe63a7f39f8836e1"
|
||||
|
||||
eslint-config-standard@10.2.1:
|
||||
eslint-config-standard@10.2.1, eslint-config-standard@^10.2.1:
|
||||
version "10.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz#c061e4d066f379dc17cd562c64e819b4dd454591"
|
||||
|
||||
@ -2492,7 +2492,7 @@ eslint-plugin-import@2.0.1:
|
||||
minimatch "^3.0.3"
|
||||
pkg-up "^1.0.0"
|
||||
|
||||
eslint-plugin-import@^2.7.0:
|
||||
eslint-plugin-import@^2.7.0, eslint-plugin-import@^2.8.0:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz#fa1b6ef31fcb3c501c09859c1b86f1fc5b986894"
|
||||
dependencies:
|
||||
@ -2545,6 +2545,15 @@ eslint-plugin-jsx-a11y@^6.0.2:
|
||||
emoji-regex "^6.1.0"
|
||||
jsx-ast-utils "^1.4.0"
|
||||
|
||||
eslint-plugin-node@^5.2.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-5.2.1.tgz#80df3253c4d7901045ec87fa660a284e32bdca29"
|
||||
dependencies:
|
||||
ignore "^3.3.6"
|
||||
minimatch "^3.0.4"
|
||||
resolve "^1.3.3"
|
||||
semver "5.3.0"
|
||||
|
||||
eslint-plugin-node@~4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-4.2.2.tgz#82959ca9aed79fcbd28bb1b188d05cac04fb3363"
|
||||
@ -2555,6 +2564,10 @@ eslint-plugin-node@~4.2.2:
|
||||
resolve "^1.1.7"
|
||||
semver "5.3.0"
|
||||
|
||||
eslint-plugin-promise@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.6.0.tgz#54b7658c8f454813dc2a870aff8152ec4969ba75"
|
||||
|
||||
eslint-plugin-promise@~3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz#78fbb6ffe047201627569e85a6c5373af2a68fca"
|
||||
@ -2585,7 +2598,7 @@ eslint-plugin-react@~6.10.0:
|
||||
jsx-ast-utils "^1.3.4"
|
||||
object.assign "^4.0.4"
|
||||
|
||||
eslint-plugin-standard@~3.0.1:
|
||||
eslint-plugin-standard@^3.0.1, eslint-plugin-standard@~3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz#34d0c915b45edc6f010393c7eef3823b08565cf2"
|
||||
|
||||
@ -3479,6 +3492,10 @@ html-element-attributes@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/html-element-attributes/-/html-element-attributes-1.2.0.tgz#8b1c7aaf94353fd9b455c27ec7ebaf1583e29fd0"
|
||||
|
||||
html-element-attributes@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/html-element-attributes/-/html-element-attributes-1.3.0.tgz#f06ebdfce22de979db82020265cac541fb17d4fc"
|
||||
|
||||
html-encoding-sniffer@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
|
||||
@ -3612,6 +3629,10 @@ ignore@^3.3.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
|
||||
|
||||
ignore@^3.3.6:
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
|
||||
|
||||
imurmurhash@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
@ -6113,6 +6134,12 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.2.0:
|
||||
dependencies:
|
||||
path-parse "^1.0.5"
|
||||
|
||||
resolve@^1.3.3:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
|
||||
dependencies:
|
||||
path-parse "^1.0.5"
|
||||
|
||||
restore-cursor@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
|
||||
@ -6322,6 +6349,10 @@ shellwords@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
|
||||
shortid@^2.2.8:
|
||||
version "2.2.8"
|
||||
resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.8.tgz#033b117d6a2e975804f6f0969dbe7d3d0b355131"
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
|
||||
@ -22,6 +22,7 @@ export default {
|
||||
collapseOnDataChange: true,
|
||||
freezeWhenExpanded: false,
|
||||
sortable: true,
|
||||
multiSort: true,
|
||||
resizable: true,
|
||||
filterable: false,
|
||||
defaultSortDesc: false,
|
||||
@ -29,12 +30,14 @@ export default {
|
||||
defaultFiltered: [],
|
||||
defaultResized: [],
|
||||
defaultExpanded: {},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
defaultFilterMethod: (filter, row, column) => {
|
||||
const id = filter.pivotId || filter.id
|
||||
return row[id] !== undefined
|
||||
? String(row[id]).startsWith(filter.value)
|
||||
: true
|
||||
},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
defaultSortMethod: (a, b, desc) => {
|
||||
// force null and undefined to the bottom
|
||||
a = a === null || a === undefined ? '' : a
|
||||
@ -49,7 +52,8 @@ export default {
|
||||
if (a < b) {
|
||||
return -1
|
||||
}
|
||||
// returning 0, undefined or any falsey value will use subsequent sorts or the index as a tiebreaker
|
||||
// returning 0, undefined or any falsey value will use subsequent sorts or
|
||||
// the index as a tiebreaker
|
||||
return 0
|
||||
},
|
||||
|
||||
@ -176,24 +180,24 @@ export default {
|
||||
TbodyComponent: _.makeTemplateComponent('rt-tbody', 'Tbody'),
|
||||
TrGroupComponent: _.makeTemplateComponent('rt-tr-group', 'TrGroup'),
|
||||
TrComponent: _.makeTemplateComponent('rt-tr', 'Tr'),
|
||||
ThComponent: ({ toggleSort, className, children, ...rest }) => {
|
||||
return (
|
||||
<div
|
||||
className={classnames(className, 'rt-th')}
|
||||
onClick={e => {
|
||||
toggleSort && toggleSort(e)
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
ThComponent: ({ toggleSort, className, children, ...rest }) => (
|
||||
<div
|
||||
className={classnames(className, 'rt-th')}
|
||||
onClick={e => (
|
||||
toggleSort && toggleSort(e)
|
||||
)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
TdComponent: _.makeTemplateComponent('rt-td', 'Td'),
|
||||
TfootComponent: _.makeTemplateComponent('rt-tfoot', 'Tfoot'),
|
||||
FilterComponent: ({ filter, onChange }) => (
|
||||
<input
|
||||
type='text'
|
||||
type="text"
|
||||
style={{
|
||||
width: '100%',
|
||||
}}
|
||||
@ -215,6 +219,7 @@ export default {
|
||||
const previewValues = subRows
|
||||
.filter(d => typeof d[column.id] !== 'undefined')
|
||||
.map((row, i) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<span key={i}>
|
||||
{row[column.id]}
|
||||
{i < subRows.length - 1 ? ', ' : ''}
|
||||
@ -236,7 +241,7 @@ export default {
|
||||
className={classnames('-loading', { '-active': loading }, className)}
|
||||
{...rest}
|
||||
>
|
||||
<div className='-loading-inner'>
|
||||
<div className="-loading-inner">
|
||||
{loadingText}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
122
src/hoc/README.md
Normal file
122
src/hoc/README.md
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
<div style="text-align:center;">
|
||||
<a href="https://github.com/react-tools/react-table" target="\_parent"><img src="https://github.com/react-tools/media/raw/master/logo-react-table.png" alt="React Table Logo" style="width:450px;"/></a>
|
||||
</div>
|
||||
|
||||
# ReactTable - expanding with HOCs
|
||||
This documentation is about expanding ReactTable using Higher Order Components/Functions.
|
||||
|
||||
## Covered in this README
|
||||
- A Brief explanation of HOCs and why they are a good approach for ReactTable enhancements
|
||||
- Documentation of the currently available HOCs
|
||||
- TreeTable
|
||||
- SelectTable
|
||||
- Documentation of the standard for writing HOCs with ReactTable
|
||||
|
||||
## What are HOCs and why use them with ReactTable
|
||||
HOCs (or Higher Order Components/Functions) are either a React Component (or a function that returns a React Component)
|
||||
that are used to enhance the functionality of an existing component. How much you can enhance depends on the props that
|
||||
the component exposes.
|
||||
|
||||
Fortunately, ReactTable exposes a LOT of functionality as props to the component. In some cases there are too many
|
||||
props to keep track of and that is where HOCs come in.
|
||||
|
||||
You can write a HOC that just focusses on the additional functionality you want to enhance and keep those enhancements to
|
||||
reuse over and over again when you need them. You don't have to edit the ReactSource code, just wrap ReactTable in one or
|
||||
more HOCs (more on some issues related to chaining HOCs later) that provide the additional functionality you want to expose.
|
||||
|
||||
The most obvious HOC is one that can add `checkbox` or select functionality. The HOC included provides `select` functionality
|
||||
that allows the developer to specify if they want a `checkbox` or `radio` style of select column. The implementation of the
|
||||
selection is recorded (e.g. in component state, Redux, etc.) and how to manage multiple selections. The HOC really only handles
|
||||
the rendering pieces.
|
||||
|
||||
But there is more documentation on the `select` HOC below.
|
||||
|
||||
|
||||
## Currently Available HOCs
|
||||
|
||||
### TreeTable
|
||||
TreeTable takes over the rendering of the generated pivot rows of ReactTable so that they appear more like an expandable Tree.
|
||||
|
||||
It accomplishes this by rendering a 100% wide div and then only rendering the cell that controls the pivot at that level.
|
||||
|
||||
Using it is as simple as doing the following:
|
||||
```javascript
|
||||
import ReactTable from 'react-table'
|
||||
import treeTableHOC from 'react-table/lib/hoc/treeTable'
|
||||
|
||||
const TreeTable = treeTableHOC(ReactTable)
|
||||
```
|
||||
After you have done the above, you can then use `TreeTable` just as you would `ReactTable` but it will render pivots using
|
||||
the Tree style described above.
|
||||
|
||||
|
||||
### SelectTable
|
||||
SelectTable is a little trickier. The HOCs attempt to avoid adding additional state and, as there is no internal ID for a row that
|
||||
can be relied on to be static (ReactTable just reuses indexes when rendering) the developer has to maintain the state outside of even
|
||||
the wrapped component. So it is largely based on callbacks.
|
||||
|
||||
You include the HOC in the same manner as you would for the treeTableHOC but then need to provide the following overrides:
|
||||
- isSelected - returns `true` if the key passed is selected otherwise it should return `false`
|
||||
- selectAll - a property that indicates if the selectAll is set (`true|false`)
|
||||
- toggleAll - called when the user clicks the `selectAll` checkbox/radio
|
||||
- toggleSelection - called when the use clicks a specific checkbox/radio in a row
|
||||
- selectType - either `checkbox|radio` to indicate what type of selection is required
|
||||
|
||||
In the case of `radio` there is no `selectAll` displayed but the developer is responsible for only making one selection in
|
||||
the controlling component's state. You could select multiple but it wouldn't make sense and you should use `checkbox` instead.
|
||||
|
||||
You also have to decide what `selectAll` means. Given ReactTable is a paged solution there are other records off-page. When someone
|
||||
selects the `selectAll` checkbox, should it mark every possible record, only what might be visible to due a Filter or only those items
|
||||
on the current page?
|
||||
|
||||
The example opts for the middle approach so it gets a `ref` to the ReactTable instance and pulls the `sortedData` out of the resolved
|
||||
state (then walks through those records and pulls their ID into the `selection` state of the controlling component).
|
||||
|
||||
You can also replace the input component that is used to render the select box and select all box:
|
||||
- SelectAllInputComponent - the checkbox in the top left corner
|
||||
- SelectInputComponent - the checkbox used on a row
|
||||
|
||||
### SelectTreeTable
|
||||
SelectTreeTable is a combination of TreeTable and SelectTable.
|
||||
|
||||
To function correctly the chain has to be in the correct order as follows (see the comments in the guid on HOCs below).
|
||||
|
||||
```Javascript
|
||||
const SelectTreeTable = selectTableHOC(treeTableHOC(ReactTable));
|
||||
```
|
||||
|
||||
In this particular instance it is (probably) because the functions need access to the state on the wrapped component to manage
|
||||
the selected items. Although that is not totally clearly the issue.
|
||||
|
||||
## HOC Guide for ReactTable
|
||||
There are a few rules required when writing a HOC for ReactTable (other than meeting the normal lint standards - which are
|
||||
still being developed).
|
||||
|
||||
Firstly, there are issues with `ref` when you write a HOC. Consider a deeply nested component wrapped in multiple HOCs...
|
||||
|
||||
A HOC in the middle of the chain requires access to the instance of the component it thinks it is wrapping but there is at
|
||||
least one other wrapper in the way. The challenge is: How do I get to the actual wrapped component?
|
||||
|
||||
Each HOC is required to be a React Class so that a `ref` can be obtained against each component:
|
||||
|
||||
```Javascript
|
||||
<Component ... ref={r => this.wrappedInstance = r} />
|
||||
```
|
||||
*NOTE:* "Component" can also be the `<ReactTable />` instance.
|
||||
|
||||
Then the following method needs
|
||||
to be placed on the class so that it exposes the correct instance of ReactTable:
|
||||
|
||||
```Javascript
|
||||
getWrappedInstance() {
|
||||
if (!this.wrappedInstance) console.warn('<component name here> - No wrapped instance')
|
||||
if (this.wrappedInstance.getWrappedInstance) return this.wrappedInstance.getWrappedInstance()
|
||||
else return this.wrappedInstance
|
||||
}
|
||||
```
|
||||
Essentially this will walk down the chain (if there are chained HOCs) and stop when it gets to the end and return the wrapped instance.
|
||||
|
||||
Finally, sometimes the chains need to be in a specific order to function correctly. It is not clear if this is just an architectural
|
||||
issue or if it would be better solved using a library like `recompose`. Anyone who is able to contribute a reliable solution to this
|
||||
is welcome to submit a PR.
|
||||
111
src/hoc/selectTable/index.js
Normal file
111
src/hoc/selectTable/index.js
Normal file
@ -0,0 +1,111 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const defaultSelectInputComponent = (props) => {
|
||||
return (
|
||||
<input
|
||||
type={props.selectType || 'checkbox'}
|
||||
checked={props.checked}
|
||||
onClick={(e)=>{
|
||||
const { shiftKey } = e;
|
||||
e.stopPropagation();
|
||||
props.onClick(props.id, shiftKey, props.row);
|
||||
}}
|
||||
onChange={()=>{}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default (Component) => {
|
||||
|
||||
const wrapper = class RTSelectTable extends React.Component {
|
||||
|
||||
constructor(props)
|
||||
{
|
||||
super(props);
|
||||
}
|
||||
|
||||
rowSelector(row) {
|
||||
if(!row || !row.hasOwnProperty(this.props.keyField)) return null;
|
||||
const { toggleSelection, selectType, keyField } = this.props;
|
||||
const checked = this.props.isSelected(row[this.props.keyField]);
|
||||
const inputProps =
|
||||
{
|
||||
checked,
|
||||
onClick: toggleSelection,
|
||||
selectType,
|
||||
id: row[keyField],
|
||||
row,
|
||||
}
|
||||
return React.createElement(this.props.SelectInputComponent,inputProps);
|
||||
}
|
||||
|
||||
headSelector(row) {
|
||||
const { selectType } = this.props;
|
||||
if (selectType === 'radio') return null;
|
||||
|
||||
const { toggleAll, selectAll: checked, SelectAllInputComponent, } = this.props;
|
||||
const inputProps =
|
||||
{
|
||||
checked,
|
||||
onClick: toggleAll,
|
||||
selectType,
|
||||
}
|
||||
|
||||
return React.createElement(SelectAllInputComponent,inputProps);
|
||||
}
|
||||
|
||||
// this is so we can expose the underlying ReactTable to get at the sortedData for selectAll
|
||||
getWrappedInstance() {
|
||||
if (!this.wrappedInstance) console.warn('RTSelectTable - No wrapped instance');
|
||||
if (this.wrappedInstance.getWrappedInstance) return this.wrappedInstance.getWrappedInstance();
|
||||
else return this.wrappedInstance
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
const {
|
||||
columns:originalCols, isSelected, toggleSelection, toggleAll, keyField, selectAll,
|
||||
selectType, SelectAllInputComponent, SelectInputComponent,
|
||||
...rest
|
||||
} = this.props;
|
||||
const select = {
|
||||
id: '_selector',
|
||||
accessor: ()=>'x', // this value is not important
|
||||
Header: this.headSelector.bind(this),
|
||||
Cell: (ci) => { return this.rowSelector.bind(this)(ci.original); },
|
||||
width: 30,
|
||||
filterable: false,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
style: { textAlign: 'center' },
|
||||
}
|
||||
const columns = [
|
||||
select,
|
||||
...originalCols,
|
||||
];
|
||||
const extra = {
|
||||
columns,
|
||||
};
|
||||
return (
|
||||
<Component {...rest} {...extra} ref={(r)=>this.wrappedInstance=r}/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
wrapper.displayName = 'RTSelectTable';
|
||||
wrapper.defaultProps =
|
||||
{
|
||||
keyField: '_id',
|
||||
isSelected: (key)=>{ console.log('No isSelected handler provided:',{key})},
|
||||
selectAll: false,
|
||||
toggleSelection: (key, shift, row)=>{ console.log('No toggleSelection handler provided:', { key, shift, row }) },
|
||||
toggleAll: () => { console.log('No toggleAll handler provided.') },
|
||||
selectType: 'check',
|
||||
SelectInputComponent: defaultSelectInputComponent,
|
||||
SelectAllInputComponent: defaultSelectInputComponent,
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
80
src/hoc/treeTable/index.js
Normal file
80
src/hoc/treeTable/index.js
Normal file
@ -0,0 +1,80 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export default (Component) => {
|
||||
const wrapper = class RTTreeTable extends React.Component {
|
||||
|
||||
constructor(props)
|
||||
{
|
||||
super(props);
|
||||
this.getWrappedInstance.bind(this);
|
||||
this.TrComponent.bind(this);
|
||||
this.getTrProps.bind(this);
|
||||
}
|
||||
|
||||
// this is so we can expose the underlying ReactTable to get at the sortedData for selectAll
|
||||
getWrappedInstance = () => {
|
||||
if (!this.wrappedInstance) console.warn('RTTreeTable - No wrapped instance');
|
||||
if (this.wrappedInstance.getWrappedInstance) return this.wrappedInstance.getWrappedInstance();
|
||||
else return this.wrappedInstance
|
||||
}
|
||||
|
||||
TrComponent = (props) => {
|
||||
const {
|
||||
ri,
|
||||
...rest
|
||||
} = props;
|
||||
if(ri && ri.groupedByPivot) {
|
||||
const cell = {...props.children[ri.level]};
|
||||
|
||||
cell.props.style.flex = 'unset';
|
||||
cell.props.style.width = '100%';
|
||||
cell.props.style.maxWidth = 'unset';
|
||||
cell.props.style.paddingLeft = `${this.props.treeTableIndent*ri.level}px`;
|
||||
// cell.props.style.backgroundColor = '#DDD';
|
||||
cell.props.style.borderBottom = '1px solid rgba(128,128,128,0.2)';
|
||||
|
||||
return <div className={`rt-tr ${rest.className}`} style={rest.style}>{cell}</div>;
|
||||
}
|
||||
return <Component.defaultProps.TrComponent {...rest} />;
|
||||
}
|
||||
|
||||
getTrProps = (state,ri,ci,instance) => {
|
||||
return {ri};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { columns, treeTableIndent, ...rest } = this.props;
|
||||
const { TrComponent, getTrProps } = this;
|
||||
const extra = {
|
||||
columns: columns.map((col)=>{
|
||||
let column = col;
|
||||
if(rest.pivotBy && rest.pivotBy.includes(col.accessor))
|
||||
{
|
||||
column = {
|
||||
accessor: col.accessor,
|
||||
width: `${treeTableIndent}px`,
|
||||
show: false,
|
||||
Header: '',
|
||||
}
|
||||
}
|
||||
return column;
|
||||
}),
|
||||
TrComponent,
|
||||
getTrProps,
|
||||
};
|
||||
|
||||
return (
|
||||
<Component {...rest} {...extra} ref={ r => this.wrappedInstance=r }/>
|
||||
)
|
||||
}
|
||||
}
|
||||
wrapper.displayName = 'RTTreeTable';
|
||||
wrapper.defaultProps =
|
||||
{
|
||||
treeTableIndent: 10,
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
467
src/index.js
467
src/index.js
@ -80,6 +80,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
loadingText,
|
||||
noDataText,
|
||||
sortable,
|
||||
multiSort,
|
||||
resizable,
|
||||
filterable,
|
||||
// Pivoting State
|
||||
@ -141,27 +142,25 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
const hasColumnFooter = allVisibleColumns.some(d => d.Footer)
|
||||
const hasFilters = filterable || allVisibleColumns.some(d => d.filterable)
|
||||
|
||||
const recurseRowsViewIndex = (rows, path = [], index = -1) => {
|
||||
return [
|
||||
rows.map((row, i) => {
|
||||
index++
|
||||
const rowWithViewIndex = {
|
||||
...row,
|
||||
_viewIndex: index,
|
||||
}
|
||||
const newPath = path.concat([i])
|
||||
if (rowWithViewIndex[subRowsKey] && _.get(expanded, newPath)) {
|
||||
;[rowWithViewIndex[subRowsKey], index] = recurseRowsViewIndex(
|
||||
rowWithViewIndex[subRowsKey],
|
||||
newPath,
|
||||
index
|
||||
)
|
||||
}
|
||||
return rowWithViewIndex
|
||||
}),
|
||||
index,
|
||||
]
|
||||
}
|
||||
const recurseRowsViewIndex = (rows, path = [], index = -1) => ([
|
||||
rows.map((row, i) => {
|
||||
index += 1
|
||||
const rowWithViewIndex = {
|
||||
...row,
|
||||
_viewIndex: index,
|
||||
}
|
||||
const newPath = path.concat([i])
|
||||
if (rowWithViewIndex[subRowsKey] && _.get(expanded, newPath)) {
|
||||
[rowWithViewIndex[subRowsKey], index] = recurseRowsViewIndex(
|
||||
rowWithViewIndex[subRowsKey],
|
||||
newPath,
|
||||
index,
|
||||
)
|
||||
}
|
||||
return rowWithViewIndex
|
||||
}),
|
||||
index,
|
||||
])
|
||||
;[pageRows] = recurseRowsViewIndex(pageRows)
|
||||
|
||||
const canPrevious = page > 0
|
||||
@ -171,7 +170,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
allVisibleColumns.map(d => {
|
||||
const resizedColumn = resized.find(x => x.id === d.id) || {}
|
||||
return _.getFirstDefined(resizedColumn.value, d.width, d.minWidth)
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
let rowIndex = -1
|
||||
@ -189,59 +188,45 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
rowMinWidth,
|
||||
}
|
||||
|
||||
// Visual Components
|
||||
const rootProps = _.splitProps(
|
||||
getProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
const tableProps = _.splitProps(
|
||||
getTableProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
const tBodyProps = _.splitProps(
|
||||
getTbodyProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
const loadingProps = getLoadingProps(finalState, undefined, undefined, this)
|
||||
const noDataProps = getNoDataProps(finalState, undefined, undefined, this)
|
||||
const resizerProps = getResizerProps(finalState, undefined, undefined, this)
|
||||
|
||||
const makeHeaderGroups = () => {
|
||||
const theadGroupProps = _.splitProps(
|
||||
getTheadGroupProps(finalState, undefined, undefined, this)
|
||||
)
|
||||
const theadGroupTrProps = _.splitProps(
|
||||
getTheadGroupTrProps(finalState, undefined, undefined, this)
|
||||
)
|
||||
return (
|
||||
<TheadComponent
|
||||
className={classnames('-headerGroups', theadGroupProps.className)}
|
||||
style={{
|
||||
...theadGroupProps.style,
|
||||
minWidth: `${rowMinWidth}px`,
|
||||
}}
|
||||
{...theadGroupProps.rest}
|
||||
>
|
||||
<TrComponent
|
||||
className={theadGroupTrProps.className}
|
||||
style={theadGroupTrProps.style}
|
||||
{...theadGroupTrProps.rest}
|
||||
>
|
||||
{headerGroups.map(makeHeaderGroup)}
|
||||
</TrComponent>
|
||||
</TheadComponent>
|
||||
)
|
||||
}
|
||||
// Visual Components
|
||||
|
||||
const makeHeaderGroup = (column, i) => {
|
||||
const resizedValue = col =>
|
||||
(resized.find(x => x.id === col.id) || {}).value
|
||||
const flex = _.sum(
|
||||
column.columns.map(
|
||||
col => (col.width || resizedValue(col) ? 0 : col.minWidth)
|
||||
)
|
||||
col => (col.width || resizedValue(col) ? 0 : col.minWidth),
|
||||
),
|
||||
)
|
||||
const width = _.sum(
|
||||
column.columns.map(col =>
|
||||
_.getFirstDefined(resizedValue(col), col.width, col.minWidth)
|
||||
)
|
||||
_.getFirstDefined(resizedValue(col), col.width, col.minWidth),
|
||||
),
|
||||
)
|
||||
const maxWidth = _.sum(
|
||||
column.columns.map(col =>
|
||||
_.getFirstDefined(resizedValue(col), col.width, col.maxWidth)
|
||||
)
|
||||
_.getFirstDefined(resizedValue(col), col.width, col.maxWidth),
|
||||
),
|
||||
)
|
||||
|
||||
const theadGroupThProps = _.splitProps(
|
||||
getTheadGroupThProps(finalState, undefined, column, this)
|
||||
getTheadGroupThProps(finalState, undefined, column, this),
|
||||
)
|
||||
const columnHeaderProps = _.splitProps(
|
||||
column.getHeaderProps(finalState, undefined, column, this)
|
||||
column.getHeaderProps(finalState, undefined, column, this),
|
||||
)
|
||||
|
||||
const classes = [
|
||||
@ -269,7 +254,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
|
||||
return (
|
||||
<ThComponent
|
||||
key={i + '-' + column.id}
|
||||
key={`${i}-${column.id}`}
|
||||
className={classnames(classes)}
|
||||
style={{
|
||||
...styles,
|
||||
@ -279,34 +264,34 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
>
|
||||
{_.normalizeComponent(column.Header, {
|
||||
data: sortedData,
|
||||
column: column,
|
||||
column,
|
||||
})}
|
||||
</ThComponent>
|
||||
)
|
||||
}
|
||||
|
||||
const makeHeaders = () => {
|
||||
const theadProps = _.splitProps(
|
||||
getTheadProps(finalState, undefined, undefined, this)
|
||||
const makeHeaderGroups = () => {
|
||||
const theadGroupProps = _.splitProps(
|
||||
getTheadGroupProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
const theadTrProps = _.splitProps(
|
||||
getTheadTrProps(finalState, undefined, undefined, this)
|
||||
const theadGroupTrProps = _.splitProps(
|
||||
getTheadGroupTrProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
return (
|
||||
<TheadComponent
|
||||
className={classnames('-header', theadProps.className)}
|
||||
className={classnames('-headerGroups', theadGroupProps.className)}
|
||||
style={{
|
||||
...theadProps.style,
|
||||
...theadGroupProps.style,
|
||||
minWidth: `${rowMinWidth}px`,
|
||||
}}
|
||||
{...theadProps.rest}
|
||||
{...theadGroupProps.rest}
|
||||
>
|
||||
<TrComponent
|
||||
className={theadTrProps.className}
|
||||
style={theadTrProps.style}
|
||||
{...theadTrProps.rest}
|
||||
className={theadGroupTrProps.className}
|
||||
style={theadGroupTrProps.style}
|
||||
{...theadGroupTrProps.rest}
|
||||
>
|
||||
{allVisibleColumns.map(makeHeader)}
|
||||
{headerGroups.map(makeHeaderGroup)}
|
||||
</TrComponent>
|
||||
</TheadComponent>
|
||||
)
|
||||
@ -320,18 +305,18 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
const width = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.minWidth
|
||||
column.minWidth,
|
||||
)
|
||||
const maxWidth = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.maxWidth
|
||||
column.maxWidth,
|
||||
)
|
||||
const theadThProps = _.splitProps(
|
||||
getTheadThProps(finalState, undefined, column, this)
|
||||
getTheadThProps(finalState, undefined, column, this),
|
||||
)
|
||||
const columnHeaderProps = _.splitProps(
|
||||
column.getHeaderProps(finalState, undefined, column, this)
|
||||
column.getHeaderProps(finalState, undefined, column, this),
|
||||
)
|
||||
|
||||
const classes = [
|
||||
@ -364,7 +349,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
|
||||
return (
|
||||
<ThComponent
|
||||
key={i + '-' + column.id}
|
||||
key={`${i}-${column.id}`}
|
||||
className={classnames(
|
||||
classes,
|
||||
isResizable && 'rt-resizable-header',
|
||||
@ -373,7 +358,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
!show && '-hidden',
|
||||
pivotBy &&
|
||||
pivotBy.slice(0, -1).includes(column.id) &&
|
||||
'rt-header-pivot'
|
||||
'rt-header-pivot',
|
||||
)}
|
||||
style={{
|
||||
...styles,
|
||||
@ -382,14 +367,14 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
maxWidth: _.asPx(maxWidth),
|
||||
}}
|
||||
toggleSort={e => {
|
||||
isSortable && this.sortColumn(column, e.shiftKey)
|
||||
if (isSortable) this.sortColumn(column, multiSort ? e.shiftKey : false)
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<div className={classnames(isResizable && 'rt-resizable-header-content')}>
|
||||
{_.normalizeComponent(column.Header, {
|
||||
data: sortedData,
|
||||
column: column,
|
||||
column,
|
||||
})}
|
||||
</div>
|
||||
{resizer}
|
||||
@ -397,28 +382,28 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
)
|
||||
}
|
||||
|
||||
const makeFilters = () => {
|
||||
const theadFilterProps = _.splitProps(
|
||||
getTheadFilterProps(finalState, undefined, undefined, this)
|
||||
const makeHeaders = () => {
|
||||
const theadProps = _.splitProps(
|
||||
getTheadProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
const theadFilterTrProps = _.splitProps(
|
||||
getTheadFilterTrProps(finalState, undefined, undefined, this)
|
||||
const theadTrProps = _.splitProps(
|
||||
getTheadTrProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
return (
|
||||
<TheadComponent
|
||||
className={classnames('-filters', theadFilterProps.className)}
|
||||
className={classnames('-header', theadProps.className)}
|
||||
style={{
|
||||
...theadFilterProps.style,
|
||||
...theadProps.style,
|
||||
minWidth: `${rowMinWidth}px`,
|
||||
}}
|
||||
{...theadFilterProps.rest}
|
||||
{...theadProps.rest}
|
||||
>
|
||||
<TrComponent
|
||||
className={theadFilterTrProps.className}
|
||||
style={theadFilterTrProps.style}
|
||||
{...theadFilterTrProps.rest}
|
||||
className={theadTrProps.className}
|
||||
style={theadTrProps.style}
|
||||
{...theadTrProps.rest}
|
||||
>
|
||||
{allVisibleColumns.map(makeFilter)}
|
||||
{allVisibleColumns.map(makeHeader)}
|
||||
</TrComponent>
|
||||
</TheadComponent>
|
||||
)
|
||||
@ -429,18 +414,18 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
const width = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.minWidth
|
||||
column.minWidth,
|
||||
)
|
||||
const maxWidth = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.maxWidth
|
||||
column.maxWidth,
|
||||
)
|
||||
const theadFilterThProps = _.splitProps(
|
||||
getTheadFilterThProps(finalState, undefined, column, this)
|
||||
getTheadFilterThProps(finalState, undefined, column, this),
|
||||
)
|
||||
const columnHeaderProps = _.splitProps(
|
||||
column.getHeaderProps(finalState, undefined, column, this)
|
||||
column.getHeaderProps(finalState, undefined, column, this),
|
||||
)
|
||||
|
||||
const classes = [
|
||||
@ -467,12 +452,12 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
const isFilterable = _.getFirstDefined(
|
||||
column.filterable,
|
||||
filterable,
|
||||
false
|
||||
false,
|
||||
)
|
||||
|
||||
return (
|
||||
<ThComponent
|
||||
key={i + '-' + column.id}
|
||||
key={`${i}-${column.id}`}
|
||||
className={classnames(classes)}
|
||||
style={{
|
||||
...styles,
|
||||
@ -490,21 +475,48 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
filter,
|
||||
onChange: value => this.filterColumn(column, value),
|
||||
},
|
||||
defaultProps.column.Filter
|
||||
defaultProps.column.Filter,
|
||||
)
|
||||
: null}
|
||||
</ThComponent>
|
||||
)
|
||||
}
|
||||
|
||||
const makeFilters = () => {
|
||||
const theadFilterProps = _.splitProps(
|
||||
getTheadFilterProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
const theadFilterTrProps = _.splitProps(
|
||||
getTheadFilterTrProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
return (
|
||||
<TheadComponent
|
||||
className={classnames('-filters', theadFilterProps.className)}
|
||||
style={{
|
||||
...theadFilterProps.style,
|
||||
minWidth: `${rowMinWidth}px`,
|
||||
}}
|
||||
{...theadFilterProps.rest}
|
||||
>
|
||||
<TrComponent
|
||||
className={theadFilterTrProps.className}
|
||||
style={theadFilterTrProps.style}
|
||||
{...theadFilterTrProps.rest}
|
||||
>
|
||||
{allVisibleColumns.map(makeFilter)}
|
||||
</TrComponent>
|
||||
</TheadComponent>
|
||||
)
|
||||
}
|
||||
|
||||
const makePageRow = (row, i, path = []) => {
|
||||
const rowInfo = {
|
||||
original: row[originalKey],
|
||||
row: row,
|
||||
row,
|
||||
index: row[indexKey],
|
||||
viewIndex: ++rowIndex,
|
||||
pageSize: pageSize,
|
||||
page: page,
|
||||
viewIndex: rowIndex += 1,
|
||||
pageSize,
|
||||
page,
|
||||
level: path.length,
|
||||
nestingPath: path.concat([i]),
|
||||
aggregated: row[aggregatedKey],
|
||||
@ -514,14 +526,14 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
const isExpanded = _.get(expanded, rowInfo.nestingPath)
|
||||
const trGroupProps = getTrGroupProps(finalState, rowInfo, undefined, this)
|
||||
const trProps = _.splitProps(
|
||||
getTrProps(finalState, rowInfo, undefined, this)
|
||||
getTrProps(finalState, rowInfo, undefined, this),
|
||||
)
|
||||
return (
|
||||
<TrGroupComponent key={rowInfo.nestingPath.join('_')} {...trGroupProps}>
|
||||
<TrComponent
|
||||
className={classnames(
|
||||
trProps.className,
|
||||
row._viewIndex % 2 ? '-even' : '-odd'
|
||||
row._viewIndex % 2 ? '-even' : '-odd',
|
||||
)}
|
||||
style={trProps.style}
|
||||
{...trProps.rest}
|
||||
@ -533,18 +545,18 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
const width = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.minWidth
|
||||
column.minWidth,
|
||||
)
|
||||
const maxWidth = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.maxWidth
|
||||
column.maxWidth,
|
||||
)
|
||||
const tdProps = _.splitProps(
|
||||
getTdProps(finalState, rowInfo, column, this)
|
||||
getTdProps(finalState, rowInfo, column, this),
|
||||
)
|
||||
const columnProps = _.splitProps(
|
||||
column.getProps(finalState, rowInfo, column, this)
|
||||
column.getProps(finalState, rowInfo, column, this),
|
||||
)
|
||||
|
||||
const classes = [
|
||||
@ -594,10 +606,10 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
{
|
||||
expanded: newExpanded,
|
||||
},
|
||||
() => {
|
||||
() => (
|
||||
onExpandedChange &&
|
||||
onExpandedChange(newExpanded, cellInfo.nestingPath, e)
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -605,7 +617,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
let resolvedCell = _.normalizeComponent(
|
||||
column.Cell,
|
||||
cellInfo,
|
||||
value
|
||||
value,
|
||||
)
|
||||
|
||||
// Resolve Renderers
|
||||
@ -632,7 +644,8 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
// Make it expandable by defualt
|
||||
cellInfo.expandable = true
|
||||
useOnExpanderClick = true
|
||||
// If pivoted, has no subRows, and does not have a subComponent, do not make expandable
|
||||
// If pivoted, has no subRows, and does not have a subComponent,
|
||||
// do not make expandable
|
||||
if (cellInfo.pivoted && !cellInfo.subRows && !SubComponent) {
|
||||
cellInfo.expandable = false
|
||||
}
|
||||
@ -655,14 +668,14 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
...cellInfo,
|
||||
value: row[pivotValKey],
|
||||
},
|
||||
row[pivotValKey]
|
||||
row[pivotValKey],
|
||||
)
|
||||
} else if (isPreview) {
|
||||
// Show the pivot preview
|
||||
resolvedCell = _.normalizeComponent(
|
||||
ResolvedAggregatedComponent,
|
||||
cellInfo,
|
||||
value
|
||||
value,
|
||||
)
|
||||
} else {
|
||||
resolvedCell = null
|
||||
@ -671,7 +684,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
resolvedCell = _.normalizeComponent(
|
||||
ResolvedAggregatedComponent,
|
||||
cellInfo,
|
||||
value
|
||||
value,
|
||||
)
|
||||
}
|
||||
|
||||
@ -679,7 +692,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
resolvedCell = _.normalizeComponent(
|
||||
ResolvedExpanderComponent,
|
||||
cellInfo,
|
||||
row[pivotValKey]
|
||||
row[pivotValKey],
|
||||
)
|
||||
if (pivotBy) {
|
||||
if (cellInfo.groupedByPivot) {
|
||||
@ -695,7 +708,9 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
? onExpanderClick
|
||||
: () => {}
|
||||
|
||||
// If there are multiple onClick events, make sure they don't override eachother. This should maybe be expanded to handle all function attributes
|
||||
// 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,
|
||||
}
|
||||
@ -715,12 +730,13 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
// Return the cell
|
||||
return (
|
||||
<TdComponent
|
||||
key={i2 + '-' + column.id}
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={`${i2}-${column.id}`}
|
||||
className={classnames(
|
||||
classes,
|
||||
!show && 'hidden',
|
||||
cellInfo.expandable && 'rt-expandable',
|
||||
(isBranch || isPreview) && 'rt-pivot'
|
||||
(isBranch || isPreview) && 'rt-pivot',
|
||||
)}
|
||||
style={{
|
||||
...styles,
|
||||
@ -740,7 +756,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
{rowInfo.subRows &&
|
||||
isExpanded &&
|
||||
rowInfo.subRows.map((d, i) =>
|
||||
makePageRow(d, i, rowInfo.nestingPath)
|
||||
makePageRow(d, i, rowInfo.nestingPath),
|
||||
)}
|
||||
{SubComponent &&
|
||||
!rowInfo.subRows &&
|
||||
@ -750,52 +766,26 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
)
|
||||
}
|
||||
|
||||
const makePadRow = (row, i) => {
|
||||
const trGroupProps = getTrGroupProps(
|
||||
finalState,
|
||||
undefined,
|
||||
undefined,
|
||||
this
|
||||
)
|
||||
const trProps = _.splitProps(
|
||||
getTrProps(finalState, undefined, undefined, this)
|
||||
)
|
||||
return (
|
||||
<TrGroupComponent key={i} {...trGroupProps}>
|
||||
<TrComponent
|
||||
className={classnames(
|
||||
'-padRow',
|
||||
(pageRows.length + i) % 2 ? '-even' : '-odd',
|
||||
trProps.className
|
||||
)}
|
||||
style={trProps.style || {}}
|
||||
>
|
||||
{allVisibleColumns.map(makePadColumn)}
|
||||
</TrComponent>
|
||||
</TrGroupComponent>
|
||||
)
|
||||
}
|
||||
|
||||
const makePadColumn = (column, i) => {
|
||||
const resizedCol = resized.find(x => x.id === column.id) || {}
|
||||
const show =
|
||||
typeof column.show === 'function' ? column.show() : column.show
|
||||
let width = _.getFirstDefined(
|
||||
const width = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.minWidth
|
||||
column.minWidth,
|
||||
)
|
||||
let flex = width
|
||||
let maxWidth = _.getFirstDefined(
|
||||
const flex = width
|
||||
const maxWidth = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.maxWidth
|
||||
column.maxWidth,
|
||||
)
|
||||
const tdProps = _.splitProps(
|
||||
getTdProps(finalState, undefined, column, this)
|
||||
getTdProps(finalState, undefined, column, this),
|
||||
)
|
||||
const columnProps = _.splitProps(
|
||||
column.getProps(finalState, undefined, column, this)
|
||||
column.getProps(finalState, undefined, column, this),
|
||||
)
|
||||
|
||||
const classes = [
|
||||
@ -812,7 +802,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
|
||||
return (
|
||||
<TdComponent
|
||||
key={i + '-' + column.id}
|
||||
key={`${i}-${column.id}`}
|
||||
className={classnames(classes, !show && 'hidden')}
|
||||
style={{
|
||||
...styles,
|
||||
@ -827,10 +817,96 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
)
|
||||
}
|
||||
|
||||
const makePadRow = (row, i) => {
|
||||
const trGroupProps = getTrGroupProps(
|
||||
finalState,
|
||||
undefined,
|
||||
undefined,
|
||||
this,
|
||||
)
|
||||
const trProps = _.splitProps(
|
||||
getTrProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
return (
|
||||
<TrGroupComponent key={i} {...trGroupProps}>
|
||||
<TrComponent
|
||||
className={classnames(
|
||||
'-padRow',
|
||||
(pageRows.length + i) % 2 ? '-even' : '-odd',
|
||||
trProps.className,
|
||||
)}
|
||||
style={trProps.style || {}}
|
||||
>
|
||||
{allVisibleColumns.map(makePadColumn)}
|
||||
</TrComponent>
|
||||
</TrGroupComponent>
|
||||
)
|
||||
}
|
||||
|
||||
const makeColumnFooter = (column, i) => {
|
||||
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,
|
||||
)
|
||||
const maxWidth = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.maxWidth,
|
||||
)
|
||||
const tFootTdProps = _.splitProps(
|
||||
getTfootTdProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
const columnProps = _.splitProps(
|
||||
column.getProps(finalState, undefined, column, this),
|
||||
)
|
||||
const columnFooterProps = _.splitProps(
|
||||
column.getFooterProps(finalState, undefined, column, this),
|
||||
)
|
||||
|
||||
const classes = [
|
||||
tFootTdProps.className,
|
||||
column.className,
|
||||
columnProps.className,
|
||||
columnFooterProps.className,
|
||||
]
|
||||
|
||||
const styles = {
|
||||
...tFootTdProps.style,
|
||||
...column.style,
|
||||
...columnProps.style,
|
||||
...columnFooterProps.style,
|
||||
}
|
||||
|
||||
return (
|
||||
<TdComponent
|
||||
key={`${i}-${column.id}`}
|
||||
className={classnames(classes, !show && 'hidden')}
|
||||
style={{
|
||||
...styles,
|
||||
flex: `${width} 0 auto`,
|
||||
width: _.asPx(width),
|
||||
maxWidth: _.asPx(maxWidth),
|
||||
}}
|
||||
{...columnProps.rest}
|
||||
{...tFootTdProps.rest}
|
||||
{...columnFooterProps.rest}
|
||||
>
|
||||
{_.normalizeComponent(column.Footer, {
|
||||
data: sortedData,
|
||||
column,
|
||||
})}
|
||||
</TdComponent>
|
||||
)
|
||||
}
|
||||
|
||||
const makeColumnFooters = () => {
|
||||
const tFootProps = getTfootProps(finalState, undefined, undefined, this)
|
||||
const tFootTrProps = _.splitProps(
|
||||
getTfootTrProps(finalState, undefined, undefined, this)
|
||||
getTfootTrProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
return (
|
||||
<TfootComponent
|
||||
@ -852,69 +928,9 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
)
|
||||
}
|
||||
|
||||
const makeColumnFooter = (column, i) => {
|
||||
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
|
||||
)
|
||||
const maxWidth = _.getFirstDefined(
|
||||
resizedCol.value,
|
||||
column.width,
|
||||
column.maxWidth
|
||||
)
|
||||
const tFootTdProps = _.splitProps(
|
||||
getTfootTdProps(finalState, undefined, undefined, this)
|
||||
)
|
||||
const columnProps = _.splitProps(
|
||||
column.getProps(finalState, undefined, column, this)
|
||||
)
|
||||
const columnFooterProps = _.splitProps(
|
||||
column.getFooterProps(finalState, undefined, column, this)
|
||||
)
|
||||
|
||||
const classes = [
|
||||
tFootTdProps.className,
|
||||
column.className,
|
||||
columnProps.className,
|
||||
columnFooterProps.className,
|
||||
]
|
||||
|
||||
const styles = {
|
||||
...tFootTdProps.style,
|
||||
...column.style,
|
||||
...columnProps.style,
|
||||
...columnFooterProps.style,
|
||||
}
|
||||
|
||||
return (
|
||||
<TdComponent
|
||||
key={i + '-' + column.id}
|
||||
className={classnames(classes, !show && 'hidden')}
|
||||
style={{
|
||||
...styles,
|
||||
flex: `${width} 0 auto`,
|
||||
width: _.asPx(width),
|
||||
maxWidth: _.asPx(maxWidth),
|
||||
}}
|
||||
{...columnProps.rest}
|
||||
{...tFootTdProps.rest}
|
||||
{...columnFooterProps.rest}
|
||||
>
|
||||
{_.normalizeComponent(column.Footer, {
|
||||
data: sortedData,
|
||||
column: column,
|
||||
})}
|
||||
</TdComponent>
|
||||
)
|
||||
}
|
||||
|
||||
const makePagination = () => {
|
||||
const paginationProps = _.splitProps(
|
||||
getPaginationProps(finalState, undefined, undefined, this)
|
||||
getPaginationProps(finalState, undefined, undefined, this),
|
||||
)
|
||||
return (
|
||||
<PaginationComponent
|
||||
@ -931,19 +947,6 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
)
|
||||
}
|
||||
|
||||
const rootProps = _.splitProps(
|
||||
getProps(finalState, undefined, undefined, this)
|
||||
)
|
||||
const tableProps = _.splitProps(
|
||||
getTableProps(finalState, undefined, undefined, this)
|
||||
)
|
||||
const tBodyProps = _.splitProps(
|
||||
getTbodyProps(finalState, undefined, undefined, this)
|
||||
)
|
||||
const loadingProps = getLoadingProps(finalState, undefined, undefined, this)
|
||||
const noDataProps = getNoDataProps(finalState, undefined, undefined, this)
|
||||
const resizerProps = getResizerProps(finalState, undefined, undefined, this)
|
||||
|
||||
const makeTable = () => {
|
||||
const pagination = makePagination()
|
||||
return (
|
||||
@ -956,14 +959,14 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
{...rootProps.rest}
|
||||
>
|
||||
{showPagination && showPaginationTop
|
||||
? <div className='pagination-top'>
|
||||
? <div className="pagination-top">
|
||||
{pagination}
|
||||
</div>
|
||||
: null}
|
||||
<TableComponent
|
||||
className={classnames(
|
||||
tableProps.className,
|
||||
currentlyResizing ? 'rt-resizing' : ''
|
||||
currentlyResizing ? 'rt-resizing' : '',
|
||||
)}
|
||||
style={tableProps.style}
|
||||
{...tableProps.rest}
|
||||
@ -985,7 +988,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
|
||||
{hasColumnFooter ? makeColumnFooters() : null}
|
||||
</TableComponent>
|
||||
{showPagination && showPaginationBottom
|
||||
? <div className='pagination-bottom'>
|
||||
? <div className="pagination-bottom">
|
||||
{pagination}
|
||||
</div>
|
||||
: null}
|
||||
|
||||
@ -64,7 +64,7 @@ export default Base =>
|
||||
if (freezeWhenExpanded) {
|
||||
// if any rows are expanded, freeze the existing data and sorting
|
||||
const keys = Object.keys(newResolvedState.expanded)
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
if (newResolvedState.expanded[keys[i]]) {
|
||||
newResolvedState.frozen = true
|
||||
break
|
||||
@ -109,18 +109,18 @@ export default Base =>
|
||||
newResolvedState.pages = newResolvedState.manual
|
||||
? newResolvedState.pages
|
||||
: Math.ceil(
|
||||
newResolvedState.sortedData.length / newResolvedState.pageSize
|
||||
newResolvedState.sortedData.length / newResolvedState.pageSize,
|
||||
)
|
||||
newResolvedState.page = Math.max(
|
||||
newResolvedState.page >= newResolvedState.pages
|
||||
? newResolvedState.pages - 1
|
||||
: newResolvedState.page,
|
||||
0
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
return this.setState(newResolvedState, () => {
|
||||
cb && cb()
|
||||
if (cb) { cb() }
|
||||
if (
|
||||
oldState.page !== newResolvedState.page ||
|
||||
oldState.pageSize !== newResolvedState.pageSize ||
|
||||
|
||||
194
src/methods.js
194
src/methods.js
@ -42,7 +42,7 @@ export default Base =>
|
||||
let expanderColumn = columns.find(
|
||||
col =>
|
||||
col.expander ||
|
||||
(col.columns && col.columns.some(col2 => col2.expander))
|
||||
(col.columns && col.columns.some(col2 => col2.expander)),
|
||||
)
|
||||
// The actual expander might be in the columns field of a group column
|
||||
if (expanderColumn && !expanderColumn.expander) {
|
||||
@ -91,48 +91,48 @@ export default Base =>
|
||||
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.'
|
||||
'A column id is required if using a non-string accessor for column above.',
|
||||
)
|
||||
}
|
||||
|
||||
// Fall back to an undefined accessor
|
||||
if (!dcol.accessor) {
|
||||
dcol.accessor = d => undefined
|
||||
dcol.accessor = () => undefined
|
||||
}
|
||||
|
||||
return dcol
|
||||
}
|
||||
|
||||
const allDecoratedColumns = []
|
||||
|
||||
// Decorate the columns
|
||||
const decorateAndAddToAll = (column, parentColumn) => {
|
||||
const decoratedColumn = makeDecoratedColumn(column, parentColumn)
|
||||
allDecoratedColumns.push(decoratedColumn)
|
||||
return decoratedColumn
|
||||
}
|
||||
const allDecoratedColumns = []
|
||||
const decoratedColumns = columnsWithExpander.map((column, i) => {
|
||||
|
||||
const decoratedColumns = columnsWithExpander.map(column => {
|
||||
if (column.columns) {
|
||||
return {
|
||||
...column,
|
||||
columns: column.columns.map(d => decorateAndAddToAll(d, column)),
|
||||
}
|
||||
} else {
|
||||
return decorateAndAddToAll(column)
|
||||
}
|
||||
return decorateAndAddToAll(column)
|
||||
})
|
||||
|
||||
// Build the visible columns, headers and flat column list
|
||||
let visibleColumns = decoratedColumns.slice()
|
||||
let allVisibleColumns = []
|
||||
|
||||
visibleColumns = visibleColumns.map((column, i) => {
|
||||
visibleColumns = visibleColumns.map(column => {
|
||||
if (column.columns) {
|
||||
const visibleSubColumns = column.columns.filter(
|
||||
d =>
|
||||
pivotBy.indexOf(d.id) > -1
|
||||
? false
|
||||
: _.getFirstDefined(d.show, true)
|
||||
)
|
||||
const visibleSubColumns = column.columns.filter(d => (
|
||||
pivotBy.indexOf(d.id) > -1
|
||||
? false
|
||||
: _.getFirstDefined(d.show, true)
|
||||
))
|
||||
return {
|
||||
...column,
|
||||
columns: visibleSubColumns,
|
||||
@ -141,13 +141,13 @@ export default Base =>
|
||||
return column
|
||||
})
|
||||
|
||||
visibleColumns = visibleColumns.filter(column => {
|
||||
return column.columns
|
||||
visibleColumns = visibleColumns.filter(column => (
|
||||
column.columns
|
||||
? column.columns.length
|
||||
: pivotBy.indexOf(column.id) > -1
|
||||
? false
|
||||
: _.getFirstDefined(column.show, true)
|
||||
})
|
||||
))
|
||||
|
||||
// Find any custom pivot location
|
||||
const pivotIndex = visibleColumns.findIndex(col => col.pivot)
|
||||
@ -163,10 +163,10 @@ export default Base =>
|
||||
}
|
||||
})
|
||||
|
||||
let PivotParentColumn = pivotColumns.reduce(
|
||||
const PivotParentColumn = pivotColumns.reduce(
|
||||
(prev, current) =>
|
||||
prev && prev === current.parentColumn && current.parentColumn,
|
||||
pivotColumns[0].parentColumn
|
||||
pivotColumns[0].parentColumn,
|
||||
)
|
||||
|
||||
let PivotGroupHeader = hasHeaderGroups && PivotParentColumn.Header
|
||||
@ -202,13 +202,13 @@ export default Base =>
|
||||
headerGroups.push({
|
||||
...this.props.column,
|
||||
...column,
|
||||
columns: columns,
|
||||
columns,
|
||||
})
|
||||
currentSpan = []
|
||||
}
|
||||
|
||||
// Build flast list of allVisibleColumns and HeaderGroups
|
||||
visibleColumns.forEach((column, i) => {
|
||||
visibleColumns.forEach(column => {
|
||||
if (column.columns) {
|
||||
allVisibleColumns = allVisibleColumns.concat(column.columns)
|
||||
if (currentSpan.length > 0) {
|
||||
@ -238,13 +238,18 @@ export default Base =>
|
||||
})
|
||||
if (row[subRowsKey]) {
|
||||
row[subRowsKey] = row[subRowsKey].map((d, i) =>
|
||||
accessRow(d, i, level + 1)
|
||||
accessRow(d, i, level + 1),
|
||||
)
|
||||
}
|
||||
return row
|
||||
}
|
||||
let resolvedData = data.map((d, i) => accessRow(d, i))
|
||||
|
||||
// TODO: Make it possible to fabricate nested rows without pivoting
|
||||
const aggregatingColumns = allVisibleColumns.filter(
|
||||
d => !d.expander && d.aggregate,
|
||||
)
|
||||
|
||||
// If pivoting, recursively group the data
|
||||
const aggregate = rows => {
|
||||
const aggregationValues = {}
|
||||
@ -254,11 +259,6 @@ export default Base =>
|
||||
})
|
||||
return aggregationValues
|
||||
}
|
||||
|
||||
// TODO: Make it possible to fabricate nested rows without pivoting
|
||||
const aggregatingColumns = allVisibleColumns.filter(
|
||||
d => !d.expander && d.aggregate
|
||||
)
|
||||
if (pivotBy.length) {
|
||||
const groupRecursively = (rows, keys, i = 0) => {
|
||||
// This is the last level, just return the rows
|
||||
@ -267,20 +267,18 @@ export default Base =>
|
||||
}
|
||||
// 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,
|
||||
[nestingLevelKey]: i,
|
||||
[groupedByPivotKey]: true,
|
||||
}
|
||||
})
|
||||
_.groupBy(rows, keys[i]),
|
||||
).map(([key, value]) => ({
|
||||
[pivotIDKey]: keys[i],
|
||||
[pivotValKey]: key,
|
||||
[keys[i]]: key,
|
||||
[subRowsKey]: value,
|
||||
[nestingLevelKey]: i,
|
||||
[groupedByPivotKey]: true,
|
||||
}))
|
||||
// Recurse into the subRows
|
||||
groupedRows = groupedRows.map(rowGroup => {
|
||||
let subRows = groupRecursively(rowGroup[subRowsKey], keys, i + 1)
|
||||
const subRows = groupRecursively(rowGroup[subRowsKey], keys, i + 1)
|
||||
return {
|
||||
...rowGroup,
|
||||
[subRowsKey]: subRows,
|
||||
@ -329,10 +327,10 @@ export default Base =>
|
||||
resolvedData,
|
||||
filtered,
|
||||
defaultFilterMethod,
|
||||
allVisibleColumns
|
||||
allVisibleColumns,
|
||||
),
|
||||
sorted,
|
||||
sortMethodsByColumnID
|
||||
sortMethodsByColumnID,
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -366,11 +364,10 @@ export default Base =>
|
||||
// If 'filterAll' is set to true, pass the entire dataset to the filter method
|
||||
if (column.filterAll) {
|
||||
return filterMethod(nextFilter, filteredSoFar, column)
|
||||
} else {
|
||||
return filteredSoFar.filter(row => {
|
||||
return filterMethod(nextFilter, row, column)
|
||||
})
|
||||
}
|
||||
return filteredSoFar.filter(row => (
|
||||
filterMethod(nextFilter, row, column)
|
||||
))
|
||||
}, filteredData)
|
||||
|
||||
// Apply the filter to the subrows if we are pivoting, and then
|
||||
@ -386,7 +383,7 @@ export default Base =>
|
||||
row[this.props.subRowsKey],
|
||||
filtered,
|
||||
defaultFilterMethod,
|
||||
allVisibleColumns
|
||||
allVisibleColumns,
|
||||
),
|
||||
}
|
||||
})
|
||||
@ -411,16 +408,16 @@ export default Base =>
|
||||
sorted.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], sort.desc)
|
||||
}
|
||||
}
|
||||
return (a, b) => {
|
||||
return this.props.defaultSortMethod(a[sort.id], b[sort.id], sort.desc)
|
||||
return (a, b) => (
|
||||
sortMethodsByColumnID[sort.id](a[sort.id], b[sort.id], sort.desc)
|
||||
)
|
||||
}
|
||||
return (a, b) => (
|
||||
this.props.defaultSortMethod(a[sort.id], b[sort.id], sort.desc)
|
||||
)
|
||||
}),
|
||||
sorted.map(d => !d.desc),
|
||||
this.props.indexKey
|
||||
this.props.indexKey,
|
||||
)
|
||||
|
||||
sortedData.forEach(row => {
|
||||
@ -430,7 +427,7 @@ export default Base =>
|
||||
row[this.props.subRowsKey] = this.sortData(
|
||||
row[this.props.subRowsKey],
|
||||
sorted,
|
||||
sortMethodsByColumnID
|
||||
sortMethodsByColumnID,
|
||||
)
|
||||
})
|
||||
|
||||
@ -440,7 +437,7 @@ export default Base =>
|
||||
getMinRows () {
|
||||
return _.getFirstDefined(
|
||||
this.props.minRows,
|
||||
this.getStateOrProp('pageSize')
|
||||
this.getStateOrProp('pageSize'),
|
||||
)
|
||||
}
|
||||
|
||||
@ -452,9 +449,9 @@ export default Base =>
|
||||
if (collapseOnPageChange) {
|
||||
newState.expanded = {}
|
||||
}
|
||||
this.setStateWithData(newState, () => {
|
||||
this.setStateWithData(newState, () => (
|
||||
onPageChange && onPageChange(page)
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
onPageSizeChange (newPageSize) {
|
||||
@ -470,16 +467,16 @@ export default Base =>
|
||||
pageSize: newPageSize,
|
||||
page: newPage,
|
||||
},
|
||||
() => {
|
||||
() => (
|
||||
onPageSizeChange && onPageSizeChange(newPageSize, newPage)
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
sortColumn (column, additive) {
|
||||
const { sorted, skipNextSort, defaultSortDesc } = this.getResolvedState()
|
||||
|
||||
const firstSortDirection = column.hasOwnProperty('defaultSortDesc')
|
||||
const firstSortDirection = Object.prototype.hasOwnProperty.call(column, 'defaultSortDesc')
|
||||
? column.defaultSortDesc
|
||||
: defaultSortDesc
|
||||
const secondSortDirection = !firstSortDirection
|
||||
@ -519,20 +516,18 @@ export default Base =>
|
||||
newSorted = [existing]
|
||||
}
|
||||
}
|
||||
} else if (additive) {
|
||||
newSorted.push({
|
||||
id: column.id,
|
||||
desc: firstSortDirection,
|
||||
})
|
||||
} else {
|
||||
if (additive) {
|
||||
newSorted.push({
|
||||
newSorted = [
|
||||
{
|
||||
id: column.id,
|
||||
desc: firstSortDirection,
|
||||
})
|
||||
} else {
|
||||
newSorted = [
|
||||
{
|
||||
id: column.id,
|
||||
desc: firstSortDirection,
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
} else {
|
||||
// Multi-Sort
|
||||
@ -556,21 +551,19 @@ export default Base =>
|
||||
if (!additive) {
|
||||
newSorted = newSorted.slice(existingIndex, column.length)
|
||||
}
|
||||
} else {
|
||||
// New Sort Column
|
||||
if (additive) {
|
||||
newSorted = newSorted.concat(
|
||||
column.map(d => ({
|
||||
id: d.id,
|
||||
desc: firstSortDirection,
|
||||
}))
|
||||
)
|
||||
} else {
|
||||
newSorted = column.map(d => ({
|
||||
// New Sort Column
|
||||
} else if (additive) {
|
||||
newSorted = newSorted.concat(
|
||||
column.map(d => ({
|
||||
id: d.id,
|
||||
desc: firstSortDirection,
|
||||
}))
|
||||
}
|
||||
})),
|
||||
)
|
||||
} else {
|
||||
newSorted = column.map(d => ({
|
||||
id: d.id,
|
||||
desc: firstSortDirection,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -582,9 +575,9 @@ export default Base =>
|
||||
: this.state.page,
|
||||
sorted: newSorted,
|
||||
},
|
||||
() => {
|
||||
() => (
|
||||
onSortedChange && onSortedChange(newSorted, column, additive)
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -593,16 +586,14 @@ export default Base =>
|
||||
const { onFilteredChange } = this.props
|
||||
|
||||
// Remove old filter first if it exists
|
||||
const newFiltering = (filtered || []).filter(x => {
|
||||
if (x.id !== column.id) {
|
||||
return true
|
||||
}
|
||||
})
|
||||
const newFiltering = (filtered || []).filter(x => (
|
||||
x.id !== column.id
|
||||
))
|
||||
|
||||
if (value !== '') {
|
||||
newFiltering.push({
|
||||
id: column.id,
|
||||
value: value,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
@ -610,9 +601,9 @@ export default Base =>
|
||||
{
|
||||
filtered: newFiltering,
|
||||
},
|
||||
() => {
|
||||
() => (
|
||||
onFilteredChange && onFilteredChange(newFiltering, column, value)
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -634,7 +625,7 @@ export default Base =>
|
||||
currentlyResizing: {
|
||||
id: column.id,
|
||||
startX: pageX,
|
||||
parentWidth: parentWidth,
|
||||
parentWidth,
|
||||
},
|
||||
},
|
||||
() => {
|
||||
@ -647,7 +638,7 @@ export default Base =>
|
||||
document.addEventListener('mouseup', this.resizeColumnEnd)
|
||||
document.addEventListener('mouseleave', this.resizeColumnEnd)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -667,10 +658,11 @@ export default Base =>
|
||||
pageX = event.pageX
|
||||
}
|
||||
|
||||
// Set the min size to 10 to account for margin and border or else the group headers don't line up correctly
|
||||
// Set the min size to 10 to account for margin and border or else the
|
||||
// group headers don't line up correctly
|
||||
const newWidth = Math.max(
|
||||
currentlyResizing.parentWidth + pageX - currentlyResizing.startX,
|
||||
11
|
||||
11,
|
||||
)
|
||||
|
||||
newResized.push({
|
||||
@ -682,15 +674,15 @@ export default Base =>
|
||||
{
|
||||
resized: newResized,
|
||||
},
|
||||
() => {
|
||||
() => (
|
||||
onResizedChange && onResizedChange(newResized, event)
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
resizeColumnEnd (event) {
|
||||
event.stopPropagation()
|
||||
let isTouch = event.type === 'touchend' || event.type === 'touchcancel'
|
||||
const isTouch = event.type === 'touchend' || event.type === 'touchcancel'
|
||||
|
||||
if (isTouch) {
|
||||
document.removeEventListener('touchmove', this.resizeColumnMoving)
|
||||
|
||||
@ -4,7 +4,7 @@ import classnames from 'classnames'
|
||||
// import _ from './utils'
|
||||
|
||||
const defaultButton = props => (
|
||||
<button type='button' {...props} className='-btn'>
|
||||
<button type="button" {...props} className="-btn">
|
||||
{props.children}
|
||||
</button>
|
||||
)
|
||||
@ -42,7 +42,7 @@ export default class ReactTablePagination extends Component {
|
||||
}
|
||||
|
||||
applyPage (e) {
|
||||
e && e.preventDefault()
|
||||
if (e) { e.preventDefault() }
|
||||
const page = this.state.page
|
||||
this.changePage(page === '' ? this.props.page : page)
|
||||
}
|
||||
@ -70,9 +70,9 @@ export default class ReactTablePagination extends Component {
|
||||
className={classnames(className, '-pagination')}
|
||||
style={this.props.paginationStyle}
|
||||
>
|
||||
<div className='-previous'>
|
||||
<div className="-previous">
|
||||
<PreviousComponent
|
||||
onClick={e => {
|
||||
onClick={() => {
|
||||
if (!canPrevious) return
|
||||
this.changePage(page - 1)
|
||||
}}
|
||||
@ -81,11 +81,11 @@ export default class ReactTablePagination extends Component {
|
||||
{this.props.previousText}
|
||||
</PreviousComponent>
|
||||
</div>
|
||||
<div className='-center'>
|
||||
<span className='-pageInfo'>
|
||||
<div className="-center">
|
||||
<span className="-pageInfo">
|
||||
{this.props.pageText}{' '}
|
||||
{showPageJump
|
||||
? <div className='-pageJump'>
|
||||
? <div className="-pageJump">
|
||||
<input
|
||||
type={this.state.page === '' ? 'text' : 'number'}
|
||||
onChange={e => {
|
||||
@ -105,31 +105,30 @@ export default class ReactTablePagination extends Component {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
: <span className='-currentPage'>
|
||||
: <span className="-currentPage">
|
||||
{page + 1}
|
||||
</span>}{' '}
|
||||
{this.props.ofText}{' '}
|
||||
<span className='-totalPages'>{pages || 1}</span>
|
||||
<span className="-totalPages">{pages || 1}</span>
|
||||
</span>
|
||||
{showPageSizeOptions &&
|
||||
<span className='select-wrap -pageSizeOptions'>
|
||||
<span className="select-wrap -pageSizeOptions">
|
||||
<select
|
||||
onChange={e => onPageSizeChange(Number(e.target.value))}
|
||||
value={pageSize}
|
||||
>
|
||||
{pageSizeOptions.map((option, i) => {
|
||||
return (
|
||||
<option key={i} value={option}>
|
||||
{option} {this.props.rowsText}
|
||||
</option>
|
||||
)
|
||||
})}
|
||||
{pageSizeOptions.map((option, i) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<option key={i} value={option}>
|
||||
{option} {this.props.rowsText}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</span>}
|
||||
</div>
|
||||
<div className='-next'>
|
||||
<div className="-next">
|
||||
<NextComponent
|
||||
onClick={e => {
|
||||
onClick={() => {
|
||||
if (!canNext) return
|
||||
this.changePage(page + 1)
|
||||
}}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import PropTypes from "prop-types";
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default {
|
||||
// General
|
||||
@ -85,37 +85,37 @@ export default {
|
||||
Cell: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
PropTypes.func,
|
||||
]),
|
||||
Header: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
PropTypes.func,
|
||||
]),
|
||||
Footer: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
PropTypes.func,
|
||||
]),
|
||||
Aggregated: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
PropTypes.func,
|
||||
]),
|
||||
Pivot: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
PropTypes.func,
|
||||
]),
|
||||
PivotValue: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
PropTypes.func,
|
||||
]),
|
||||
Expander: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
PropTypes.func,
|
||||
]),
|
||||
Filter: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
|
||||
|
||||
@ -145,8 +145,8 @@ export default {
|
||||
getFooterProps: PropTypes.object,
|
||||
filterMethod: PropTypes.func,
|
||||
filterAll: PropTypes.bool,
|
||||
sortMethod: PropTypes.func
|
||||
})
|
||||
sortMethod: PropTypes.func,
|
||||
}),
|
||||
),
|
||||
|
||||
// Global Expander Column Defaults
|
||||
@ -154,7 +154,7 @@ export default {
|
||||
sortable: PropTypes.bool,
|
||||
resizable: PropTypes.bool,
|
||||
filterable: PropTypes.bool,
|
||||
width: PropTypes.number
|
||||
width: PropTypes.number,
|
||||
}),
|
||||
|
||||
pivotDefaults: PropTypes.object,
|
||||
@ -181,7 +181,8 @@ export default {
|
||||
ExpanderComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
PivotValueComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
AggregatedComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
PivotComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]), // this is a computed default generated using
|
||||
// this is a computed default generated using
|
||||
PivotComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
// the ExpanderComponent and PivotValueComponent at run-time in methods.js
|
||||
PaginationComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
PreviousComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
@ -189,5 +190,5 @@ export default {
|
||||
LoadingComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
NoDataComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
ResizerComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
PadRowComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element])
|
||||
};
|
||||
PadRowComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
}
|
||||
|
||||
33
src/utils.js
33
src/utils.js
@ -30,7 +30,9 @@ function get (obj, path, def) {
|
||||
let val
|
||||
try {
|
||||
val = pathObj.reduce((current, pathPart) => current[pathPart], obj)
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
// continue regardless of error
|
||||
}
|
||||
return typeof val !== 'undefined' ? val : def
|
||||
}
|
||||
|
||||
@ -59,7 +61,7 @@ function last (arr) {
|
||||
|
||||
function range (n) {
|
||||
const arr = []
|
||||
for (let i = 0; i < n; i++) {
|
||||
for (let i = 0; i < n; i += 1) {
|
||||
arr.push(n)
|
||||
}
|
||||
return arr
|
||||
@ -67,7 +69,7 @@ function range (n) {
|
||||
|
||||
function orderBy (arr, funcs, dirs, indexKey) {
|
||||
return arr.sort((rowA, rowB) => {
|
||||
for (let i = 0; i < funcs.length; i++) {
|
||||
for (let i = 0; i < funcs.length; i += 1) {
|
||||
const comp = funcs[i]
|
||||
const desc = dirs[i] === false || dirs[i] === 'desc'
|
||||
const sortInt = comp(rowA, rowB)
|
||||
@ -83,8 +85,8 @@ function orderBy (arr, funcs, dirs, indexKey) {
|
||||
}
|
||||
|
||||
function remove (a, b) {
|
||||
return a.filter(function (o, i) {
|
||||
var r = b(o)
|
||||
return a.filter((o, i) => {
|
||||
const r = b(o)
|
||||
if (r) {
|
||||
a.splice(i, 1)
|
||||
return true
|
||||
@ -101,7 +103,7 @@ function clone (a) {
|
||||
return value.toString()
|
||||
}
|
||||
return value
|
||||
})
|
||||
}),
|
||||
)
|
||||
} catch (e) {
|
||||
return a
|
||||
@ -109,7 +111,7 @@ function clone (a) {
|
||||
}
|
||||
|
||||
function getFirstDefined (...args) {
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
if (typeof args[i] !== 'undefined') {
|
||||
return args[i]
|
||||
}
|
||||
@ -117,9 +119,9 @@ function getFirstDefined (...args) {
|
||||
}
|
||||
|
||||
function sum (arr) {
|
||||
return arr.reduce((a, b) => {
|
||||
return a + b
|
||||
}, 0)
|
||||
return arr.reduce((a, b) => (
|
||||
a + b
|
||||
), 0)
|
||||
}
|
||||
|
||||
function makeTemplateComponent (compClass, displayName) {
|
||||
@ -146,7 +148,7 @@ function groupBy (xs, key) {
|
||||
|
||||
function asPx (value) {
|
||||
value = Number(value)
|
||||
return Number.isNaN(value) ? null : value + 'px'
|
||||
return Number.isNaN(value) ? null : `${value}px`
|
||||
}
|
||||
|
||||
function isArray (a) {
|
||||
@ -169,7 +171,7 @@ function flattenDeep (arr, newArr = []) {
|
||||
if (!isArray(arr)) {
|
||||
newArr.push(arr)
|
||||
} else {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
for (let i = 0; i < arr.length; i += 1) {
|
||||
flattenDeep(arr[i], newArr)
|
||||
}
|
||||
}
|
||||
@ -186,15 +188,16 @@ function splitProps ({ className, style, ...rest }) {
|
||||
|
||||
function compactObject (obj) {
|
||||
const newObj = {}
|
||||
for (var key in obj) {
|
||||
Object.keys(obj).map(key => {
|
||||
if (
|
||||
obj.hasOwnProperty(key) &&
|
||||
Object.prototype.hasOwnProperty.call(obj, key) &&
|
||||
obj[key] !== undefined &&
|
||||
typeof obj[key] !== 'undefined'
|
||||
) {
|
||||
newObj[key] = obj[key]
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return newObj
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user