Compare commits

..
Author SHA1 Message Date
Mike DeVita 49eb999821 fixes issue #564 - IE not displaying rows 2017-10-23 11:12:45 -07:00
56 changed files with 3516 additions and 21615 deletions
+75 -1
View File
@@ -1,3 +1,77 @@
module.exports = { module.exports = {
extends: 'react-tools', parserOptions: {
ecmaVersion: 8,
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true,
node: false,
classes: true
},
sourceType: 'module'
},
parser: 'babel-eslint',
extends: ['standard'],
plugins: ['react'],
rules: {
// Nozzle
'jsx-quotes': [2, 'prefer-single'],
'comma-dangle': [2, 'always-multiline'],
// // React
'react/jsx-boolean-value': 2,
'react/jsx-curly-spacing': [2, 'never'],
'react/jsx-equals-spacing': [2, 'never'],
// 'react/jsx-indent': 2,
'react/jsx-indent-props': [2, 2],
'react/jsx-no-duplicate-props': 2,
'react/jsx-no-undef': 2,
'react/jsx-tag-spacing': [
2,
{
closingSlash: 'never',
beforeSelfClosing: 'always',
afterOpening: 'never'
}
],
'react/jsx-uses-react': 2,
'react/jsx-uses-vars': 2,
'react/self-closing-comp': 2,
'react/jsx-no-bind': [
2,
{
allowArrowFunctions: true,
allowBind: false,
ignoreRefs: true
}
],
'react/no-did-update-set-state': 2,
'react/no-unknown-property': 2,
'react/react-in-jsx-scope': 2,
'react/jsx-closing-bracket-location': [0, 'tag-aligned'],
'react/jsx-tag-spacing': [2, { beforeSelfClosing: 'always' }],
'react/jsx-wrap-multilines': 2,
'react/self-closing-comp': 2,
'react/jsx-key': 2,
'react/jsx-no-comment-textnodes': 2,
'react/jsx-no-duplicate-props': 2,
'react/jsx-no-target-blank': 2,
'react/jsx-no-undef': 2,
'react/jsx-uses-react': 2,
'react/jsx-uses-vars': 2,
'react/no-danger-with-children': 2,
'react/no-deprecated': 2,
'react/no-direct-mutation-state': 2,
'react/no-find-dom-node': 2,
'react/no-is-mounted': 2,
'react/no-render-return-value': 2,
'react/no-string-refs': 2,
'react/no-unknown-property': 2,
'react/react-in-jsx-scope': 2,
'react/require-render-return': 2
// 'react/jsx-max-props-per-line': [2, { maximum: 1 }]
}
} }
-1
View File
@@ -1 +0,0 @@
* text=auto
-38
View File
@@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Codesandbox!**
Use a new [react-table codesandbox](codesandbox.io/s/X6npLXPRW) to reproduce the issue.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
-17
View File
@@ -1,17 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
@@ -1,11 +0,0 @@
---
name: Questions, Support & Help
about: Looking for help? Need a question answered?
---
STOP!!!
Github issues is no place for general support. We encourage you to use our public support forum at https://spectrum.chat/react-table.
NOTE: If a support/help/question issue is opened, it will be closed immediately and be redirected to the forum. Thanks for helping us keep our issues clean and productive!
-3
View File
@@ -1,11 +1,8 @@
node_modules/ node_modules/
lib/ lib/
es/
docs/build docs/build
react-table.js react-table.js
react-table.min.js
react-table.css react-table.css
*.log *.log
.idea .idea
.DS_Store .DS_Store
.history
+42 -158
View File
@@ -1,173 +1,57 @@
## 6.8.6
#### Fixes & Optimizations
* Since `resolveData` is now capable of materializing data on it's own, the `data` prop is no longer required as a prop-type.
## 6.8.4
#### Fixes & Optimizations
* Only run `resolveData` prop when `data` prop has changed, not any others.
## 6.8.3
#### Fixes & Optimizations
* Allow the `resolveData` prop to alter or materialize new data when the `data` prop changes.
## 6.8.1
#### Fixes & Optimizations
* Updated eslint and code formatting
## 6.7.5
#### Fixes & Optimizations
* Now passes `column` to `getResizerProps` (#667)
* NOTE: `getResizerProps` is now only called if the column is resizable
* Fixes the `className` ordering in defaultProps for ThComponent (#673)
* NOTE: user supplied classNames now come at the end so they can extend the defaults
## 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 ## 6.5.0
##### New Features ##### 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.
* `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.
## 6.4.0 ## 6.4.0
##### New Features ##### New Features
- `PadRowComponent` - the content rendered inside of a padding row. Defaults to a react component that renders ` `
* `PadRowComponent` - the content rendered inside of a padding row. Defaults to a react component that renders ` `
## 6.3.0 ## 6.3.0
##### New Features ##### New Features
- `defaultSortDesc` - allows you to set the default sorting direction for all columns to descending.
* `defaultSortDesc` - allows you to set the default sorting direction for all columns to descending. - `column.defaultSortDesc` - allows you to set the default sorting direction for a specific column. Falls back to the global `defaultSortDesc` when not set at all.
* `column.defaultSortDesc` - allows you to set the default sorting direction for a specific column. Falls back to the global `defaultSortDesc` when not set at all.
## 6.0.0 ## 6.0.0
##### New Features ##### New Features
- New Renderers:
- `Aggregated` - Custom renderer for aggregated cells
- `Pivot` - Custom renderer for Pivoted Cells (utilizes `Expander` and `PivotValue`)
- `PivotValue` - Custom renderer for Pivot cell values (deprecates the undocumented `pivotRender` option)
- `Expander` - Custom renderer for Pivot cell Expander
- Added custom sorting methods per table via `defaultSortMethod` and per column via `column.sortMethod`
- Pivot columns are now visibly separate and sorted/filtered independently.
- Added `column.resizable` to override global table `resizable` option for specific columns.
- Added `column.sortable` to override global table `sortable` option for specific columns.
- Added `column.filterable` to override global table `filterable` option for specific columns.
- Added `defaultExpanded` table option.
- All callbacks can now be utilized without needing to hoist and manage the piece of state they export. That is what their prop counterparts are for, so now the corresponding prop is used instead of the callback to detect a "fully controlled" state.
- Prevent transitions while column resizing for a smoother resize effect.
- Disable text selection while resizing columns.
* New Renderers:
* `Aggregated` - Custom renderer for aggregated cells
* `Pivot` - Custom renderer for Pivoted Cells (utilizes `Expander` and `PivotValue`)
* `PivotValue` - Custom renderer for Pivot cell values (deprecates the undocumented `pivotRender` option)
* `Expander` - Custom renderer for Pivot cell Expander
* Added custom sorting methods per table via `defaultSortMethod` and per column via `column.sortMethod`
* Pivot columns are now visibly separate and sorted/filtered independently.
* Added `column.resizable` to override global table `resizable` option for specific columns.
* Added `column.sortable` to override global table `sortable` option for specific columns.
* Added `column.filterable` to override global table `filterable` option for specific columns.
* Added `defaultExpanded` table option.
* All callbacks can now be utilized without needing to hoist and manage the piece of state they export. That is what their prop counterparts are for, so now the corresponding prop is used instead of the callback to detect a "fully controlled" state.
* Prevent transitions while column resizing for a smoother resize effect.
* Disable text selection while resizing columns.
##### Breaking API Changes ##### Breaking API Changes
- New Renderers:
* New Renderers: - `Cell` - deprecates and replaces `render`
* `Cell` - deprecates and replaces `render` - `Header` - deprecates and replaces `header`
* `Header` - deprecates and replaces `header` - `Footer` - deprecates and replaces `footer`
* `Footer` - deprecates and replaces `footer` - `Filter`- deprecates and replaces `filterRender`
* `Filter`- deprecates and replaces `filterRender` - Callbacks now provide the destination state as the primary parameter(s). This makes hoisting and controlling the state in redux or component state much easier. eg.
* Callbacks now provide the destination state as the primary parameter(s). This makes hoisting and controlling the state in redux or component state much easier. eg. - `onSorting` no longer requires you to build your own toggle logic
* `onSorting` no longer requires you to build your own toggle logic - `onResize` no longer requires you to build your own resize logic
* `onResize` no longer requires you to build your own resize logic - Renamed `onChange` callback -> `onFetchData` which will always fire when a new data model needs to be fetched (or if not using `manual`, when new data is materialized internally).
* Renamed `onChange` callback -> `onFetchData` which will always fire when a new data model needs to be fetched (or if not using `manual`, when new data is materialized internally). - Renamed `filtering` -> `filtered`
* Renamed `filtering` -> `filtered` - Renamed `sorting` -> `sorted`
* Renamed `sorting` -> `sorted` - Renamed `expandedRows` -> `expanded`
* Renamed `expandedRows` -> `expanded` - Renamed `resizing` -> `resized`
* Renamed `resizing` -> `resized` - Renamed `defaultResizing` -> `defaultResized`
* Renamed `defaultResizing` -> `defaultResized` - Renamed `defaultFiltering` -> `defaultFiltered`
* Renamed `defaultFiltering` -> `defaultFiltered` - Renamed `defaultSorting` -> `defaultSorted`
* Renamed `defaultSorting` -> `defaultSorted` - Renamed `onSortingChange` -> `onSortedChange`
* Renamed `onSortingChange` -> `onSortedChange` - Renamed `onFilteringChange` -> `onFilteredChange`
* Renamed `onFilteringChange` -> `onFilteredChange` - Renamed `onResize` -> `onResizedChange`
* Renamed `onResize` -> `onResizedChange` - Renamed `onExpandRow` -> `onExpandedChange`
* Renamed `onExpandRow` -> `onExpandedChange` - Renamed `showFilters` -> `filterable`
* Renamed `showFilters` -> `filterable` - Renamed `hideFilter` -> `filterable` (Column option. Note the true/false value is now flipped.)
* Renamed `hideFilter` -> `filterable` (Column option. Note the true/false value is now flipped.) - `cellInfo.row` and `rowInfo.row` now reference the materialize data for the table. To reference the original row, use `cellInfo.original` and `rowInfo.original`
* `cellInfo.row` and `rowInfo.row` now reference the materialize data for the table. To reference the original row, use `cellInfo.original` and `rowInfo.original` - Removed `pivotRender` column option. You can now control how the value is displayed by overriding the `PivotValueComponent` or the individual column's `PivotValue` renderer. See [Pivoting Options Story](https://react-table.js.org/?selectedKind=2.%20Demos&selectedStory=Pivoting%20Options&full=0&down=1&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel) for a reference on how to customize pivot column rendering.
* Removed `pivotRender` column option. You can now control how the value is displayed by overriding the `PivotValueComponent` or the individual column's `PivotValue` renderer. See [Pivoting Options Story](https://react-table.js.org/?selectedKind=2.%20Demos&selectedStory=Pivoting%20Options&full=0&down=1&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel) for a reference on how to customize pivot column rendering.
+14
View File
@@ -0,0 +1,14 @@
## What version of React-Table are you using?
Your bug may already be fixed in the latest release. Run `yarn upgrade react-table`!
Place your version here...
## What bug are you experiencing, or what feature are you proposing?
Please include a detailed explanation here...
## Use https://codesandbox.io/s/X6npLXPRW (by clicking the "Fork" button) to reproduce the issue.
Then paste a link to your newly forked codesandbox here...
## What are the steps to reproduce the issue?
1. list the steps
2. to reproduce
3. the issue
+139 -339
View File
@@ -3,7 +3,6 @@
</div> </div>
# React Table # React Table
`react-table` is a **lightweight, fast and extendable datagrid** built for React `react-table` is a **lightweight, fast and extendable datagrid** built for React
<a href="https://travis-ci.org/react-tools/react-table" target="\_parent"> <a href="https://travis-ci.org/react-tools/react-table" target="\_parent">
@@ -12,8 +11,8 @@
<a href="https://npmjs.com/package/react-table" target="\_parent"> <a href="https://npmjs.com/package/react-table" target="\_parent">
<img alt="" src="https://img.shields.io/npm/dm/react-table.svg" /> <img alt="" src="https://img.shields.io/npm/dm/react-table.svg" />
</a> </a>
<a href="https://spectrum.chat/react-table"> <a href="https://react-chat-signup.herokuapp.com/" target="\_parent">
<img alt="Join the community on Spectrum" src="https://withspectrum.github.io/badge/badge.svg" /> <img alt="" src="https://img.shields.io/badge/slack-react--chat-blue.svg" />
</a> </a>
<a href="https://github.com/react-tools/react-table" target="\_parent"> <a href="https://github.com/react-tools/react-table" target="\_parent">
<img alt="" src="https://img.shields.io/github/stars/react-tools/react-table.svg?style=social&label=Star" /> <img alt="" src="https://img.shields.io/github/stars/react-tools/react-table.svg?style=social&label=Star" />
@@ -28,8 +27,9 @@
<br /> <br />
<br /> <br />
## Features [![Sponsor](https://app.codesponsor.io/embed/zpmS8V9r31sBSCeVzP7Wm6Sr/react-tools/react-table.svg)](https://app.codesponsor.io/link/zpmS8V9r31sBSCeVzP7Wm6Sr/react-tools/react-table)
## Features
- Lightweight at 11kb (and just 2kb more for styles) - Lightweight at 11kb (and just 2kb more for styles)
- Fully customizable (JSX, templates, state, styles, callbacks) - Fully customizable (JSX, templates, state, styles, callbacks)
- Client-side & Server-side pagination - Client-side & Server-side pagination
@@ -38,94 +38,17 @@
- Pivoting & Aggregation - Pivoting & Aggregation
- Minimal design & easily themeable - Minimal design & easily themeable
- Fully controllable via optional props and callbacks - Fully controllable via optional props and callbacks
- <a href="https://medium.com/@tannerlinsley/why-i-wrote-react-table-and-the-problems-it-has-solved-for-nozzle-others-445c4e93d4a8#.axza4ixba" target="\_parent">"Why I wrote React Table and the problems it has solved for Nozzle.io"</a> by Tanner Linsley - <a href="https://medium.com/@tannerlinsley/why-i-wrote-react-table-and-the-problems-it-has-solved-for-nozzle-others-445c4e93d4a8#.axza4ixba" target="\_parent">"Why I wrote React Table and the problems it has solved for Nozzle.io</a> by Tanner Linsley
## [Demos and examples](http://react-table.js.org/#/story/simple-table) ## [Demos and examples](http://react-table.js.org/#/story/simple-table)
## Versions ## Versions
- This documentation is for version 6 of react-table. - This documentation is for version 6 of react-table.
- [View the Changelog](https://github.com/react-tools/react-table/blob/master/CHANGELOG.md) - [View the Changelog](https://github.com/react-tools/react-table/blob/master/CHANGELOG.md)
- Previous versions: - Previous versions:
- [5.x.x Readme](https://github.com/react-tools/react-table/blob/ad7d31cd3978eb45da7c6194dbab93c1e9a8594d/README.md) - [5.x.x Readme](https://github.com/react-tools/react-table/blob/ad7d31cd3978eb45da7c6194dbab93c1e9a8594d/README.md)
## Sponsors
**React Table v7** is mostly planned and I (@tannerlinsley) am looking for Patreon support to make it a reality. It will require a decent time commitment on my part to not only implement it, but also help people migrate and continue to maintain it. If you would like to contribute to my Patreon goal for v7 and beyond, [visit my Patreon and help me out!](https://patreon.com/tannerlinsley).
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://patreon.com/tannerlinsley" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/platinum.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/sponsor-retool.png">
</a>
</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://patreon.com/tannerlinsley" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/gold.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/gold-placeholder.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/gold-placeholder.png">
</a>
</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://patreon.com/tannerlinsley" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver-placeholder.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver-placeholder.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://tryretool.com/?utm_source=sponsor&utm_campaign=react_table" target="_blank">
<img src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver-placeholder.png">
</a>
</td>
</tr>
</tbody>
</table>
## Issues, Questions and Support
Github issues are temporarily disabled until v7 is out. Most if not all issues that were or will be opened are likely to be resolved or uneccessary after v7 is released (mostly due to the headless approach of the new component). If you have an issue or a question, feel free to use the [React Table Spectrum Community/Forum](https://spectrum.chat/react-table)!
## Table of Contents ## Table of Contents
- [Installation](#installation) - [Installation](#installation)
- [Example](#example) - [Example](#example)
- [Data](#data) - [Data](#data)
@@ -147,56 +70,41 @@ Github issues are temporarily disabled until v7 is out. Most if not all issues t
- [Scripts](#scripts) - [Scripts](#scripts)
- [Used By](#used-by) - [Used By](#used-by)
## Installation ## Installation
1. Install React Table as a dependency
1. Install React Table as a dependency
```bash ```bash
# Yarn
$ yarn add react-table $ yarn add react-table
# NPM
$ npm install react-table
``` ```
2. Import the `react-table` module
2. Import the `react-table` module
```javascript ```javascript
// ES6 // ES6
import ReactTable from "react-table"; import ReactTable from 'react-table'
// ES5 // ES5
var ReactTable = require("react-table").default; var ReactTable = require('react-table').default
``` ```
3. Import styles by including `react-table.css`
3. Import styles by including `react-table.css`
```javascript ```javascript
// JS (Webpack) // JS (Webpack)
import 'react-table/react-table.css' import 'react-table/react-table.css'
// Old-school // Old-school
<link rel="stylesheet" href="node_modules/react-table/react-table.css"> <link rel="stylesheet" href="node_modules/react-table/react-table.css">
``` ```
##### CDN ##### CDN
```html ```html
<!-- CSS --> <!-- CSS -->
<link <link rel="stylesheet" href="https://unpkg.com/react-table@latest/react-table.css">
rel="stylesheet"
href="https://unpkg.com/react-table@latest/react-table.css"
/>
<!-- JS --> <!-- JS -->
<script src="https://unpkg.com/react-table@latest/react-table.js"></script> <script src="https://unpkg.com/react-table@latest/react-table.js"></script>
<script src="https://unpkg.com/react-table@latest/react-table.min.js"></script>
<script> <script>
var ReactTable = window.ReactTable.default; var ReactTable = window.ReactTable.default
</script> </script>
``` ```
## Example
## Example
```javascript ```javascript
import ReactTable from 'react-table' import ReactTable from 'react-table'
@@ -228,7 +136,7 @@ render() {
accessor: 'friend.age' accessor: 'friend.age'
}] }]
return <ReactTable <ReactTable
data={data} data={data}
columns={columns} columns={columns}
/> />
@@ -236,33 +144,15 @@ render() {
``` ```
## Data ## Data
Simply pass the `data` prop anything that resembles an array or object. Client-side sorting and pagination are built in, and your table will update gracefully as you change any props. [Server-side data](#server-side-data) is also supported! Simply pass the `data` prop anything that resembles an array or object. Client-side sorting and pagination are built in, and your table will update gracefully as you change any props. [Server-side data](#server-side-data) is also supported!
```javascript
<ReactTable
data={[...]}
/>
```
**Pro Tip: Using the `resolveData` prop** - Any time the `data` prop value changes (using a `===` comparison), the table will update, but sometimes you need to materialize, alter, or shape this data before it enters the table. To do this, you can use the `resolveData` prop! It recieves the `data` prop as the only parameter and returns the resolved data.
```javascript
<ReactTable
data={myData} // The data prop should be immutable and only change when you want to update the table
resolveData={data => data.map(row => row)} // But you can break immutability here because `resolveData` runs when the `data` prop changes!
/>
```
## Props ## Props
These are all of the available props (and their default values) for the main `<ReactTable />` component. These are all of the available props (and their default values) for the main `<ReactTable />` component.
```javascript ```javascript
{ {
// General // General
data: [], data: [],
resolveData: data => resolvedData,
loading: false, loading: false,
showPagination: true, showPagination: true,
showPaginationTop: false, showPaginationTop: false,
@@ -270,15 +160,13 @@ These are all of the available props (and their default values) for the main `<R
showPageSizeOptions: true, showPageSizeOptions: true,
pageSizeOptions: [5, 10, 20, 25, 50, 100], pageSizeOptions: [5, 10, 20, 25, 50, 100],
defaultPageSize: 20, defaultPageSize: 20,
minRows: undefined, // controls the minimum number of rows to display - default will be `pageSize` minRows: undefined,
// NOTE: if you set minRows to 0 then you get rid of empty padding rows BUT your table formatting will also look strange when there are ZERO rows in the table
showPageJump: true, showPageJump: true,
collapseOnSortingChange: true, collapseOnSortingChange: true,
collapseOnPageChange: true, collapseOnPageChange: true,
collapseOnDataChange: true, collapseOnDataChange: true,
freezeWhenExpanded: false, freezeWhenExpanded: false,
sortable: true, sortable: true,
multiSort: true,
resizable: true, resizable: true,
filterable: false, filterable: false,
defaultSortDesc: false, defaultSortDesc: false,
@@ -290,13 +178,13 @@ These are all of the available props (and their default values) for the main `<R
const id = filter.pivotId || filter.id const id = filter.pivotId || filter.id
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
}, },
defaultSortMethod: (a, b, desc) => { defaultSortMethod: (a, b) => {
// force null and undefined to the bottom // force null and undefined to the bottom
a = a === null || a === undefined ? '' : a a = (a === null || a === undefined) ? -Infinity : a
b = b === null || b === undefined ? '' : b b = (b === null || b === undefined) ? -Infinity : b
// force any string values to lowercase // force any string values to lowercase
a = typeof a === 'string' ? a.toLowerCase() : a a = a === 'string' ? a.toLowerCase() : a
b = typeof b === 'string' ? b.toLowerCase() : b b = b === 'string' ? b.toLowerCase() : b
// Return either 1 or -1 to indicate a sort priority // Return either 1 or -1 to indicate a sort priority
if (a > b) { if (a > b) {
return 1 return 1
@@ -304,8 +192,7 @@ These are all of the available props (and their default values) for the main `<R
if (a < b) { if (a < b) {
return -1 return -1
} }
// returning 0, undefined or any falsey value will use subsequent sorts or // returning 0, undefined or any falsey value will use subsequent sorts or the index as a tiebreaker
// the index as a tiebreaker
return 0 return 0
}, },
PadRowComponent: () => <span>&nbsp;</span>, // the content rendered inside of a padding row PadRowComponent: () => <span>&nbsp;</span>, // the content rendered inside of a padding row
@@ -371,21 +258,7 @@ These are all of the available props (and their default values) for the main `<R
getNoDataProps: () => ({}), getNoDataProps: () => ({}),
getResizerProps: () => ({}), getResizerProps: () => ({}),
// Custom pagination rendering
renderPageJump: ({ onChange, value, onBlur, onKeyPress, inputType, pageJumpText }) => component,
renderCurrentPage: page => component,
renderTotalPagesCount: pages => component,
renderPageSizeOptions: ({
pageSize,
pageSizeOptions,
rowsSelectorText,
onPageSizeChange,
rowsText,
}) => component
// Global Column Defaults // Global Column Defaults
// To override only some values, import { ReactTableDefaults } from 'react-table'
// and construct your overrides (e.g. {...ReactTableDefaults.column, className: 'react-table-cell'})
column: { column: {
// Renderers // Renderers
Cell: undefined, Cell: undefined,
@@ -421,8 +294,6 @@ These are all of the available props (and their default values) for the main `<R
}, },
// Global Expander Column Defaults // Global Expander Column Defaults
// To override only some values, import { ReactTableDefaults } from 'react-table'
// and construct your overrides (e.g. {...ReactTableDefaults.expanderDefaults, sortable: true})
expanderDefaults: { expanderDefaults: {
sortable: false, sortable: false,
resizable: false, resizable: false,
@@ -441,23 +312,19 @@ These are all of the available props (and their default values) for the main `<R
pageText: 'Page', pageText: 'Page',
ofText: 'of', ofText: 'of',
rowsText: 'rows', rowsText: 'rows',
// Accessibility Labels
pageJumpText: 'jump to page',
rowsSelectorText: 'rows per page',
} }
``` ```
You can easily override the core defaults like so: You can easily override the core defaults like so:
```javascript ```javascript
import { ReactTableDefaults } from "react-table"; import { ReactTableDefaults } from 'react-table'
Object.assign(ReactTableDefaults, { Object.assign(ReactTableDefaults, {
defaultPageSize: 10, defaultPageSize: 10,
minRows: 3 minRows: 3,
// etc... // etc...
}); })
``` ```
Or just define them as props Or just define them as props
@@ -471,8 +338,7 @@ Or just define them as props
``` ```
## Columns ## Columns
`<ReactTable/>` requires a `columns` prop, which is an array of objects containing the following properties
`<ReactTable />` requires a `columns` prop, which is an array of objects containing the following properties
```javascript ```javascript
[{ [{
@@ -487,7 +353,7 @@ Or just define them as props
Aggregated: JSX | String | Function // Used to render aggregated cells. Defaults to a comma separated list of values. Aggregated: JSX | String | Function // Used to render aggregated cells. Defaults to a comma separated list of values.
Pivot: JSX | String | Function | cellInfo => ( // Used to render a pivoted cell Pivot: JSX | String | Function | cellInfo => ( // Used to render a pivoted cell
<span> <span>
<Expander /><PivotValue /> // By default, will utilize the the PivotValue and Expander components at run time <Expander/><PivotValue /> // By default, will utilize the the PivotValue and Expander components at run time
</span> </span>
), ),
PivotValue: JSX | String | Function // Used to render the value inside of a Pivot cell PivotValue: JSX | String | Function // Used to render the value inside of a Pivot cell
@@ -541,9 +407,7 @@ Or just define them as props
``` ```
## Renderers ## Renderers
React Table supports very flexible renderers for just about everything: React Table supports very flexible renderers for just about everything:
- `Cell` - Renders a standard cell - `Cell` - Renders a standard cell
- `Header` - Renders a column header or column group header - `Header` - Renders a column header or column group header
- `Footer` - Renders a column footer - `Footer` - Renders a column footer
@@ -554,20 +418,18 @@ React Table supports very flexible renderers for just about everything:
- `Expander` - Renders the Expander used in both the default `Pivot` renderer and any expander-designated column - `Expander` - Renders the Expander used in both the default `Pivot` renderer and any expander-designated column
Any of these renderers can be one of the following: Any of these renderers can be one of the following:
- A React Class - A React Class
- JSX or any rendered react component - JSX or any rendered react component
- Stateless functional component - Stateless functional component
- Function that returns any primitive - Function that returns any primitive
All of these formats receive the following props: All of these formats receive the following props:
```javascript ```javascript
{ {
// Row-level props // Row-level props
row: Object, // the materialized row of data row: Object, // the materialized row of data
original: , // the original row of data original: , // the original row of data
index: '', // the index of the row in the original array index: '', // the index of the row in the original arra
viewIndex: '', // the index of the row relative to the current view viewIndex: '', // the index of the row relative to the current view
level: '', // the nesting level of this row level: '', // the nesting level of this row
nestingPath: '', // the nesting path of this row nestingPath: '', // the nesting path of this row
@@ -590,7 +452,6 @@ All of these formats receive the following props:
``` ```
## Accessors ## Accessors
Accessors are functions that return the value to populate the row's value for the column. Accessors are functions that return the value to populate the row's value for the column.
This lets the render function not have to worry about accessing the correct data, the value is automatically populated in it's props. This lets the render function not have to worry about accessing the correct data, the value is automatically populated in it's props.
@@ -600,22 +461,21 @@ Any values that contain a dot (`.`) will be split.
Any values that contain bracket (`[]`) will be split. Any values that contain bracket (`[]`) will be split.
This array is then used as the path to the value to return. This array is then used as the path to the value to return.
("\$" is the placeholder value that would be returned by the default accessor) ("$" is the placeholder value that would be returned by the default accessor)
| value | path | data | | value | path | data |
| ------------ | --------------- | ----------------------- | |--------------|-----------------|------------------------|
| "a" | ["a"] | {"a": \$} | | "a" | ["a"] | {"a": $} |
| "a.b" | ["a", "b"] | {"a": {"b": \$}} | | "a.b" | ["a", "b"] | {"a": {"b": $}} |
| "a[0]" | ["a", "0"] | {"a": [$]} | | "a[0]" | ["a", "0"] | {"a": [$]} |
| ["a.b", "c"] | ["a", "b", "c"] | {"a": {"b": {"c": \$}}} | | ["a.b", "c"] | ["a", "b", "c"] | {"a": {"b": {"c": $}}} |
_NOTE_ *NOTE*
If your data has a field/key with a dot (`.`) you will need to supply a custom accessor. If your data has a field/key with a dot (`.`) you will need to supply a custom accessor.
## Column Header Groups ## Column Header Groups
To group columns with another header column, just nest your columns in a header column. Header columns utilize the same header properties as regular columns.
To group columns with another header column, just nest your columns in a header column. Header columns utilize the same header properties as regular columns.
```javascript ```javascript
const columns = [{ const columns = [{
Header: 'Favorites', Header: 'Favorites',
@@ -626,7 +486,7 @@ const columns = [{
}, { }, {
Header: 'Food', Header: 'Food',
accessor: 'favorites.food' accessor: 'favorites.food'
}, { } {
Header: 'Actor', Header: 'Actor',
accessor: 'favorites.actor' accessor: 'favorites.actor'
}] }]
@@ -634,11 +494,9 @@ const columns = [{
``` ```
## Custom Cell, Header and Footer Rendering ## Custom Cell, Header and Footer Rendering
You can use any react component or JSX to display content in column headers, cells and footers. Any component you use will be passed the following props (if available): You can use any react component or JSX to display content in column headers, cells and footers. Any component you use will be passed the following props (if available):
- `row` - Original row from your data - `row` - Original row from your data
- `original` - The post-accessed values from the original row - `row` - The post-accessed values from the original row
- `index` - The index of the row - `index` - The index of the row
- `viewIndex` - the index of the row relative to the current page - `viewIndex` - the index of the row relative to the current page
- `level` - The nesting depth (zero-indexed) - `level` - The nesting depth (zero-indexed)
@@ -649,68 +507,53 @@ You can use any react component or JSX to display content in column headers, cel
```javascript ```javascript
// This column uses a stateless component to produce a different colored bar depending on the value // This column uses a stateless component to produce a different colored bar depending on the value
// You can also use stateful components or any other function that returns JSX // You can also use stateful components or any other function that returns JSX
const columns = [ const columns = [{
{ Header: () => <span><i className='fa-tasks' /> Progress</span>,
Header: () => ( accessor: 'progress',
<span> Cell: row => (
<i className="fa-tasks" /> Progress <div
</span> style={{
), width: '100%',
accessor: "progress", height: '100%',
Cell: row => ( backgroundColor: '#dadada',
borderRadius: '2px'
}}
>
<div <div
style={{ style={{
width: "100%", width: `${row.value}%`,
height: "100%", height: '100%',
backgroundColor: "#dadada", backgroundColor: row.value > 66 ? '#85cc00'
borderRadius: "2px" : row.value > 33 ? '#ffbf00'
: '#ff2e00',
borderRadius: '2px',
transition: 'all .2s ease-out'
}} }}
> />
<div </div>
style={{ )
width: `${row.value}%`, }]
height: "100%",
backgroundColor:
row.value > 66
? "#85cc00"
: row.value > 33
? "#ffbf00"
: "#ff2e00",
borderRadius: "2px",
transition: "all .2s ease-out"
}}
/>
</div>
)
}
];
``` ```
## Styles ## Styles
- React-table ships with a minimal and clean stylesheet to get you on your feet quickly. - React-table ships with a minimal and clean stylesheet to get you on your feet quickly.
- The stylesheet is located at `react-table/react-table.css`. - The stylesheet is located at `react-table/react-table.css`.
- There are countless ways to import a stylesheet. If you have questions on how to do so, consult the documentation of your build system. - There are countless ways to import a stylesheet. If you have questions on how to do so, consult the documentation of your build system.
#### Classes #### Classes
- Adding a `-striped` className to ReactTable will slightly color odd numbered rows for legibility - Adding a `-striped` className to ReactTable will slightly color odd numbered rows for legibility
- Adding a `-highlight` className to ReactTable will highlight any row as you hover over it - Adding a `-highlight` className to ReactTable will highlight any row as you hover over it
#### CSS #### CSS
We think the default styles looks great! But, if you prefer a more custom look, all of the included styles are easily overridable. Every single component contains a unique class that makes it super easy to customize. Just go for it! We think the default styles looks great! But, if you prefer a more custom look, all of the included styles are easily overridable. Every single component contains a unique class that makes it super easy to customize. Just go for it!
#### JS Styles #### JS Styles
Every single react-table element and `get[ComponentName]Props` callback supports `classname` and `style` props. Every single react-table element and `get[ComponentName]Props` callback supports `classname` and `style` props.
## Custom Props ## Custom Props
#### Built-in Components #### Built-in Components
Every single built-in component's props can be dynamically extended using any one of these prop-callbacks: Every single built-in component's props can be dynamically extended using any one of these prop-callbacks:
```javascript ```javascript
<ReactTable <ReactTable
getProps={fn} getProps={fn}
@@ -721,9 +564,6 @@ Every single built-in component's props can be dynamically extended using any on
getTheadProps={fn} getTheadProps={fn}
getTheadTrProps={fn} getTheadTrProps={fn}
getTheadThProps={fn} getTheadThProps={fn}
getTheadFilterProps={fn}
getTheadFilterTrProps={fn}
getTheadFilterThProps={fn}
getTbodyProps={fn} getTbodyProps={fn}
getTrGroupProps={fn} getTrGroupProps={fn}
getTrProps={fn} getTrProps={fn}
@@ -739,25 +579,23 @@ Every single built-in component's props can be dynamically extended using any on
If used, **a callback prop must return an valid object**, even if it's an empty one. If used, **a callback prop must return an valid object**, even if it's an empty one.
These callbacks are executed with each render of the element with four parameters: These callbacks are executed with each render of the element with four parameters:
1. Table State
1. Table State 2. RowInfo (undefined if not applicable)
2. RowInfo (undefined if not applicable) 3. Column (undefined if not applicable)
3. Column (undefined if not applicable) 4. React Table Instance
4. React Table Instance
This makes it extremely easy to add, say... a row click callback! This makes it extremely easy to add, say... a row click callback!
```javascript ```javascript
// When any Td element is clicked, we'll log out some information // When any Td element is clicked, we'll log out some information
<ReactTable <ReactTable
getTdProps={(state, rowInfo, column, instance) => { getTdProps={(state, rowInfo, column, instance) => {
return { return {
onClick: (e, handleOriginal) => { onClick: (e, handleOriginal) => {
console.log("A Td Element was clicked!"); console.log('A Td Element was clicked!')
console.log("it produced this event:", e); console.log('it produced this event:', e)
console.log("It was in this column:", column); console.log('It was in this column:', column)
console.log("It was in this row:", rowInfo); console.log('It was in this row:', rowInfo)
console.log("It was in this table instance:", instance); console.log('It was in this table instance:', instance)
// IMPORTANT! React-Table uses onClick internally to trigger // IMPORTANT! React-Table uses onClick internally to trigger
// events like expanding SubComponents and pivots. // events like expanding SubComponents and pivots.
@@ -765,35 +603,32 @@ This makes it extremely easy to add, say... a row click callback!
// If you want to fire the original onClick handler, call the // If you want to fire the original onClick handler, call the
// 'handleOriginal' function. // 'handleOriginal' function.
if (handleOriginal) { if (handleOriginal) {
handleOriginal(); handleOriginal()
} }
} }
}; }
}} }}
/> />
``` ```
You can use these callbacks for dynamic styling as well! You can use these callbacks for dynamic styling as well!
```javascript ```javascript
// Any Tr element will be green if its (row.age > 20) // Any Tr element will be green if its (row.age > 20)
<ReactTable <ReactTable
getTrProps={(state, rowInfo, column) => { getTrProps={(state, rowInfo, column) => {
return { return {
style: { style: {
background: rowInfo.row.age > 20 ? "green" : "red" background: rowInfo.row.age > 20 ? 'green' : 'red'
} }
}; }
}} }}
/> />
``` ```
#### Column Components #### Column Components
Just as core components can have dynamic props, columns and column headers can too! Just as core components can have dynamic props, columns and column headers can too!
You can utilize either of these prop callbacks on columns: You can utilize either of these prop callbacks on columns:
```javascript ```javascript
const columns = [{ const columns = [{
getHeaderProps: () => (...), getHeaderProps: () => (...),
@@ -802,27 +637,22 @@ const columns = [{
``` ```
In a similar fashion these can be used to dynamically style just about anything! In a similar fashion these can be used to dynamically style just about anything!
```javascript ```javascript
// This columns cells will be red if (row.name === Santa Clause) // This columns cells will be red if (row.name === Santa Clause)
const columns = [ const columns = [{
{ getProps: (state, rowInfo, column) => {
getProps: (state, rowInfo, column) => { return {
return { style: {
style: { background: rowInfo.row.name === 'Santa Clause' ? 'red' : null
background: rowInfo.row.name === "Santa Clause" ? "red" : null }
}
};
} }
} }
]; }]
``` ```
## Pivoting and Aggregation ## Pivoting and Aggregation
Pivoting the table will group records together based on their accessed values and allow the rows in that group to be expanded underneath it. Pivoting the table will group records together based on their accessed values and allow the rows in that group to be expanded underneath it.
To pivot, pass an array of `columnID`'s to `pivotBy`. Remember, a column's `id` is either the one that you assign it (when using a custom accessors) or its `accessor` string. To pivot, pass an array of `columnID`'s to `pivotBy`. Remember, a column's `id` is either the one that you assign it (when using a custom accessors) or its `accessor` string.
```javascript ```javascript
<ReactTable <ReactTable
... ...
@@ -831,60 +661,52 @@ To pivot, pass an array of `columnID`'s to `pivotBy`. Remember, a column's `id`
``` ```
Naturally when grouping rows together, you may want to aggregate the rows inside it into the grouped column. No aggregation is done by default, however, it is very simple to aggregate any pivoted columns: Naturally when grouping rows together, you may want to aggregate the rows inside it into the grouped column. No aggregation is done by default, however, it is very simple to aggregate any pivoted columns:
```javascript ```javascript
// In this example, we use lodash to sum and average the values, but you can use whatever you want to aggregate. // In this example, we use lodash to sum and average the values, but you can use whatever you want to aggregate.
const columns = [ const columns = [{
{ Header: 'Age',
Header: "Age", accessor: 'age',
accessor: "age", aggregate: (values, rows) => _.round(_.mean(values)),
aggregate: (values, rows) => _.round(_.mean(values)), Aggregated: row => {
Aggregated: row => { // You can even render the cell differently if it's an aggregated cell
// You can even render the cell differently if it's an aggregated cell return <span>row.value (avg)</span>
return <span>row.value (avg)</span>;
}
},
{
Header: "Visits",
accessor: "visits",
aggregate: (values, rows) => _.sum(values)
} }
]; }, {
Header: 'Visits',
accessor: 'visits',
aggregate: (values, rows) => _.sum(values)
}]
``` ```
Pivoted columns can be sorted just like regular columns including holding down the `<shift>` button to multi-sort. Pivoted columns can be sorted just like regular columns including holding down the `<shift>` button to multi-sort.
## Sub Tables and Sub Components ## Sub Tables and Sub Components
By adding a `SubComponent` props, you can easily add an expansion level to all root-level rows: By adding a `SubComponent` props, you can easily add an expansion level to all root-level rows:
```javascript ```javascript
<ReactTable <ReactTable
data={data} data={data}
columns={columns} columns={columns}
defaultPageSize={10} defaultPageSize={10}
SubComponent={row => { SubComponent={(row) => {
return ( return (
<div> <div>
You can put any component you want here, even another React Table! You You can put any component you want here, even another React Table! You even have access to the row-level data if you need! Spark-charts, drill-throughs, infographics... the possibilities are endless!
even have access to the row-level data if you need! Spark-charts,
drill-throughs, infographics... the possibilities are endless!
</div> </div>
); )
}} }}
/> />
``` ```
## Server-side Data
## Server-side Data
If you want to handle pagination, sorting, and filtering on the server, `react-table` makes it easy on you. If you want to handle pagination, sorting, and filtering on the server, `react-table` makes it easy on you.
1. Feed React Table `data` from somewhere dynamic. eg. `state`, a redux store, etc... 1. Feed React Table `data` from somewhere dynamic. eg. `state`, a redux store, etc...
1. Add `manual` as a prop. This informs React Table that you'll be handling sorting and pagination server-side 1. Add `manual` as a prop. This informs React Table that you'll be handling sorting and pagination server-side
1. Subscribe to the `onFetchData` prop. This function is called at `componentDidMount` and any time sorting, pagination or filterting is changed in the table 1. Subscribe to the `onFetchData` prop. This function is called at `compomentDidMount` and any time sorting, pagination or filterting is changed in the table
1. In the `onFetchData` callback, request your data using the provided information in the params of the function (current state and instance) 1. In the `onFetchData` callback, request your data using the provided information in the params of the function (current state and instance)
1. Update your data with the rows to be displayed 1. Update your data with the rows to be displayed
1. Optionally set how many pages there are total 1. Optionally set how many pages there are total
```javascript ```javascript
<ReactTable <ReactTable
@@ -915,14 +737,12 @@ If you want to handle pagination, sorting, and filtering on the server, `react-t
/> />
``` ```
For a detailed example, take a peek at our <a href="https://react-table.js.org/#/story/server-side-data" target="\_parent">async table mockup</a> For a detailed example, take a peek at our <a href="https://github.com/react-tools/react-table/blob/master/stories/ServerSide.js" target="\_parent">async table mockup</a>
## Fully Controlled Component ## Fully Controlled Component
React Table by default works fantastically out of the box, but you can achieve even more control and customization if you choose to maintain the state yourself. It is very easy to do, even if you only want to manage *parts* of the state.
React Table by default works fantastically out of the box, but you can achieve even more control and customization if you choose to maintain the state yourself. It is very easy to do, even if you only want to manage _parts_ of the state.
Here are the props and their corresponding callbacks that control the state of the a table: Here are the props and their corresponding callbacks that control the state of the a table:
```javascript ```javascript
<ReactTable <ReactTable
// Props // Props
@@ -957,50 +777,42 @@ Here are the props and their corresponding callbacks that control the state of t
onPageSizeChange={(pageSize, pageIndex) => {...}} // Called when the pageSize is changed by the user. The resolve page is also sent to maintain approximate position in the data onPageSizeChange={(pageSize, pageIndex) => {...}} // Called when the pageSize is changed by the user. The resolve page is also sent to maintain approximate position in the data
onSortedChange={(newSorted, column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. If the column is a pivoted column, `column` will be an array of columns onSortedChange={(newSorted, column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. If the column is a pivoted column, `column` will be an array of columns
onExpandedChange={(newExpanded, index, event) => {...}} // Called when an expander is clicked. Use this to manage `expanded` onExpandedChange={(newExpanded, index, event) => {...}} // Called when an expander is clicked. Use this to manage `expanded`
onFilteredChange={(filtered, column) => {...}} // Called when a user enters a value into a filter input field or the value passed to the onFiltersChange handler by the Filter option. onFilteredChange={(column, value) => {...}} // Called when a user enters a value into a filter input field or the value passed to the onFiltersChange handler by the Filter option.
onResizedChange={(newResized, event) => {...}} // Called when a user clicks on a resizing component (the right edge of a column header) onResizedChange={(newResized, event) => {...}} // Called when a user clicks on a resizing component (the right edge of a column header)
/> />
``` ```
## Functional Rendering ## Functional Rendering
Possibly one of the coolest features of React-Table is its ability to expose internal components and state for custom render logic. The easiest way to do this is to pass a function as the child of `<ReactTable />`. Possibly one of the coolest features of React-Table is its ability to expose internal components and state for custom render logic. The easiest way to do this is to pass a function as the child of `<ReactTable />`.
The function you pass will be called with the following items: The function you pass will be called with the following items:
- Fully-resolved state of the table - Fully-resolved state of the table
- A function that returns the standard table component - A function that returns the standard table component
- The instance of the component - The instance of the component
You can then return any JSX or react you want! This turns out to be perfect for: You can then return any JSX or react you want! This turns out to be perfect for:
- Accessing the internal state of the table without a `ref` - Accessing the internal state of the table without a `ref`
- Decorating the table or extending it with your own UI - Decorating the table or extending it with your own UI
- Building your own custom display logic - Building your own custom display logic
Accessing internal state and wrapping with more UI: Accessing internal state and wrapping with more UI:
```javascript ```javascript
<ReactTable data={data} columns={columns}> <ReactTable
data={data}
columns={columns}
>
{(state, makeTable, instance) => { {(state, makeTable, instance) => {
return ( return (
<div <div style={{
style={{ background: '#ffcf00',
background: "#ffcf00", borderRadius: '5px',
borderRadius: "5px", overflow: 'hidden',
overflow: "hidden", padding: '5px'
padding: "5px" }}>
}} <pre><code>state.allVisibleColumns === {JSON.stringify(state.allVisibleColumns, null, 4)}</code></pre>
>
<pre>
<code>
state.allVisibleColumns ==={" "}
{JSON.stringify(state.allVisibleColumns, null, 4)}
</code>
</pre>
{makeTable()} {makeTable()}
</div> </div>
); )
}} }}
</ReactTable> </ReactTable>
``` ```
@@ -1008,55 +820,45 @@ Accessing internal state and wrapping with more UI:
The possibilities are endless! The possibilities are endless!
## Sorting ## Sorting
Sorting comes built in with React-Table. Sorting comes built in with React-Table.
- Click a column header to sort by its accessor. - Click a column header to sort by its accessor.
- Click it again to reverse the sort. - Click it again to reverse the sort.
- Set `defaultSortDesc` property to `true` to make the first sort direction default to descending. - Set `defaultSortDesc` property to `true` to make the first sort direction default to descending.
- Override a specific column's default sort direction by using the same `defaultSortDesc` property on a column, set to `true` - Override a specific column's default sort direction by using the same `defaultSortDesc` property on a column, set to `true`
## Multi-Sort ## 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! 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 ## Custom Sorting Algorithm
To override the default sorting algorithm for the whole table use the `defaultSortMethod` prop. To override the default sorting algorithm for the whole table use the `defaultSortMethod` prop.
To override the sorting algorithm for a single column, use the `sortMethod` column property. To override the sorting algorithm for a single column, use the `sortMethod` column property.
Supply a function that implements the native javascript [`Array.sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) interface. This is React Table's default sorting algorithm: Supply a function that implements the native javascript [`Array.sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) interface. This is React Table's default sorting algorithm:
- `a` the first value to compare - `a` the first value to compare
- `b` the second value to compare - `b` the second value to compare
- `desc` true if sort is descending, false if ascending - `dir` the
```javascript ```javascript
defaultSortMethod = (a, b, desc) => { defaultSortMethod = (a, b) => {
// force null and undefined to the bottom // force null and undefined to the bottom
a = a === null || a === undefined ? -Infinity : a; a = (a === null || a === undefined) ? -Infinity : a
b = b === null || b === undefined ? -Infinity : b; b = (b === null || b === undefined) ? -Infinity : b
// force any string values to lowercase // force any string values to lowercase
a = typeof a === "string" ? a.toLowerCase() : a; a = typeof a === 'string' ? a.toLowerCase() : a
b = typeof b === "string" ? b.toLowerCase() : b; b = typeof b === 'string' ? b.toLowerCase() : b
// Return either 1 or -1 to indicate a sort priority // Return either 1 or -1 to indicate a sort priority
if (a > b) { if (a > b) {
return 1; return 1
} }
if (a < b) { if (a < b) {
return -1; return -1
} }
// returning 0 or undefined will use any subsequent column sorting methods or the row index as a tiebreaker // returning 0 or undefined will use any subsequent column sorting methods or the row index as a tiebreaker
return 0; return 0
}; }
``` ```
## Filtering ## Filtering
Filtering can be enabled by setting the `filterable` option on the table. Filtering can be enabled by setting the `filterable` option on the table.
If you don't want particular column to be filtered you can set the `filterable={false}` option on the column. If you don't want particular column to be filtered you can set the `filterable={false}` option on the column.
@@ -1069,14 +871,12 @@ By default, `filterMethod` is passed a single row of data at a time, and you are
Alternatively, you can set `filterAll` to `true`, and `filterMethod` will be passed the entire array of rows to be filtered, and you will then be responsible for returning the new filtered array. This is extremely handy when you need to utilize a utility like fuzzy matching that requires the entire array of items. Alternatively, you can set `filterAll` to `true`, and `filterMethod` will be passed the entire array of rows to be filtered, and you will then be responsible for returning the new filtered array. This is extremely handy when you need to utilize a utility like fuzzy matching that requires the entire array of items.
To completely override the filter that is shown, you can set the `Filter` column option. Using this option you can specify the JSX that is shown. The option is passed an `onChange` method which must be called with the the value that you want to pass to the `filterMethod` option whenever the filter has changed. To completely override the filter that is shown, you can set the `Filter` column option. Using this option you can specify the JSX that is shown. The option is passed an `onChange` method which must be called with the the value that you wan't to pass to the `filterMethod` option whenever the filter has changed.
See <a href="http://react-table.js.org/#/story/custom-filtering" target="\_parent">Custom Filtering</a> demo for examples. See <a href="http://react-table.js.org/#/story/custom-filtering" target="\_parent">Custom Filtering</a> demo for examples.
## Component Overrides ## Component Overrides
Though we confidently stand by the markup and architecture behind it, `react-table` does offer the ability to change the core componentry it uses to render everything. You can extend or override these internal components by passing a react component to it's corresponding prop on either the global props or on a one-off basis like so: Though we confidently stand by the markup and architecture behind it, `react-table` does offer the ability to change the core componentry it uses to render everything. You can extend or override these internal components by passing a react component to it's corresponding prop on either the global props or on a one-off basis like so:
```javascript ```javascript
// Change the global default // Change the global default
import { ReactTableDefaults } from 'react-table' import { ReactTableDefaults } from 'react-table'
@@ -1086,7 +886,7 @@ Object.assign(ReactTableDefaults, {
TbodyComponent: component, TbodyComponent: component,
TrGroupComponent: component, TrGroupComponent: component,
TrComponent: component, TrComponent: component,
ThComponent: component, ThComponent: component
TdComponent: component, TdComponent: component,
TfootComponent: component, TfootComponent: component,
ExpanderComponent: component, ExpanderComponent: component,
@@ -1112,8 +912,8 @@ Object.assign(ReactTableDefaults, {
If you choose to change the core components React-Table uses to render, you must make sure your replacement components consume and utilize all of the supplied and inherited props that are needed for that component to function properly. We would suggest investigating <a href="https://github.com/react-tools/react-table/blob/master/src/index.js" target="\_parent">the source</a> for the component you wish to replace. If you choose to change the core components React-Table uses to render, you must make sure your replacement components consume and utilize all of the supplied and inherited props that are needed for that component to function properly. We would suggest investigating <a href="https://github.com/react-tools/react-table/blob/master/src/index.js" target="\_parent">the source</a> for the component you wish to replace.
## Contributing
## Contributing
To suggest a feature, create an issue if it does not already exist. To suggest a feature, create an issue if it does not already exist.
If you would like to help develop a suggested feature follow these steps: If you would like to help develop a suggested feature follow these steps:
@@ -1122,7 +922,7 @@ If you would like to help develop a suggested feature follow these steps:
- Auto-build files as you edit with `$ yarn run watch` - Auto-build files as you edit with `$ yarn run watch`
- Implement your changes to files in the `src/` directory - Implement your changes to files in the `src/` directory
- Run the <a href="https://github.com/tannerlinsley/react-story">React Story</a> locally with `$ yarn run docs` - Run the <a href="https://github.com/tannerlinsley/react-story">React Story</a> locally with `$ yarn run docs`
- View changes as you edit `docs/src` - View changes as you edit `docs/src`,
- Submit PR for review - Submit PR for review
#### Scripts #### Scripts
+192 -197
View File
@@ -5,83 +5,83 @@ You can find the most recent version of this guide [here](https://github.com/fac
## Table of Contents ## Table of Contents
* [Updating to New Releases](#updating-to-new-releases) - [Updating to New Releases](#updating-to-new-releases)
* [Sending Feedback](#sending-feedback) - [Sending Feedback](#sending-feedback)
* [Folder Structure](#folder-structure) - [Folder Structure](#folder-structure)
* [Available Scripts](#available-scripts) - [Available Scripts](#available-scripts)
* [npm start](#npm-start) - [npm start](#npm-start)
* [npm test](#npm-test) - [npm test](#npm-test)
* [npm run build](#npm-run-build) - [npm run build](#npm-run-build)
* [npm run eject](#npm-run-eject) - [npm run eject](#npm-run-eject)
* [Supported Language Features and Polyfills](#supported-language-features-and-polyfills) - [Supported Language Features and Polyfills](#supported-language-features-and-polyfills)
* [Syntax Highlighting in the Editor](#syntax-highlighting-in-the-editor) - [Syntax Highlighting in the Editor](#syntax-highlighting-in-the-editor)
* [Displaying Lint Output in the Editor](#displaying-lint-output-in-the-editor) - [Displaying Lint Output in the Editor](#displaying-lint-output-in-the-editor)
* [Debugging in the Editor](#debugging-in-the-editor) - [Debugging in the Editor](#debugging-in-the-editor)
* [Changing the Page `<title>`](#changing-the-page-title) - [Changing the Page `<title>`](#changing-the-page-title)
* [Installing a Dependency](#installing-a-dependency) - [Installing a Dependency](#installing-a-dependency)
* [Importing a Component](#importing-a-component) - [Importing a Component](#importing-a-component)
* [Adding a Stylesheet](#adding-a-stylesheet) - [Adding a Stylesheet](#adding-a-stylesheet)
* [Post-Processing CSS](#post-processing-css) - [Post-Processing CSS](#post-processing-css)
* [Adding a CSS Preprocessor (Sass, Less etc.)](#adding-a-css-preprocessor-sass-less-etc) - [Adding a CSS Preprocessor (Sass, Less etc.)](#adding-a-css-preprocessor-sass-less-etc)
* [Adding Images and Fonts](#adding-images-and-fonts) - [Adding Images and Fonts](#adding-images-and-fonts)
* [Using the `public` Folder](#using-the-public-folder) - [Using the `public` Folder](#using-the-public-folder)
* [Changing the HTML](#changing-the-html) - [Changing the HTML](#changing-the-html)
* [Adding Assets Outside of the Module System](#adding-assets-outside-of-the-module-system) - [Adding Assets Outside of the Module System](#adding-assets-outside-of-the-module-system)
* [When to Use the `public` Folder](#when-to-use-the-public-folder) - [When to Use the `public` Folder](#when-to-use-the-public-folder)
* [Using Global Variables](#using-global-variables) - [Using Global Variables](#using-global-variables)
* [Adding Bootstrap](#adding-bootstrap) - [Adding Bootstrap](#adding-bootstrap)
* [Using a Custom Theme](#using-a-custom-theme) - [Using a Custom Theme](#using-a-custom-theme)
* [Adding Flow](#adding-flow) - [Adding Flow](#adding-flow)
* [Adding Custom Environment Variables](#adding-custom-environment-variables) - [Adding Custom Environment Variables](#adding-custom-environment-variables)
* [Referencing Environment Variables in the HTML](#referencing-environment-variables-in-the-html) - [Referencing Environment Variables in the HTML](#referencing-environment-variables-in-the-html)
* [Adding Temporary Environment Variables In Your Shell](#adding-temporary-environment-variables-in-your-shell) - [Adding Temporary Environment Variables In Your Shell](#adding-temporary-environment-variables-in-your-shell)
* [Adding Development Environment Variables In `.env`](#adding-development-environment-variables-in-env) - [Adding Development Environment Variables In `.env`](#adding-development-environment-variables-in-env)
* [Can I Use Decorators?](#can-i-use-decorators) - [Can I Use Decorators?](#can-i-use-decorators)
* [Integrating with an API Backend](#integrating-with-an-api-backend) - [Integrating with an API Backend](#integrating-with-an-api-backend)
* [Node](#node) - [Node](#node)
* [Ruby on Rails](#ruby-on-rails) - [Ruby on Rails](#ruby-on-rails)
* [Proxying API Requests in Development](#proxying-api-requests-in-development) - [Proxying API Requests in Development](#proxying-api-requests-in-development)
* [Using HTTPS in Development](#using-https-in-development) - [Using HTTPS in Development](#using-https-in-development)
* [Generating Dynamic `<meta>` Tags on the Server](#generating-dynamic-meta-tags-on-the-server) - [Generating Dynamic `<meta>` Tags on the Server](#generating-dynamic-meta-tags-on-the-server)
* [Pre-Rendering into Static HTML Files](#pre-rendering-into-static-html-files) - [Pre-Rendering into Static HTML Files](#pre-rendering-into-static-html-files)
* [Injecting Data from the Server into the Page](#injecting-data-from-the-server-into-the-page) - [Injecting Data from the Server into the Page](#injecting-data-from-the-server-into-the-page)
* [Running Tests](#running-tests) - [Running Tests](#running-tests)
* [Filename Conventions](#filename-conventions) - [Filename Conventions](#filename-conventions)
* [Command Line Interface](#command-line-interface) - [Command Line Interface](#command-line-interface)
* [Version Control Integration](#version-control-integration) - [Version Control Integration](#version-control-integration)
* [Writing Tests](#writing-tests) - [Writing Tests](#writing-tests)
* [Testing Components](#testing-components) - [Testing Components](#testing-components)
* [Using Third Party Assertion Libraries](#using-third-party-assertion-libraries) - [Using Third Party Assertion Libraries](#using-third-party-assertion-libraries)
* [Initializing Test Environment](#initializing-test-environment) - [Initializing Test Environment](#initializing-test-environment)
* [Focusing and Excluding Tests](#focusing-and-excluding-tests) - [Focusing and Excluding Tests](#focusing-and-excluding-tests)
* [Coverage Reporting](#coverage-reporting) - [Coverage Reporting](#coverage-reporting)
* [Continuous Integration](#continuous-integration) - [Continuous Integration](#continuous-integration)
* [Disabling jsdom](#disabling-jsdom) - [Disabling jsdom](#disabling-jsdom)
* [Snapshot Testing](#snapshot-testing) - [Snapshot Testing](#snapshot-testing)
* [Editor Integration](#editor-integration) - [Editor Integration](#editor-integration)
* [Developing Components in Isolation](#developing-components-in-isolation) - [Developing Components in Isolation](#developing-components-in-isolation)
* [Making a Progressive Web App](#making-a-progressive-web-app) - [Making a Progressive Web App](#making-a-progressive-web-app)
* [Deployment](#deployment) - [Deployment](#deployment)
* [Static Server](#static-server) - [Static Server](#static-server)
* [Other Solutions](#other-solutions) - [Other Solutions](#other-solutions)
* [Serving Apps with Client-Side Routing](#serving-apps-with-client-side-routing) - [Serving Apps with Client-Side Routing](#serving-apps-with-client-side-routing)
* [Building for Relative Paths](#building-for-relative-paths) - [Building for Relative Paths](#building-for-relative-paths)
* [Azure](#azure) - [Azure](#azure)
* [Firebase](#firebase) - [Firebase](#firebase)
* [GitHub Pages](#github-pages) - [GitHub Pages](#github-pages)
* [Heroku](#heroku) - [Heroku](#heroku)
* [Modulus](#modulus) - [Modulus](#modulus)
* [Netlify](#netlify) - [Netlify](#netlify)
* [Now](#now) - [Now](#now)
* [S3 and CloudFront](#s3-and-cloudfront) - [S3 and CloudFront](#s3-and-cloudfront)
* [Surge](#surge) - [Surge](#surge)
* [Advanced Configuration](#advanced-configuration) - [Advanced Configuration](#advanced-configuration)
* [Troubleshooting](#troubleshooting) - [Troubleshooting](#troubleshooting)
* [`npm start` doesn’t detect changes](#npm-start-doesnt-detect-changes) - [`npm start` doesn’t detect changes](#npm-start-doesnt-detect-changes)
* [`npm test` hangs on macOS Sierra](#npm-test-hangs-on-macos-sierra) - [`npm test` hangs on macOS Sierra](#npm-test-hangs-on-macos-sierra)
* [`npm run build` silently fails](#npm-run-build-silently-fails) - [`npm run build` silently fails](#npm-run-build-silently-fails)
* [`npm run build` fails on Heroku](#npm-run-build-fails-on-heroku) - [`npm run build` fails on Heroku](#npm-run-build-fails-on-heroku)
* [Something Missing?](#something-missing) - [Something Missing?](#something-missing)
## Updating to New Releases ## Updating to New Releases
@@ -207,7 +207,7 @@ To configure the syntax highlighting in your favorite text editor, head to the [
## Displaying Lint Output in the Editor ## Displaying Lint Output in the Editor
> Note: this feature is available with `react-scripts@0.2.0` and higher. >Note: this feature is available with `react-scripts@0.2.0` and higher.
Some editors, including Sublime Text, Atom, and Visual Studio Code, provide plugins for ESLint. Some editors, including Sublime Text, Atom, and Visual Studio Code, provide plugins for ESLint.
@@ -215,24 +215,24 @@ They are not required for linting. You should see the linter output right in you
You would need to install an ESLint plugin for your editor first. You would need to install an ESLint plugin for your editor first.
> **A note for Atom `linter-eslint` users** >**A note for Atom `linter-eslint` users**
> If you are using the Atom `linter-eslint` plugin, make sure that **Use global ESLint installation** option is checked: >If you are using the Atom `linter-eslint` plugin, make sure that **Use global ESLint installation** option is checked:
> <img src="http://i.imgur.com/yVNNHJM.png" width="300"> ><img src="http://i.imgur.com/yVNNHJM.png" width="300">
> **For Visual Studio Code users**
> VS Code ESLint plugin automatically detects Create React App's configuration file. So you do not need to create `eslintrc.json` at the root directory, except when you want to add your own rules. In that case, you should include CRA's config by adding this line: >**For Visual Studio Code users**
> ```js >VS Code ESLint plugin automatically detects Create React App's configuration file. So you do not need to create `eslintrc.json` at the root directory, except when you want to add your own rules. In that case, you should include CRA's config by adding this line:
> {
> // ... >```js
> "extends": "react-app" {
> } // ...
> ``` "extends": "react-app"
}
```
````
Then add this block to the `package.json` file of your project: Then add this block to the `package.json` file of your project:
```js ```js
@@ -242,9 +242,9 @@ Then add this block to the `package.json` file of your project:
"extends": "react-app" "extends": "react-app"
} }
} }
```` ```
Finally, you will need to install some packages _globally_: Finally, you will need to install some packages *globally*:
```sh ```sh
npm install -g eslint-config-react-app@0.3.0 eslint@3.8.1 babel-eslint@7.0.0 eslint-plugin-react@6.4.1 eslint-plugin-import@2.0.1 eslint-plugin-jsx-a11y@4.0.0 eslint-plugin-flowtype@2.21.0 npm install -g eslint-config-react-app@0.3.0 eslint@3.8.1 babel-eslint@7.0.0 eslint-plugin-react@6.4.1 eslint-plugin-import@2.0.1 eslint-plugin-jsx-a11y@4.0.0 eslint-plugin-flowtype@2.21.0
@@ -265,19 +265,17 @@ Then add the block below to your `launch.json` file and put it inside the `.vsco
```json ```json
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [{
{ "name": "Chrome",
"name": "Chrome", "type": "chrome",
"type": "chrome", "request": "launch",
"request": "launch", "url": "http://localhost:3000",
"url": "http://localhost:3000", "webRoot": "${workspaceRoot}/src",
"webRoot": "${workspaceRoot}/src", "userDataDir": "${workspaceRoot}/.vscode/chrome",
"userDataDir": "${workspaceRoot}/.vscode/chrome", "sourceMapPathOverrides": {
"sourceMapPathOverrides": { "webpack:///src/*": "${webRoot}/*"
"webpack:///src/*": "${webRoot}/*"
}
} }
] }]
} }
``` ```
@@ -311,7 +309,7 @@ For example:
### `Button.js` ### `Button.js`
```js ```js
import React, { Component } from "react"; import React, { Component } from 'react';
class Button extends Component { class Button extends Component {
render() { render() {
@@ -324,9 +322,10 @@ export default Button; // Don’t forget to use export default!
### `DangerButton.js` ### `DangerButton.js`
```js ```js
import React, { Component } from "react"; import React, { Component } from 'react';
import Button from "./Button"; // Import a component from another file import Button from './Button'; // Import a component from another file
class DangerButton extends Component { class DangerButton extends Component {
render() { render() {
@@ -364,8 +363,8 @@ This project setup uses [Webpack](https://webpack.github.io/) for handling all a
### `Button.js` ### `Button.js`
```js ```js
import React, { Component } from "react"; import React, { Component } from 'react';
import "./Button.css"; // Tell Webpack that Button.js uses these styles import './Button.css'; // Tell Webpack that Button.js uses these styles
class Button extends Component { class Button extends Component {
render() { render() {
@@ -404,11 +403,11 @@ becomes this:
display: flex; display: flex;
-webkit-box-orient: horizontal; -webkit-box-orient: horizontal;
-webkit-box-direction: normal; -webkit-box-direction: normal;
-ms-flex-direction: row; -ms-flex-direction: row;
flex-direction: row; flex-direction: row;
-webkit-box-align: center; -webkit-box-align: center;
-ms-flex-align: center; -ms-flex-align: center;
align-items: center; align-items: center;
} }
``` ```
@@ -437,7 +436,7 @@ Then in `package.json`, add the following lines to `scripts`:
"test": "react-scripts test --env=jsdom", "test": "react-scripts test --env=jsdom",
``` ```
> Note: To use a different preprocessor, replace `build-css` and `watch-css` commands according to your preprocessor’s documentation. >Note: To use a different preprocessor, replace `build-css` and `watch-css` commands according to your preprocessor’s documentation.
Now you can rename `src/App.css` to `src/App.scss` and run `npm run watch-css`. The watcher will find every Sass file in `src` subdirectories, and create a corresponding CSS file next to it, in our case overwriting `src/App.css`. Since `src/App.js` still imports `src/App.css`, the styles become a part of your application. You can now edit `src/App.scss`, and `src/App.css` will be regenerated. Now you can rename `src/App.css` to `src/App.scss` and run `npm run watch-css`. The watcher will find every Sass file in `src` subdirectories, and create a corresponding CSS file next to it, in our case overwriting `src/App.css`. Since `src/App.js` still imports `src/App.css`, the styles become a part of your application. You can now edit `src/App.scss`, and `src/App.css` will be regenerated.
@@ -478,8 +477,8 @@ You can **`import` an image right in a JavaScript module**. This tells Webpack t
Here is an example: Here is an example:
```js ```js
import React from "react"; import React from 'react';
import logo from "./logo.png"; // Tell Webpack this JS file uses this image import logo from './logo.png'; // Tell Webpack this JS file uses this image
console.log(logo); // /logo.84287d09.png console.log(logo); // /logo.84287d09.png
@@ -510,7 +509,7 @@ An alternative way of handling static assets is described in the next section.
## Using the `public` Folder ## Using the `public` Folder
> Note: this feature is available with `react-scripts@0.5.0` and higher. >Note: this feature is available with `react-scripts@0.5.0` and higher.
### Changing the HTML ### Changing the HTML
@@ -531,7 +530,7 @@ This mechanism provides a number of benefits:
However there is an **escape hatch** that you can use to add an asset outside of the module system. However there is an **escape hatch** that you can use to add an asset outside of the module system.
If you put a file into the `public` folder, it will **not** be processed by Webpack. Instead it will be copied into the build folder untouched. To reference assets in the `public` folder, you need to use a special variable called `PUBLIC_URL`. If you put a file into the `public` folder, it will **not** be processed by Webpack. Instead it will be copied into the build folder untouched. To reference assets in the `public` folder, you need to use a special variable called `PUBLIC_URL`.
Inside `index.html`, you can use it like this: Inside `index.html`, you can use it like this:
@@ -597,19 +596,19 @@ npm install react-bootstrap --save
npm install bootstrap@3 --save npm install bootstrap@3 --save
``` ```
Import Bootstrap CSS and optionally Bootstrap theme CSS in the beginning of your `src/index.js` file: Import Bootstrap CSS and optionally Bootstrap theme CSS in the beginning of your ```src/index.js``` file:
```js ```js
import "bootstrap/dist/css/bootstrap.css"; import 'bootstrap/dist/css/bootstrap.css';
import "bootstrap/dist/css/bootstrap-theme.css"; import 'bootstrap/dist/css/bootstrap-theme.css';
// Put any other imports below so that CSS from your // Put any other imports below so that CSS from your
// components takes precedence over default styles. // components takes precedence over default styles.
``` ```
Import required React Bootstrap components within `src/App.js` file or your custom component files: Import required React Bootstrap components within ```src/App.js``` file or your custom component files:
```js ```js
import { Navbar, Jumbotron, Button } from "react-bootstrap"; import { Navbar, Jumbotron, Button } from 'react-bootstrap';
``` ```
Now you are ready to use the imported React Bootstrap components within your component hierarchy defined in the render method. Here is an example [`App.js`](https://gist.githubusercontent.com/gaearon/85d8c067f6af1e56277c82d19fd4da7b/raw/6158dd991b67284e9fc8d70b9d973efe87659d72/App.js) redone using React Bootstrap. Now you are ready to use the imported React Bootstrap components within your component hierarchy defined in the render method. Here is an example [`App.js`](https://gist.githubusercontent.com/gaearon/85d8c067f6af1e56277c82d19fd4da7b/raw/6158dd991b67284e9fc8d70b9d973efe87659d72/App.js) redone using React Bootstrap.
@@ -633,10 +632,10 @@ Recent versions of [Flow](http://flowtype.org/) work with Create React App proje
To add Flow to a Create React App project, follow these steps: To add Flow to a Create React App project, follow these steps:
1. Run `npm install --save-dev flow-bin` (or `yarn add --dev flow-bin`). 1. Run `npm install --save-dev flow-bin` (or `yarn add --dev flow-bin`).
2. Add `"flow": "flow"` to the `scripts` section of your `package.json`. 2. Add `"flow": "flow"` to the `scripts` section of your `package.json`.
3. Run `npm run flow -- init` (or `yarn flow -- init`) to create a [`.flowconfig` file](https://flowtype.org/docs/advanced-configuration.html) in the root directory. 3. Run `npm run flow -- init` (or `yarn flow -- init`) to create a [`.flowconfig` file](https://flowtype.org/docs/advanced-configuration.html) in the root directory.
4. Add `// @flow` to any files you want to type check (for example, to `src/App.js`). 4. Add `// @flow` to any files you want to type check (for example, to `src/App.js`).
Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors. Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors.
You can optionally use an IDE like [Nuclide](https://nuclide.io/docs/languages/flow/) for a better integrated experience. You can optionally use an IDE like [Nuclide](https://nuclide.io/docs/languages/flow/) for a better integrated experience.
@@ -646,7 +645,7 @@ To learn more about Flow, check out [its documentation](https://flowtype.org/).
## Adding Custom Environment Variables ## Adding Custom Environment Variables
> Note: this feature is available with `react-scripts@0.2.3` and higher. >Note: this feature is available with `react-scripts@0.2.3` and higher.
Your project can consume variables declared in your environment as if they were declared locally in your JS files. By Your project can consume variables declared in your environment as if they were declared locally in your JS files. By
default you will have `NODE_ENV` defined for you, and any other environment variables starting with default you will have `NODE_ENV` defined for you, and any other environment variables starting with
@@ -654,7 +653,7 @@ default you will have `NODE_ENV` defined for you, and any other environment vari
**The environment variables are embedded during the build time**. Since Create React App produces a static HTML/CSS/JS bundle, it can’t possibly read them at runtime. To read them at runtime, you would need to load HTML into memory on the server and replace placeholders in runtime, just like [described here](#injecting-data-from-the-server-into-the-page). Alternatively you can rebuild the app on the server anytime you change them. **The environment variables are embedded during the build time**. Since Create React App produces a static HTML/CSS/JS bundle, it can’t possibly read them at runtime. To read them at runtime, you would need to load HTML into memory on the server and replace placeholders in runtime, just like [described here](#injecting-data-from-the-server-into-the-page). Alternatively you can rebuild the app on the server anytime you change them.
> Note: You must create custom environment variables beginning with `REACT_APP_`. Any other variables except `NODE_ENV` will be ignored to avoid accidentally [exposing a private key on the machine that could have the same name](https://github.com/facebookincubator/create-react-app/issues/865#issuecomment-252199527). Changing any environment variables will require you to restart the development server if it is running. >Note: You must create custom environment variables beginning with `REACT_APP_`. Any other variables except `NODE_ENV` will be ignored to avoid accidentally [exposing a private key on the machine that could have the same name](https://github.com/facebookincubator/create-react-app/issues/865#issuecomment-252199527). Changing any environment variables will require you to restart the development server if it is running.
These environment variables will be defined for you on `process.env`. For example, having an environment These environment variables will be defined for you on `process.env`. For example, having an environment
variable named `REACT_APP_SECRET_CODE` will be exposed in your JS as `process.env.REACT_APP_SECRET_CODE`. variable named `REACT_APP_SECRET_CODE` will be exposed in your JS as `process.env.REACT_APP_SECRET_CODE`.
@@ -700,7 +699,7 @@ a `.env` file. Both of these ways are described in the next few sections.
Having access to the `NODE_ENV` is also useful for performing actions conditionally: Having access to the `NODE_ENV` is also useful for performing actions conditionally:
```js ```js
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== 'production') {
analytics.disable(); analytics.disable();
} }
``` ```
@@ -709,7 +708,7 @@ When you compile the app with `npm run build`, the minification step will strip
### Referencing Environment Variables in the HTML ### Referencing Environment Variables in the HTML
> Note: this feature is available with `react-scripts@0.9.0` and higher. >Note: this feature is available with `react-scripts@0.9.0` and higher.
You can also access the environment variables starting with `REACT_APP_` in the `public/index.html`. For example: You can also access the environment variables starting with `REACT_APP_` in the `public/index.html`. For example:
@@ -743,7 +742,7 @@ REACT_APP_SECRET_CODE=abcdef npm start
### Adding Development Environment Variables In `.env` ### Adding Development Environment Variables In `.env`
> Note: this feature is available with `react-scripts@0.5.0` and higher. >Note: this feature is available with `react-scripts@0.5.0` and higher.
To define permanent environment variables, create a file called `.env` in the root of your project: To define permanent environment variables, create a file called `.env` in the root of your project:
@@ -754,8 +753,8 @@ REACT_APP_SECRET_CODE=abcdef
These variables will act as the defaults if the machine does not explicitly set them.<br> These variables will act as the defaults if the machine does not explicitly set them.<br>
Please refer to the [dotenv documentation](https://github.com/motdotla/dotenv) for more details. Please refer to the [dotenv documentation](https://github.com/motdotla/dotenv) for more details.
> Note: If you are defining environment variables for development, your CI and/or hosting platform will most likely need >Note: If you are defining environment variables for development, your CI and/or hosting platform will most likely need
> these defined as well. Consult their documentation how to do this. For example, see the documentation for [Travis CI](https://docs.travis-ci.com/user/environment-variables/) or [Heroku](https://devcenter.heroku.com/articles/config-vars). these defined as well. Consult their documentation how to do this. For example, see the documentation for [Travis CI](https://docs.travis-ci.com/user/environment-variables/) or [Heroku](https://devcenter.heroku.com/articles/config-vars).
## Can I Use Decorators? ## Can I Use Decorators?
@@ -780,7 +779,6 @@ These tutorials will help you to integrate your app with an API backend running
using `fetch()` to access it. using `fetch()` to access it.
### Node ### Node
Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/). Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/).
You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo). You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo).
@@ -791,7 +789,7 @@ You can find the companion GitHub repository [here](https://github.com/fullstack
## Proxying API Requests in Development ## Proxying API Requests in Development
> Note: this feature is available with `react-scripts@0.2.3` and higher. >Note: this feature is available with `react-scripts@0.2.3` and higher.
People often serve the front-end React app from the same host and port as their backend implementation.<br> People often serve the front-end React app from the same host and port as their backend implementation.<br>
For example, a production setup might look like this after the app is deployed: For example, a production setup might look like this after the app is deployed:
@@ -828,7 +826,7 @@ If the `proxy` option is **not** flexible enough for you, alternatively you can:
## Using HTTPS in Development ## Using HTTPS in Development
> Note: this feature is available with `react-scripts@0.4.0` and higher. >Note: this feature is available with `react-scripts@0.4.0` and higher.
You may require the dev server to serve pages over HTTPS. One particular case where this could be useful is when using [the "proxy" feature](#proxying-api-requests-in-development) to proxy requests to an API server when that API server is itself serving HTTPS. You may require the dev server to serve pages over HTTPS. One particular case where this could be useful is when using [the "proxy" feature](#proxying-api-requests-in-development) to proxy requests to an API server when that API server is itself serving HTTPS.
@@ -893,7 +891,8 @@ Then, on the server, you can replace `__SERVER_DATA__` with a JSON of real data
## Running Tests ## Running Tests
> Note: this feature is available with `react-scripts@0.3.0` and higher.<br> >[Read the migration guide to learn how to enable it in older projects!](https://github.com/facebookincubator/create-react-app/blob/master/CHANGELOG.md#migrating-from-023-to-030) >Note: this feature is available with `react-scripts@0.3.0` and higher.<br>
>[Read the migration guide to learn how to enable it in older projects!](https://github.com/facebookincubator/create-react-app/blob/master/CHANGELOG.md#migrating-from-023-to-030)
Create React App uses [Jest](https://facebook.github.io/jest/) as its test runner. To prepare for this integration, we did a [major revamp](https://facebook.github.io/jest/blog/2016/09/01/jest-15.html) of Jest so if you heard bad things about it years ago, give it another try. Create React App uses [Jest](https://facebook.github.io/jest/) as its test runner. To prepare for this integration, we did a [major revamp](https://facebook.github.io/jest/blog/2016/09/01/jest-15.html) of Jest so if you heard bad things about it years ago, give it another try.
@@ -938,9 +937,9 @@ To create tests, add `it()` (or `test()`) blocks with the name of the test and i
Jest provides a built-in `expect()` global function for making assertions. A basic test could look like this: Jest provides a built-in `expect()` global function for making assertions. A basic test could look like this:
```js ```js
import sum from "./sum"; import sum from './sum';
it("sums numbers", () => { it('sums numbers', () => {
expect(sum(1, 2)).toEqual(3); expect(sum(1, 2)).toEqual(3);
expect(sum(2, 2)).toEqual(4); expect(sum(2, 2)).toEqual(4);
}); });
@@ -956,12 +955,12 @@ There is a broad spectrum of component testing techniques. They range from a “
Different projects choose different testing tradeoffs based on how often components change, and how much logic they contain. If you haven’t decided on a testing strategy yet, we recommend that you start with creating simple smoke tests for your components: Different projects choose different testing tradeoffs based on how often components change, and how much logic they contain. If you haven’t decided on a testing strategy yet, we recommend that you start with creating simple smoke tests for your components:
```js ```js
import React from "react"; import React from 'react';
import ReactDOM from "react-dom"; import ReactDOM from 'react-dom';
import App from "./App"; import App from './App';
it("renders without crashing", () => { it('renders without crashing', () => {
const div = document.createElement("div"); const div = document.createElement('div');
ReactDOM.render(<App />, div); ReactDOM.render(<App />, div);
}); });
``` ```
@@ -977,11 +976,11 @@ npm install --save-dev enzyme react-addons-test-utils
``` ```
```js ```js
import React from "react"; import React from 'react';
import { shallow } from "enzyme"; import { shallow } from 'enzyme';
import App from "./App"; import App from './App';
it("renders without crashing", () => { it('renders without crashing', () => {
shallow(<App />); shallow(<App />);
}); });
``` ```
@@ -993,11 +992,11 @@ You can read the [Enzyme documentation](http://airbnb.io/enzyme/) for more testi
Here is an example from Enzyme documentation that asserts specific output, rewritten to use Jest matchers: Here is an example from Enzyme documentation that asserts specific output, rewritten to use Jest matchers:
```js ```js
import React from "react"; import React from 'react';
import { shallow } from "enzyme"; import { shallow } from 'enzyme';
import App from "./App"; import App from './App';
it("renders welcome message", () => { it('renders welcome message', () => {
const wrapper = shallow(<App />); const wrapper = shallow(<App />);
const welcome = <h2>Welcome to React</h2>; const welcome = <h2>Welcome to React</h2>;
// expect(wrapper.contains(welcome)).to.equal(true); // expect(wrapper.contains(welcome)).to.equal(true);
@@ -1011,7 +1010,7 @@ Nevertheless you can use a third-party assertion library like [Chai](http://chai
Additionally, you might find [jest-enzyme](https://github.com/blainekasten/enzyme-matchers) helpful to simplify your tests with readable matchers. The above `contains` code can be written simpler with jest-enzyme. Additionally, you might find [jest-enzyme](https://github.com/blainekasten/enzyme-matchers) helpful to simplify your tests with readable matchers. The above `contains` code can be written simpler with jest-enzyme.
```js ```js
expect(wrapper).toContainReact(welcome); expect(wrapper).toContainReact(welcome)
``` ```
To setup jest-enzyme with Create React App, follow the instructions for [initializing your test environment](#initializing-test-environment) to import `jest-enzyme`. To setup jest-enzyme with Create React App, follow the instructions for [initializing your test environment](#initializing-test-environment) to import `jest-enzyme`.
@@ -1022,9 +1021,10 @@ npm install --save-dev jest-enzyme
```js ```js
// src/setupTests.js // src/setupTests.js
import "jest-enzyme"; import 'jest-enzyme';
``` ```
### Using Third Party Assertion Libraries ### Using Third Party Assertion Libraries
We recommend that you use `expect()` for assertions and `jest.fn()` for spies. If you are having issues with them please [file those against Jest](https://github.com/facebook/jest/issues/new), and we’ll fix them. We intend to keep making them better for React, supporting, for example, [pretty-printing React elements as JSX](https://github.com/facebook/jest/pull/1566). We recommend that you use `expect()` for assertions and `jest.fn()` for spies. If you are having issues with them please [file those against Jest](https://github.com/facebook/jest/issues/new), and we’ll fix them. We intend to keep making them better for React, supporting, for example, [pretty-printing React elements as JSX](https://github.com/facebook/jest/pull/1566).
@@ -1032,29 +1032,28 @@ We recommend that you use `expect()` for assertions and `jest.fn()` for spies. I
However, if you are used to other libraries, such as [Chai](http://chaijs.com/) and [Sinon](http://sinonjs.org/), or if you have existing code using them that you’d like to port over, you can import them normally like this: However, if you are used to other libraries, such as [Chai](http://chaijs.com/) and [Sinon](http://sinonjs.org/), or if you have existing code using them that you’d like to port over, you can import them normally like this:
```js ```js
import sinon from "sinon"; import sinon from 'sinon';
import { expect } from "chai"; import { expect } from 'chai';
``` ```
and then use them in your tests like you normally do. and then use them in your tests like you normally do.
### Initializing Test Environment ### Initializing Test Environment
> Note: this feature is available with `react-scripts@0.4.0` and higher. >Note: this feature is available with `react-scripts@0.4.0` and higher.
If your app uses a browser API that you need to mock in your tests or if you just need a global setup before running your tests, add a `src/setupTests.js` to your project. It will be automatically executed before running your tests. If your app uses a browser API that you need to mock in your tests or if you just need a global setup before running your tests, add a `src/setupTests.js` to your project. It will be automatically executed before running your tests.
For example: For example:
#### `src/setupTests.js` #### `src/setupTests.js`
```js ```js
const localStorageMock = { const localStorageMock = {
getItem: jest.fn(), getItem: jest.fn(),
setItem: jest.fn(), setItem: jest.fn(),
clear: jest.fn() clear: jest.fn()
}; };
global.localStorage = localStorageMock; global.localStorage = localStorageMock
``` ```
### Focusing and Excluding Tests ### Focusing and Excluding Tests
@@ -1080,12 +1079,10 @@ When creating a build of your application with `npm run build` linter warnings a
Popular CI servers already set the environment variable `CI` by default but you can do this yourself too: Popular CI servers already set the environment variable `CI` by default but you can do this yourself too:
### On CI servers ### On CI servers
#### Travis CI #### Travis CI
1. Following the [Travis Getting started](https://docs.travis-ci.com/user/getting-started/) guide for syncing your GitHub repository with Travis. You may need to initialize some settings manually in your [profile](https://travis-ci.org/profile) page. 1. Following the [Travis Getting started](https://docs.travis-ci.com/user/getting-started/) guide for syncing your GitHub repository with Travis. You may need to initialize some settings manually in your [profile](https://travis-ci.org/profile) page.
1. Add a `.travis.yml` file to your git repository. 1. Add a `.travis.yml` file to your git repository.
``` ```
language: node_js language: node_js
node_js: node_js:
@@ -1098,12 +1095,10 @@ script:
- npm test - npm test
- npm run build - npm run build
``` ```
1. Trigger your first build with a git push.
1. Trigger your first build with a git push. 1. [Customize your Travis CI Build](https://docs.travis-ci.com/user/customizing-the-build/) if needed.
1. [Customize your Travis CI Build](https://docs.travis-ci.com/user/customizing-the-build/) if needed.
### On your own environment ### On your own environment
##### Windows (cmd.exe) ##### Windows (cmd.exe)
```cmd ```cmd
@@ -1128,7 +1123,7 @@ CI=true npm run build
The test command will force Jest to run tests once instead of launching the watcher. The test command will force Jest to run tests once instead of launching the watcher.
> If you find yourself doing this often in development, please [file an issue](https://github.com/facebookincubator/create-react-app/issues/new) to tell us about your use case because we want to make watcher the best experience and are open to changing how it works to accommodate more workflows. > If you find yourself doing this often in development, please [file an issue](https://github.com/facebookincubator/create-react-app/issues/new) to tell us about your use case because we want to make watcher the best experience and are open to changing how it works to accommodate more workflows.
The build command will check for linter warnings and fail if any are found. The build command will check for linter warnings and fail if any are found.
@@ -1241,14 +1236,14 @@ You don’t necessarily need a static server in order to run a Create React App
Here’s a programmatic example using [Node](https://nodejs.org/) and [Express](http://expressjs.com/): Here’s a programmatic example using [Node](https://nodejs.org/) and [Express](http://expressjs.com/):
```javascript ```javascript
const express = require("express"); const express = require('express');
const path = require("path"); const path = require('path');
const app = express(); const app = express();
app.use(express.static("./build")); app.use(express.static('./build'));
app.get("/", function(req, res) { app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, "./build", "index.html")); res.sendFile(path.join(__dirname, './build', 'index.html'));
}); });
app.listen(9000); app.listen(9000);
@@ -1301,7 +1296,7 @@ This will let Create React App correctly infer the root path to use in the gener
#### Serving the Same Build from Different Paths #### Serving the Same Build from Different Paths
> Note: this feature is available with `react-scripts@0.9.0` and higher. >Note: this feature is available with `react-scripts@0.9.0` and higher.
If you are not using the HTML5 `pushState` history API or not using client-side routing at all, it is unnecessary to specify the URL from which your app will be served. Instead, you can put this in your `package.json`: If you are not using the HTML5 `pushState` history API or not using client-side routing at all, it is unnecessary to specify the URL from which your app will be served. Instead, you can put this in your `package.json`:
@@ -1378,7 +1373,7 @@ For more information see [Add Firebase to your JavaScript Project](https://fireb
### GitHub Pages ### GitHub Pages
> Note: this feature is available with `react-scripts@0.2.0` and higher. >Note: this feature is available with `react-scripts@0.2.0` and higher.
#### Step 1: Add `homepage` to `package.json` #### Step 1: Add `homepage` to `package.json`
@@ -1498,9 +1493,9 @@ Choose `build` as the path to deploy.
With this setup Netlify will build and deploy when you push to git or open a pull request: With this setup Netlify will build and deploy when you push to git or open a pull request:
1. [Start a new netlify project](https://app.netlify.com/signup) 1. [Start a new netlify project](https://app.netlify.com/signup)
2. Pick your Git hosting service and select your repository 2. Pick your Git hosting service and select your repository
3. Click `Build your site` 3. Click `Build your site`
**Support for client-side routing:** **Support for client-side routing:**
@@ -1516,22 +1511,22 @@ When you build the project, Create React App will place the `public` folder cont
[now](https://zeit.co/now) offers a zero-configuration single-command deployment. [now](https://zeit.co/now) offers a zero-configuration single-command deployment.
1. Install the `now` command-line tool either via the recommended [desktop tool](https://zeit.co/download) or via node with `npm install -g now`. 1. Install the `now` command-line tool either via the recommended [desktop tool](https://zeit.co/download) or via node with `npm install -g now`.
2. Install `serve` by running `npm install --save serve`. 2. Install `serve` by running `npm install --save serve`.
3. Add this line to `scripts` in `package.json`:
3. Add this line to `scripts` in `package.json`:
``` ```
"now-start": "serve build/", "now-start": "serve build/",
``` ```
4. Run `now` from your project directory. You will see a **now.sh** URL in your output like this: 4. Run `now` from your project directory. You will see a **now.sh** URL in your output like this:
``` ```
> Ready! https://your-project-dirname-tpspyhtdtk.now.sh (copied to clipboard) > Ready! https://your-project-dirname-tpspyhtdtk.now.sh (copied to clipboard)
``` ```
Paste that URL into your browser when the build is complete, and you will see your deployed app. Paste that URL into your browser when the build is complete, and you will see your deployed app.
Details are available in [this article.](https://zeit.co/blog/now-static) Details are available in [this article.](https://zeit.co/blog/now-static)
@@ -1542,7 +1537,7 @@ See this [blog post](https://medium.com/@omgwtfmarc/deploying-create-react-app-t
### Surge ### Surge
Install the Surge CLI if you haven’t already by running `npm install -g surge`. Run the `surge` command and log in you or create a new account. You just need to specify the _build_ folder and your custom domain, and you are done. Install the Surge CLI if you haven’t already by running `npm install -g surge`. Run the `surge` command and log in you or create a new account. You just need to specify the *build* folder and your custom domain, and you are done.
```sh ```sh
email: email@domain.com email: email@domain.com
@@ -1565,14 +1560,14 @@ Note that in order to support routers that use HTML5 `pushState` API, you may wa
You can adjust various development and production settings by setting environment variables in your shell or with [.env](#adding-development-environment-variables-in-env). You can adjust various development and production settings by setting environment variables in your shell or with [.env](#adding-development-environment-variables-in-env).
| Variable | Development | Production | Usage | Variable | Development | Production | Usage
| :--------- | :--------------------: | :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--- | :---: | :---: | :---
| BROWSER | :white_check_mark: | :x: | By default, Create React App will open the default system browser, favoring Chrome on macOS. Specify a [browser](https://github.com/sindresorhus/opn#app) to override this behavior, or set it to `none` to disable it completely. | BROWSER | :white_check_mark: | :x: | By default, Create React App will open the default system browser, favoring Chrome on macOS. Specify a [browser](https://github.com/sindresorhus/opn#app) to override this behavior, or set it to `none` to disable it completely.
| HOST | :white_check_mark: | :x: | By default, the development web server binds to `localhost`. You may use this variable to specify a different host. | HOST | :white_check_mark: | :x: | By default, the development web server binds to `localhost`. You may use this variable to specify a different host.
| PORT | :white_check_mark: | :x: | By default, the development web server will attempt to listen on port 3000 or prompt you to attempt the next available port. You may use this variable to specify a different port. | PORT | :white_check_mark: | :x: | By default, the development web server will attempt to listen on port 3000 or prompt you to attempt the next available port. You may use this variable to specify a different port.
| HTTPS | :white_check_mark: | :x: | When set to `true`, Create React App will run the development server in `https` mode. | HTTPS | :white_check_mark: | :x: | When set to `true`, Create React App will run the development server in `https` mode.
| PUBLIC_URL | :x: | :white_check_mark: | Create React App assumes your application is hosted at the serving web server's root or a subpath as specified in [`package.json` (`homepage`)](#building-for-relative-paths). Normally, Create React App ignores the hostname. You may use this variable to force assets to be referenced verbatim to the url you provide (hostname included). This may be particularly useful when using a CDN to host your application. | PUBLIC_URL | :x: | :white_check_mark: | Create React App assumes your application is hosted at the serving web server's root or a subpath as specified in [`package.json` (`homepage`)](#building-for-relative-paths). Normally, Create React App ignores the hostname. You may use this variable to force assets to be referenced verbatim to the url you provide (hostname included). This may be particularly useful when using a CDN to host your application.
| CI | :large_orange_diamond: | :white_check_mark: | When set to `true`, Create React App treats warnings as failures in the build. It also makes the test runner non-watching. Most CIs set this flag by default. | CI | :large_orange_diamond: | :white_check_mark: | When set to `true`, Create React App treats warnings as failures in the build. It also makes the test runner non-watching. Most CIs set this flag by default.
## Troubleshooting ## Troubleshooting
@@ -1612,7 +1607,7 @@ You can find [other installation methods](https://facebook.github.io/watchman/do
If this still doesn’t help, try running `launchctl unload -F ~/Library/LaunchAgents/com.github.facebook.watchman.plist`. If this still doesn’t help, try running `launchctl unload -F ~/Library/LaunchAgents/com.github.facebook.watchman.plist`.
There are also reports that _uninstalling_ Watchman fixes the issue. So if nothing else helps, remove it from your system and try again. There are also reports that *uninstalling* Watchman fixes the issue. So if nothing else helps, remove it from your system and try again.
### `npm run build` silently fails ### `npm run build` silently fails
+5 -15
View File
@@ -3,6 +3,8 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"react-scripts": "0.9.5",
"standard": "^10.0.2",
"autoprefixer": "^6.7.0", "autoprefixer": "^6.7.0",
"babel-cli": "6.14.0", "babel-cli": "6.14.0",
"babel-eslint": "6.1.2", "babel-eslint": "6.1.2",
@@ -10,15 +12,6 @@
"babel-preset-react": "6.11.1", "babel-preset-react": "6.11.1",
"babel-preset-stage-2": "6.13.0", "babel-preset-stage-2": "6.13.0",
"eslint": "^4.1.1", "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", "match-sorter": "^1.8.0",
"npm-run-all": "^3.1.1", "npm-run-all": "^3.1.1",
"onchange": "^3.0.2", "onchange": "^3.0.2",
@@ -28,10 +21,10 @@
"react-json-tree": "^0.10.9", "react-json-tree": "^0.10.9",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
"standard": "^10.0.2", "standard": "^10.0.2",
"stylus": "^0.54.5" "stylus": "^0.54.5",
"webpack": "^2.5.1"
}, },
"dependencies": { "dependencies": {
"eslint-config-react-tools": "^1.0.10",
"github-markdown-css": "^2.6.0", "github-markdown-css": "^2.6.0",
"marked": "^0.3.6", "marked": "^0.3.6",
"namor": "^1.0.1", "namor": "^1.0.1",
@@ -39,10 +32,7 @@
"react": "^15.5.4", "react": "^15.5.4",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-json-tree": "^0.10.9", "react-json-tree": "^0.10.9",
"react-script": "^2.0.5", "react-story": "^0.0.10"
"react-scripts": "^0.9.5",
"react-story": "^0.0.10",
"shortid": "^2.2.8"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
File diff suppressed because it is too large Load Diff
+27 -44
View File
@@ -1,33 +1,23 @@
/* eslint-disable */ import React from 'react'
import React from 'react';
// //
import ReactStory, { defaultProps } from 'react-story'; import ReactStory, { defaultProps } from 'react-story'
import CodeSandbox from './CodeSandbox.js'; import CodeSandbox from './CodeSandbox.js'
import './stories/utils/prism.css'; import './stories/utils/prism.css'
import '../../react-table.css'; import '../../react-table.css'
import Readme from './stories/Readme.js'; import Readme from './stories/Readme.js'
import HOCReadme from './stories/HOCReadme.js';
// import Test from './stories/test.js'
// import Tester from './examples/expander';
const stories = [ const stories = [
{ name: 'Readme', component: Readme }, { name: 'Readme', component: Readme },
{ name: 'HOC Readme', component: HOCReadme },
// { name: 'Tester', component: Test },
{ name: 'Simple Table', component: CodeSandbox('X6npLXPRW') }, { name: 'Simple Table', component: CodeSandbox('X6npLXPRW') },
{ {
name: 'Cell Renderers & Custom Components', name: 'Cell Renderers & Custom Components',
component: CodeSandbox('OyRL04Z4Y') component: CodeSandbox('OyRL04Z4Y'),
}, },
{ name: 'Default Sorting', component: CodeSandbox('gLwmmjzA3') }, { name: 'Default Sorting', component: CodeSandbox('gLwmmjzA3') },
{ {
name: 'Custom Sorting', name: 'Custom Sorting',
component: CodeSandbox('VGx67J35') component: CodeSandbox('VGx67J35'),
}, },
{ name: 'Custom Column Widths', component: CodeSandbox('o2OORXNXN') }, { name: 'Custom Column Widths', component: CodeSandbox('o2OORXNXN') },
{ name: 'Custom Component Props', component: CodeSandbox('nZW3L0wp4') }, { name: 'Custom Component Props', component: CodeSandbox('nZW3L0wp4') },
@@ -36,17 +26,17 @@ const stories = [
{ name: 'Pivoting & Aggregation', component: CodeSandbox('oNY9z8xN') }, { name: 'Pivoting & Aggregation', component: CodeSandbox('oNY9z8xN') },
{ {
name: 'Pivoting & Aggregation w/ Sub Components', name: 'Pivoting & Aggregation w/ Sub Components',
component: CodeSandbox('p0kEVBgQ') component: CodeSandbox('p0kEVBgQ'),
}, },
{ {
name: '100k Rows w/ Pivoting & Sub Components', name: '100k Rows w/ Pivoting & Sub Components',
component: CodeSandbox('DRmKj0XyK') component: CodeSandbox('DRmKj0XyK'),
}, },
{ name: 'Pivoting Options', component: CodeSandbox('kZKmNBK6r') }, { name: 'Pivoting Options', component: CodeSandbox('kZKmNBK6r') },
{ name: 'Functional Rendering', component: CodeSandbox('VPZ0Bzv8X') }, { name: 'Functional Rendering', component: CodeSandbox('VPZ0Bzv8X') },
{ {
name: 'Custom Expander Position', name: 'Custom Expander Position',
component: CodeSandbox('1jj2XrPEV') component: CodeSandbox('1jj2XrPEV'),
}, },
{ name: 'Custom "No Data" Text', component: CodeSandbox('RgRpRDv80') }, { name: 'Custom "No Data" Text', component: CodeSandbox('RgRpRDv80') },
{ name: 'Footers', component: CodeSandbox('KOqQXn3p8') }, { name: 'Footers', component: CodeSandbox('KOqQXn3p8') },
@@ -55,52 +45,46 @@ const stories = [
{ name: 'Editable Table', component: CodeSandbox('n5r19gzQP') }, { name: 'Editable Table', component: CodeSandbox('n5r19gzQP') },
{ {
name: 'Fixed Header w/ Vertical Scroll', name: 'Fixed Header w/ Vertical Scroll',
component: CodeSandbox('7LY0gjA8O') component: CodeSandbox('7LY0gjA8O'),
}, },
{ {
name: 'Multiple Pagers (Top and Bottom)', name: 'Multiple Pagers (Top and Bottom)',
component: CodeSandbox('VEZ8OgvX') component: CodeSandbox('VEZ8OgvX'),
}, },
]
{ name: 'Tree Table (HOC)', component: CodeSandbox('lxmr4wynzq') },
{ name: 'Select Table (HOC)', component: CodeSandbox('7yq5ylw09j') },
{ name: 'Select Tree Table (HOC)', component: CodeSandbox('2p7jp4klwp') },
{ name: 'Foldable Table (HOC)', component: CodeSandbox('8pkrj5yorl') },
{ name: 'Advanced Expand Table (HOC)', component: CodeSandbox('y2m39jz8v1') }
];
export default class App extends React.Component { export default class App extends React.Component {
render() { render () {
return ( return (
<ReactStory <ReactStory
style={{ style={{
display: 'block', display: 'block',
width: '100%', width: '100%',
height: '100%' height: '100%',
}} }}
pathPrefix="story/" pathPrefix='story/'
StoryWrapper={props => ( StoryWrapper={props =>
<defaultProps.StoryWrapper <defaultProps.StoryWrapper
css={{ css={{
padding: 0, padding: 0,
display: 'flex', display: 'flex',
flexDirection: 'column' flexDirection: 'column',
}} }}
> >
<a <a
href="//github.com/react-tools/react-table" href='//github.com/react-tools/react-table'
style={{ style={{
display: 'block', display: 'block',
textAlign: 'center', textAlign: 'center',
borderBottom: 'solid 3px #cccccc' borderBottom: 'solid 3px #cccccc',
}} }}
> >
<img <img
src="https://github.com/react-tools/media/raw/master/logo-react-table.png" src='https://github.com/react-tools/media/raw/master/logo-react-table.png'
alt="React Table Logo" alt='React Table Logo'
style={{ style={{
width: '150px', width: '150px',
padding: '10px' padding: '10px',
}} }}
/> />
</a> </a>
@@ -108,13 +92,12 @@ export default class App extends React.Component {
{...props} {...props}
style={{ style={{
flex: '1 0 auto', flex: '1 0 auto',
position: 'relative' position: 'relative',
}} }}
/> />
</defaultProps.StoryWrapper> </defaultProps.StoryWrapper>}
)}
stories={stories} stories={stories}
/> />
); )
} }
} }
+10 -9
View File
@@ -1,21 +1,22 @@
import React from "react"; import React from 'react'
export default id => () => { export default id => () => {
return ( return (
<iframe <iframe
src={`https://codesandbox.io/embed/${id}?autoresize=1&hidenavigation=1&view=${ src={`https://codesandbox.io/embed/${id}?autoresize=1&hidenavigation=1&view=${global.innerWidth <
global.innerWidth < 1000 ? "preview" : "split" 1000
}`} ? 'preview'
: 'split'}`}
style={{ style={{
position: "absolute", position: 'absolute',
top: 0, top: 0,
left: 0, left: 0,
width: `100%`, width: `100%`,
height: `100%`, height: `100%`,
border: 0, border: 0,
overflow: `hidden` overflow: `hidden`,
}} }}
sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin" sandbox='allow-modals allow-forms allow-popups allow-scripts allow-same-origin'
/> />
); )
}; }
@@ -1,116 +0,0 @@
import React from 'react';
import { render } from 'react-dom';
import { makeData, Logo, Tips } from './Utils';
import { advancedExpandTableHOC } from '../../../../lib/hoc/advancedExpandTable';
// Import React Table
import ReactTable from 'react-table';
import 'react-table/react-table.css';
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Cell: props => {
const {
// react table props
columnProps: { rest: { showRowSubComponent } },
nestingPath
} = props;
return (
<div>
<button
onClick={e => showRowSubComponent({ nestingPath: nestingPath }, e)}
>
SHOW SUBCOMPONENT {props.value}
</button>
</div>
);
}
},
{
Header: 'Last Name',
id: 'lastName',
accessor: d => d.lastName
},
{
Header: 'Info',
columns: [
{
Header: 'Age',
accessor: 'age',
Cell: props => {
const {
// react table props
columnProps: { rest: { toggleRowSubComponent } },
nestingPath
} = props;
return (
<div>
<button
onClick={e =>
toggleRowSubComponent({ nestingPath: nestingPath }, e)
}
>
TOGGLE SUBCOMPONENT {props.value}
</button>
</div>
);
}
},
{
Header: 'Status',
accessor: 'status'
}
]
},
{
Header: 'Stats',
columns: [
{
Header: 'Visits',
accessor: 'visits'
}
]
}
];
const AdvancedExpandReactTable = advancedExpandTableHOC(ReactTable);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: makeData()
};
}
render() {
const { data } = this.state;
return (
<div>
<AdvancedExpandReactTable
data={data}
columns={columns}
defaultPageSize={10}
className='-striped -highlight'
SubComponent={({ row, nestingPath, toggleRowSubComponent }) => {
return (
<div style={{ padding: '20px' }}>
<button
onClick={e => toggleRowSubComponent({ nestingPath }, e)}
>
CLOSE SUBCOMPONENT {row.firstName} {row.lastName}
</button>
</div>
);
}}
/>
<br />
<Tips />
<Logo />
</div>
);
}
}
render(<App />, document.getElementById('root'));
-94
View File
@@ -1,94 +0,0 @@
import React from "react";
export default Component => {
const wrapper = class RTCheckboxTable extends React.Component {
// we only need a Component so we can get the 'ref' - pure components can't get a 'ref'
rowSelector = row => {
if (!row || !row.hasOwnProperty(this.props.keyField)) return null;
const checked = this.props.isSelected(row[this.props.keyField]);
return (
<input
type="checkbox"
checked={checked}
onClick={e => {
const { shiftKey } = e;
e.stopPropagation();
this.props.toggleSelection(row[this.props.keyField], shiftKey, row);
}}
onChange={() => {}}
value=""
/>
);
};
headSelector = row => {
const checked = this.props.selectAll;
return (
<input
type="checkbox"
checked={checked}
onClick={e => {
e.stopPropagation();
this.props.toggleAll();
}}
onChange={() => {}}
value=""
/>
);
};
// this is so we can expose the underlying ReactTable to get at the sortedData for selectAll
getWrappedInstance = () => this.wrappedInstance;
render() {
const {
columns: originalCols,
isSelected,
toggleSelection,
toggleAll,
keyField,
selectAll,
...rest
} = this.props;
const { rowSelector, headSelector } = this;
const select = {
id: "_selector",
accessor: () => "x", // this value is not important
Header: headSelector,
Cell: ci => {
return rowSelector(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 = "RTCheckboxTable";
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.");
}
};
return wrapper;
};
-148
View File
@@ -1,148 +0,0 @@
import React from "react";
import shortid from "shortid";
import ReactTable from "../../../../lib/index";
import "../../../../react-table.css";
import checkboxTableHOC from "./checkboxHOC";
const CheckboxTable = checkboxTableHOC(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
};
}
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
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.checkboxTable.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 => {
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);
};
render() {
const { toggleSelection, toggleAll, isSelected, logSelection } = this;
const { data, columns, selectAll } = this.state;
const extraProps = {
selectAll,
isSelected,
toggleAll,
toggleSelection
};
return (
<div style={{ padding: "10px" }}>
<h1>react-table - Checkbox Table</h1>
<button onClick={logSelection}>Log Selection to Console</button>
{` (${this.state.selection.length}) selected`}
{data ? (
<CheckboxTable
data={data}
columns={columns}
ref={r => (this.checkboxTable = r)}
className="-striped -highlight"
{...extraProps}
/>
) : null}
</div>
);
}
}
// export default treeTableHOC(ComponentTest);
export default ComponentTest;
-60
View File
@@ -1,60 +0,0 @@
import React from "react";
export default class ComponentTest extends React.component {
render() {
return <div>Bozo</div>;
}
}
// import ReactTable from '../../../../lib/index'
// import '../../../../react-table.css'
//
// console.log('ReactTable:',ReactTable)
//
// 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' : ''}`}>
// &bull;
// </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, closeExpanded)=><ExpanderComponent {...props, closeExpanded} />,
// // SubComponent: (props)=><SubComponent {...props} />,
// // multiSort: false,
// }
// return (
// <ReactTable
// {...rtProps}
// />
// )
// }
// }
@@ -1,110 +0,0 @@
import React from 'react'
import ReactTable from '../../../../lib/index'
import FoldableTableHOC from '../../../../lib/hoc/foldableTable'
import selectTableHOC from '../../../../lib/hoc/selectTable'
const FoldableTable = FoldableTableHOC(selectTableHOC(ReactTable))
export default class FoldableTableCustomState extends React.Component {
constructor(props, context) {
super(props, context)
this.state = { folded: {}, seleted: {}, selectedAll: false }
}
getData = () => [{
id: 1,
first_name: 'Jeanette',
'last_name': 'Penddreth',
email: 'jpenddreth0@census.gov',
'gender': 'Female',
ip_address: '26.58.193.2',
}, {
'id': 2,
'first_name': 'Giavani',
last_name: 'Frediani',
'email': 'gfrediani1@senate.gov',
'gender': 'Male',
'ip_address': '229.179.4.212',
}, {
'id': 3,
first_name: 'Noell',
last_name: 'Bea',
'email': 'nbea2@imageshack.us',
gender: 'Female',
ip_address: '180.66.162.255',
}, {
'id': 4,
'first_name': 'Willard',
'last_name': 'Valek',
email: 'wvalek3@vk.com',
'gender': 'Male',
ip_address: '67.76.188.26',
}];
toggleSelection = (key, shift, row) => {
const { selected } = this.state
let newSelected = Object.assign({}, selected)
newSelected[key] = !newSelected[key]
this.setState(p => ({ selected: newSelected }))
};
toggleAll = () => {
const { selectedAll } = this.state
if (selectedAll) { this.setState(p => { return { selectedAll: false, seleted: {} } }); }
else {
const data = this.getData()
let newSelected = {}
data.forEach(d => newSelected[d.id] = true)
this.setState(p => ({ selectedAll: true, seleted: newSelected }))
}
}
isSelected = key => this.state.seleted[key];
render() {
return (<FoldableTable
//SelectTable props
keyField='id'
toggleSelection={this.toggleSelection}
toggleAll={this.toggleAll}
isSelected={this.isSelected}
selectAll={this.state.selectedAll}
selectType='checkbox'
onFoldChange={newFolded => this.setState(p => { return { folded: newFolded } })}
folded={this.state.folded}
data={this.getData()}
columns={[{
Header: "Name",
foldable: true,
columns: [
{
Header: "First Name",
accessor: "first_name"
},
{
Header: "Last Name",
accessor: "last_name"
}
]
}, {
Header: "Info",
foldable: true,
columns: [
{
Header: "Email",
accessor: "email"
},
{
Header: "Gender",
accessor: "gender"
}
]
}]
}></FoldableTable>)
}
}
@@ -1,71 +0,0 @@
import React from 'react'
import ReactTable from '../../../../lib/index'
import FoldableTableHOC from '../../../../lib/hoc/foldableTable'
const FoldableTable = FoldableTableHOC(ReactTable)
export default class FoldableTableWithHeader extends React.Component {
getData = () => [{
id: 1,
first_name: 'Jeanette',
last_name: 'Penddreth',
'email': 'jpenddreth0@census.gov',
gender: 'Female',
'ip_address': '26.58.193.2',
}, {
'id': 2,
first_name: 'Giavani',
last_name: 'Frediani',
email: 'gfrediani1@senate.gov',
gender: 'Male',
'ip_address': '229.179.4.212',
}, {
'id': 3,
'first_name': 'Noell',
last_name: 'Bea',
'email': 'nbea2@imageshack.us',
'gender': 'Female',
ip_address: '180.66.162.255',
}, {
'id': 4,
'first_name': 'Willard',
'last_name': 'Valek',
email: 'wvalek3@vk.com',
'gender': 'Male',
ip_address: '67.76.188.26',
}];
render() {
return (<FoldableTable
data={this.getData()}
columns={[{
Header: "Name",
foldable: true,
columns: [
{
Header: "First Name",
accessor: "first_name"
},
{
Header: "Last Name",
accessor: "last_name"
}
]
}, {
Header: "Info",
foldable: true,
columns: [
{
Header: "Email",
accessor: "email"
},
{
Header: "Gender",
accessor: "gender"
}
]
}]
}></FoldableTable>)
}
}
@@ -1,64 +0,0 @@
import React from 'react';
import ReactTable from '../../../../lib/index';
import FoldableTableHOC from '../../../../lib/hoc/foldableTable';
const FoldableTable = FoldableTableHOC(ReactTable);
export default class FoldableTableWithoutHeader extends React.Component {
getData = () => [{
"id": 1,
"first_name": "Jeanette",
"last_name": "Penddreth",
"email": "jpenddreth0@census.gov",
"gender": "Female",
"ip_address": "26.58.193.2"
}, {
"id": 2,
"first_name": "Giavani",
"last_name": "Frediani",
"email": "gfrediani1@senate.gov",
"gender": "Male",
"ip_address": "229.179.4.212"
}, {
"id": 3,
"first_name": "Noell",
"last_name": "Bea",
"email": "nbea2@imageshack.us",
"gender": "Female",
"ip_address": "180.66.162.255"
}, {
"id": 4,
"first_name": "Willard",
"last_name": "Valek",
"email": "wvalek3@vk.com",
"gender": "Male",
"ip_address": "67.76.188.26"
}];
render() {
return <FoldableTable
data={this.getData()}
columns={[
{
Header: "First Name",
accessor: "first_name"
},
{
Header: "Last Name",
accessor: "last_name",
foldable: true,
},
{
Header: "Email",
accessor: "email",
foldable: true,
},
{
Header: "Gender",
accessor: "gender",
foldable: true,
}
]}></FoldableTable>
}
}
-30
View File
@@ -1,30 +0,0 @@
import React from 'react'
import '../../../../react-table.css'
import FoldableTableWithHeader from './FoldableTableWithHeader'
import FoldableTableWithoutHeader from './FoldableTableWithoutHeader'
import FoldableTableCustomState from './FoldableTableCustomState'
class FaldableComponentTest extends React.Component {
render() {
return (
<div>
<p>- Sample With Header Columns</p>
<br />
<FoldableTableWithHeader />
<br /> <br />
<p>- Sample With Normal Columns</p>
<br />
<FoldableTableWithoutHeader />
<br /> <br />
<p>- Custom State and selectedTable</p>
<br />
<FoldableTableCustomState />
</div >
)
}
}
export default FaldableComponentTest
-13
View File
@@ -1,13 +0,0 @@
/* eslint-disable */
import TreeTable from './treetable'
import SelectTable from './selecttable'
import SelectTreeTable from './selecttreetable'
import FoldableTable from './foldabletable';
export {
TreeTable,
SelectTable,
SelectTreeTable,
FoldableTable
}
-173
View File
@@ -1,173 +0,0 @@
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;
-238
View File
@@ -1,238 +0,0 @@
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;
-70
View File
@@ -1,70 +0,0 @@
import React from "react";
import ReactTable from "../../../../lib/index";
import "../../../../react-table.css";
import treeTableHOC from "../../../../lib/hoc/treeTable";
async function getData() {
const result = await (await fetch("/au_500_tree.json")).json();
return result;
}
function getColumns(data, pivotBy) {
const columns = [];
const sample = data[0];
for (let key in sample) {
columns.push({
accessor: key,
Header: key
});
}
return columns;
}
export class ComponentTest extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
columns: null,
pivotBy: null // ["firstName", "lastName"],
};
}
componentDidMount() {
getData().then(data => {
// console.log('cwm data:',data);
const pivotBy = ["state", "post", "city"];
const columns = getColumns(data, pivotBy);
// console.log('cwm cols:',columns);
this.setState({ data, columns, pivotBy });
});
}
showState = () => {
console.log("state:", this.reactTable.getResolvedState());
};
render() {
const { data, columns, pivotBy } = this.state;
const extraProps = {
data,
columns,
pivotBy
};
const TreeTable = treeTableHOC(ReactTable);
return (
<div style={{ padding: "10px" }}>
<h1>react-table - Tree Table</h1>
{data ? (
<TreeTable
ref={r => (this.reactTable = r)}
className="-striped -highlight"
defaultPageSize={5}
{...extraProps}
/>
) : null}
</div>
);
}
}
export default ComponentTest;
@@ -1,53 +0,0 @@
import React from "react";
export default Component => {
const wrapper = componentProps => {
const 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 = `${componentProps.treeTableIndent *
ri.level}px`;
cell.props.style.backgroundColor = "#DDD";
cell.props.style.borderBottom = "1px solid rgba(128,128,128,0.2)";
return <div {...rest}>{cell}</div>;
}
return <Component.defaultProps.TrComponent {...rest} />;
};
const getTrProps = (state, ri, ci, instance) => {
return { ri };
};
const { columns, ...rest } = componentProps;
const extra = {
columns: columns.map(col => {
let column = col;
if (rest.pivotBy && rest.pivotBy.includes(col.accessor)) {
column = {
accessor: col.accessor,
width: `${componentProps.treeTableIndent}px`,
show: false,
Header: ""
};
}
return column;
}),
TrComponent,
getTrProps
};
return <Component {...rest} {...extra} />;
};
wrapper.displayName = "RTTreeTable";
wrapper.defaultProps = {
treeTableRowBackground: "#EEE",
treeTableIndent: 10
};
return wrapper;
};
-35
View File
@@ -1,35 +0,0 @@
import namor from "namor";
const range = len => {
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(i);
}
return arr;
};
const newPerson = () => {
const statusChance = Math.random();
return {
firstName: namor.generate({ words: 1, numbers: 0 }),
lastName: namor.generate({ words: 1, numbers: 0 }),
age: Math.floor(Math.random() * 30),
visits: Math.floor(Math.random() * 100),
progress: Math.floor(Math.random() * 100),
status:
statusChance > 0.66
? "relationship"
: statusChance > 0.33
? "complicated"
: "single"
};
};
export function makeData(len = 5553) {
return range(len).map(d => {
return {
...newPerson(),
children: range(10).map(newPerson)
};
});
}
+2 -4
View File
@@ -1,7 +1,5 @@
html, html, body, #root {
body, height: 100%
#root {
height: 100%;
} }
body { body {
margin: 0; margin: 0;
+8 -5
View File
@@ -1,7 +1,10 @@
import React from "react"; import React from 'react'
import ReactDOM from "react-dom"; import ReactDOM from 'react-dom'
// //
import "./index.css"; import './index.css'
import App from "./App.js"; import App from './App.js'
ReactDOM.render(<App />, document.getElementById("root")); ReactDOM.render(
<App />,
document.getElementById('root')
)
-23
View File
@@ -1,23 +0,0 @@
/* 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();
}
}
+11 -11
View File
@@ -1,23 +1,23 @@
/* eslint-disable import/no-webpack-loader-syntax */ /* eslint-disable import/no-webpack-loader-syntax */
import React from "react"; import React from 'react'
import marked from "marked"; import marked from 'marked'
// //
import Readme from "!raw!../../../README.md"; import Readme from '!raw!../../../README.md'
import "github-markdown-css/github-markdown.css"; import 'github-markdown-css/github-markdown.css'
import "./utils/prism.js"; import './utils/prism.js'
export default class Story extends React.Component { export default class Story extends React.Component {
render() { render () {
return ( return (
<div style={{ padding: "10px" }}> <div style={{ padding: '10px' }}>
<span <span
className="markdown-body" className='markdown-body'
dangerouslySetInnerHTML={{ __html: marked(Readme) }} dangerouslySetInnerHTML={{ __html: marked(Readme) }}
/> />
</div> </div>
); )
} }
componentDidMount() { componentDidMount () {
global.Prism && global.Prism.highlightAll(); global.Prism && global.Prism.highlightAll()
} }
} }
-27
View File
@@ -1,27 +0,0 @@
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" }
];
export default class Story extends React.Component {
render() {
return (
<div>
Test
<ReactTable data={data} columns={columns} />
</div>
);
}
}
+764 -1113
View File
File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

-8348
View File
File diff suppressed because it is too large Load Diff
+22 -86
View File
@@ -1,6 +1,6 @@
{ {
"name": "react-table", "name": "react-table",
"version": "6.9.2", "version": "6.5.3",
"description": "A fast, lightweight, opinionated table and datagrid built on React", "description": "A fast, lightweight, opinionated table and datagrid built on React",
"license": "MIT", "license": "MIT",
"homepage": "https://github.com/react-tools/react-table#readme", "homepage": "https://github.com/react-tools/react-table#readme",
@@ -15,123 +15,59 @@
"datagrid" "datagrid"
], ],
"main": "lib/index.js", "main": "lib/index.js",
"module": "es/index.js",
"files": [ "files": [
"src/", "src/",
"es/",
"lib/", "lib/",
"react-table.js", "react-table.js",
"react-table.min.js",
"react-table.css", "react-table.css",
"media/*.png" "media/*.png"
], ],
"scripts": { "scripts": {
"build:cjs": "babel src --out-dir lib --source-maps inline --copy-files", "build:node": "babel src --out-dir lib --source-maps inline",
"build:es": "cross-env BABEL_ENV=es babel src --out-dir es --source-maps inline", "build:css": "rimraf react-table.css && stylus src/index.styl --compress -o react-table.css && yarn run css:autoprefix",
"build:umd": "cross-env BABEL_ENV=umd rollup -c",
"build:umd-min": "cross-env BABEL_ENV=umd NODE_ENV=production rollup -c",
"build:css": "rimraf react-table.css && stylus src/index.styl --compress -o react-table.css && yarn css:autoprefix",
"css:autoprefix": "postcss --use autoprefixer react-table.css -r", "css:autoprefix": "postcss --use autoprefixer react-table.css -r",
"watch": "npm-run-all --parallel watch:*", "watch": "npm-run-all --parallel watch:*",
"watch:js": "onchange 'src/**/*.js' -i -- yarn build:cjs", "watch:node": "onchange 'src/**/*.js' -i -- npm run build:node",
"watch:css": "onchange 'src/**/*.styl' -i -- yarn build:css", "watch:css": "onchange 'src/**/*.styl' -i -- npm run build:css",
"test": "eslint src", "test": "eslint src",
"umd": "rimraf react-table.js && webpack --config umd.webpack.js",
"build": "npm-run-all build:*", "build": "npm-run-all build:*",
"prepublish": "yarn build", "prepublish": "npm run build && npm run umd",
"postpublish": "git push --tags", "postpublish": "git push --tags",
"docs": "yarn watch & cd docs && yarn && yarn start", "docs": "yarn watch & cd docs && yarn && yarn start",
"docs:build": "yarn build && cd docs && yarn && yarn build" "docs:build": "yarn build && cd docs && yarn && yarn run build"
}, },
"dependencies": { "dependencies": {
"classnames": "^2.2.5" "classnames": "^2.2.5"
}, },
"peerDependencies": { "peerDependencies": {
"prop-types": "^15.5.0", "react": "^15.x.x || ^16.x.x"
"react": "^15.x.x || ^16.x.x",
"react-dom": "^15.x.x || ^16.x.x"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "^6.7.0", "autoprefixer": "^6.7.0",
"babel-cli": "6.14.0", "babel-cli": "6.14.0",
"babel-eslint": "6.1.2", "babel-eslint": "6.1.2",
"babel-plugin-external-helpers": "^6.22.0", "babel-preset-es2015": "6.14.0",
"babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-react": "6.11.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-preset-stage-2": "6.13.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"cross-env": "^5.1.4",
"eslint": "^4.1.1", "eslint": "^4.1.1",
"eslint-config-react-tools": "^1.0.10",
"eslint-plugin-class-property": "^1.0.6",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^7.4.0",
"match-sorter": "^1.8.0", "match-sorter": "^1.8.0",
"npm-run-all": "^4.1.2", "npm-run-all": "^3.1.1",
"onchange": "^3.0.2", "onchange": "^3.0.2",
"postcss-cli": "^2.6.0", "postcss-cli": "^2.6.0",
"prop-types": "^15.6.0", "react": "^15.4.2",
"react": "^16.3.2", "react-dom": "^15.4.2",
"react-dom": "^16.3.2",
"react-json-tree": "^0.10.9", "react-json-tree": "^0.10.9",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
"rollup": "^0.55.3",
"rollup-plugin-babel": "^3.0.3",
"rollup-plugin-commonjs": "^8.3.0",
"rollup-plugin-node-resolve": "^3.0.2",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-uglify": "^3.0.0",
"standard": "^10.0.2", "standard": "^10.0.2",
"stylus": "^0.54.5" "stylus": "^0.54.5",
"webpack": "^2.5.1"
}, },
"babel": { "babel": {
"env": { "presets": [
"development": { "es2015",
"presets": [ "stage-2",
[ "react"
"env", ]
{
"modules": "commonjs"
}
],
"react"
],
"plugins": [
"transform-object-rest-spread",
"transform-class-properties"
]
},
"es": {
"presets": [
[
"env",
{
"modules": false
}
],
"react"
],
"plugins": [
"transform-object-rest-spread",
"transform-class-properties"
]
},
"umd": {
"presets": [
[
"env",
{
"modules": false
}
],
"react"
],
"plugins": [
"transform-object-rest-spread",
"transform-class-properties",
"external-helpers"
]
}
}
} }
} }
-42
View File
@@ -1,42 +0,0 @@
import nodeResolve from 'rollup-plugin-node-resolve'
import babel from 'rollup-plugin-babel'
import replace from 'rollup-plugin-replace'
import commonjs from 'rollup-plugin-commonjs'
import uglify from 'rollup-plugin-uglify'
const env = process.env.NODE_ENV
const config = {
input: 'src/index.js',
output: {
file: env === 'production' ? 'react-table.min.js' : 'react-table.js',
format: 'umd',
globals: {
react: 'React',
},
name: 'ReactTable',
exports: 'named',
},
external: ['react'],
plugins: [
nodeResolve(),
babel({
exclude: '**/node_modules/**',
}),
replace({
'process.env.NODE_ENV': JSON.stringify(env),
}),
commonjs(),
],
}
if (env === 'production') {
config.plugins.push(uglify({
compress: {
dead_code: true,
warnings: false,
},
}))
}
export default config
+52 -79
View File
@@ -9,14 +9,12 @@ const emptyObj = () => ({})
export default { export default {
// General // General
data: [], data: [],
resolveData: data => data,
loading: false, loading: false,
showPagination: true, showPagination: true,
showPaginationTop: false, showPaginationTop: false,
showPaginationBottom: true, showPaginationBottom: true,
showPageSizeOptions: true, showPageSizeOptions: true,
pageSizeOptions: [5, 10, 20, 25, 50, 100], pageSizeOptions: [5, 10, 20, 25, 50, 100],
defaultPage: 0,
defaultPageSize: 20, defaultPageSize: 20,
showPageJump: true, showPageJump: true,
collapseOnSortingChange: true, collapseOnSortingChange: true,
@@ -24,7 +22,6 @@ export default {
collapseOnDataChange: true, collapseOnDataChange: true,
freezeWhenExpanded: false, freezeWhenExpanded: false,
sortable: true, sortable: true,
multiSort: true,
resizable: true, resizable: true,
filterable: false, filterable: false,
defaultSortDesc: false, defaultSortDesc: false,
@@ -32,13 +29,13 @@ export default {
defaultFiltered: [], defaultFiltered: [],
defaultResized: [], defaultResized: [],
defaultExpanded: {}, defaultExpanded: {},
// eslint-disable-next-line no-unused-vars
defaultFilterMethod: (filter, row, column) => { defaultFilterMethod: (filter, row, column) => {
const id = filter.pivotId || filter.id const id = filter.pivotId || filter.id
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true return row[id] !== undefined
? String(row[id]).startsWith(filter.value)
: true
}, },
// eslint-disable-next-line no-unused-vars defaultSortMethod: (a, b) => {
defaultSortMethod: (a, b, desc) => {
// force null and undefined to the bottom // force null and undefined to the bottom
a = a === null || a === undefined ? '' : a a = a === null || a === undefined ? '' : a
b = b === null || b === undefined ? '' : b b = b === null || b === undefined ? '' : b
@@ -52,8 +49,7 @@ export default {
if (a < b) { if (a < b) {
return -1 return -1
} }
// returning 0, undefined or any falsey value will use subsequent sorts or // returning 0, undefined or any falsey value will use subsequent sorts or the index as a tiebreaker
// the index as a tiebreaker
return 0 return 0
}, },
@@ -128,14 +124,12 @@ export default {
PivotValue: undefined, PivotValue: undefined,
Expander: undefined, Expander: undefined,
Filter: undefined, Filter: undefined,
Placeholder: undefined,
// All Columns // All Columns
sortable: undefined, // use table default sortable: undefined, // use table default
resizable: undefined, // use table default resizable: undefined, // use table default
filterable: undefined, // use table default filterable: undefined, // use table default
show: true, show: true,
minWidth: 100, minWidth: 100,
minResizeWidth: 11,
// Cells only // Cells only
className: '', className: '',
style: {}, style: {},
@@ -175,95 +169,74 @@ export default {
pageText: 'Page', pageText: 'Page',
ofText: 'of', ofText: 'of',
rowsText: 'rows', rowsText: 'rows',
pageJumpText: 'jump to page',
rowsSelectorText: 'rows per page',
// Components // Components
TableComponent: ({ children, className, ...rest }) => ( TableComponent: _.makeTemplateComponent('rt-table', 'Table'),
<div
className={classnames('rt-table', className)}
role="grid"
// tabIndex='0'
{...rest}
>
{children}
</div>
),
TheadComponent: _.makeTemplateComponent('rt-thead', 'Thead'), TheadComponent: _.makeTemplateComponent('rt-thead', 'Thead'),
TbodyComponent: _.makeTemplateComponent('rt-tbody', 'Tbody'), TbodyComponent: _.makeTemplateComponent('rt-tbody', 'Tbody'),
TrGroupComponent: ({ children, className, ...rest }) => ( TrGroupComponent: _.makeTemplateComponent('rt-tr-group', 'TrGroup'),
<div className={classnames('rt-tr-group', className)} role="rowgroup" {...rest}> TrComponent: _.makeTemplateComponent('rt-tr', 'Tr'),
{children} ThComponent: ({ toggleSort, className, children, ...rest }) => {
</div> return (
), <div
TrComponent: ({ children, className, ...rest }) => ( className={classnames(className, 'rt-th')}
<div className={classnames('rt-tr', className)} role="row" {...rest}> onClick={e => {
{children} toggleSort && toggleSort(e)
</div> }}
), {...rest}
ThComponent: ({ >
toggleSort, className, children, ...rest {children}
}) => ( </div>
// eslint-disable-next-line jsx-a11y/click-events-have-key-events )
<div },
className={classnames('rt-th', className)} TdComponent: _.makeTemplateComponent('rt-td', 'Td'),
onClick={e => toggleSort && toggleSort(e)}
role="columnheader"
tabIndex="-1" // Resolves eslint issues without implementing keyboard navigation incorrectly
{...rest}
>
{children}
</div>
),
TdComponent: ({
toggleSort, className, children, ...rest
}) => (
<div className={classnames('rt-td', className)} role="gridcell" {...rest}>
{children}
</div>
),
TfootComponent: _.makeTemplateComponent('rt-tfoot', 'Tfoot'), TfootComponent: _.makeTemplateComponent('rt-tfoot', 'Tfoot'),
FilterComponent: ({ filter, onChange, column }) => ( FilterComponent: ({ filter, onChange }) =>
<input <input
type="text" type='text'
style={{ style={{
width: '100%', width: '100%',
}} }}
placeholder={column.Placeholder}
value={filter ? filter.value : ''} value={filter ? filter.value : ''}
onChange={event => onChange(event.target.value)} onChange={event => onChange(event.target.value)}
/> />,
), ExpanderComponent: ({ isExpanded }) =>
ExpanderComponent: ({ isExpanded }) => ( <div className={classnames('rt-expander', isExpanded && '-open')}>
<div className={classnames('rt-expander', isExpanded && '-open')}>&bull;</div> &bull;
), </div>,
PivotValueComponent: ({ subRows, value }) => ( PivotValueComponent: ({ subRows, value }) =>
<span> <span>
{value} {subRows && `(${subRows.length})`} {value} {subRows && `(${subRows.length})`}
</span> </span>,
),
AggregatedComponent: ({ subRows, column }) => { AggregatedComponent: ({ subRows, column }) => {
const previewValues = subRows.filter(d => typeof d[column.id] !== 'undefined').map((row, i) => ( const previewValues = subRows
// eslint-disable-next-line react/no-array-index-key .filter(d => typeof d[column.id] !== 'undefined')
<span key={i}> .map((row, i) =>
{row[column.id]} <span key={i}>
{i < subRows.length - 1 ? ', ' : ''} {row[column.id]}
{i < subRows.length - 1 ? ', ' : ''}
</span>
)
return (
<span>
{previewValues}
</span> </span>
)) )
return <span>{previewValues}</span>
}, },
PivotComponent: undefined, // this is a computed default generated using PivotComponent: undefined, // this is a computed default generated using
// the ExpanderComponent and PivotValueComponent at run-time in methods.js // the ExpanderComponent and PivotValueComponent at run-time in methods.js
PaginationComponent: Pagination, PaginationComponent: Pagination,
PreviousComponent: undefined, PreviousComponent: undefined,
NextComponent: undefined, NextComponent: undefined,
LoadingComponent: ({ LoadingComponent: ({ className, loading, loadingText, ...rest }) =>
className, loading, loadingText, ...rest <div
}) => ( className={classnames('-loading', { '-active': loading }, className)}
<div className={classnames('-loading', { '-active': loading }, className)} {...rest}> {...rest}
<div className="-loading-inner">{loadingText}</div> >
</div> <div className='-loading-inner'>
), {loadingText}
</div>
</div>,
NoDataComponent: _.makeTemplateComponent('rt-noData', 'NoData'), NoDataComponent: _.makeTemplateComponent('rt-noData', 'NoData'),
ResizerComponent: _.makeTemplateComponent('rt-resizer', 'Resizer'), ResizerComponent: _.makeTemplateComponent('rt-resizer', 'Resizer'),
PadRowComponent: () => <span>&nbsp;</span>, PadRowComponent: () => <span>&nbsp;</span>,
-316
View File
@@ -1,316 +0,0 @@
<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
Any of the below HOCs can be imported from react-table like so:
```javascript
import ReactTable from "react-table";
import treeTableHOC from "react-table/lib/hoc/treeTable";
const TreeTable = treeTableHOC(ReactTable);
```
Swap `treeTable` and `TreeTable` with any of the other HOC names as necessary.
### 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
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
**Note:** The select field defaults to the accessor `_id` property in order to render the select field for that particular row. If your objects have different
unique ID fields, make sure to tell React Table that by passing it the `keyField` property.
```javascript
<ReactTable keyField='id' />
```
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.
### FoldableTable
FoldableTable is a HOC that make the columns are foldable. The reason I developed this HOC because when working on the real project related to the financial which display so many columns for validation and comparison.
So foldable columns allow users to temporary hidden the unwanted to columns so that they can focus on the data that they want to see.
#### How it works
```javascript
const FoldableTable = FoldableTableHOC(ReactTable);
```
It will scan all the columns which `foldable` is `true` and apply the foldable column feature. This feature will work for both normal columns and header columns as samples below.
- With Header Columns
```javascript
render(){
return <FoldableTable
columns={[{
Header: "Name",
foldable: true,
columns: [{
Header: "First Name",
accessor: "first_name"
},{
Header: "Last Name",
accessor: "last_name"
}]
},{
Header: "Info",
foldable: true,
columns: [{
Header: "Email",
accessor: "email"
},{
Header: "Gender",
accessor: "gender"
}]
}]
}/>
}
```
![With Header Columns](https://raw.githubusercontent.com/baoduy/Images/master/Wordpress/JavaScripts/react-table%20foldableHOC/FoldableTable%20With%20Header.gif)
- With Nornal Columns
```javascript
render() {
return <FoldableTable
columns={[{
Header: "First Name",
accessor: "first_name"
},
{
Header: "Last Name",
accessor: "last_name",
foldable: true,
},
{
Header: "Email",
accessor: "email",
foldable: true,
},
{
Header: "Gender",
accessor: "gender",
foldable: true,
}]}></FoldableTable>
}
```
![With Normal Columns](https://raw.githubusercontent.com/baoduy/Images/master/Wordpress/JavaScripts/react-table%20foldableHOC/FoldableTable%20Without%20Header.gif)
- The `FoldableTable` also fully compatible with existing HOCs, below is with selectTableHOC.
![With Normal Columns](https://raw.githubusercontent.com/baoduy/Images/master/Wordpress/JavaScripts/react-table%20foldableHOC/FoldableTable%20With%20selectTable.gif)
#### State management
If you would like to manage the state of FoldableTable, then add the following codes.
```javascript
render() {
return <FoldableTable
onFoldChange={newFolded => this.setState(p => { return { folded: newFolded } })}
folded={this.state.folded}
/>
}
```
#### Custom Components
- FoldIconComponent: to render the Icon of buttons.
- FoldButtonComponent: to render the folding buttons for each Column.
With default rendering as below.
```javascript
const defaultFoldIconComponent = ({ collapsed }) => {
//Render your Icon here
}
const defaultFoldButtonComponent = ({ header, collapsed, icon, onClick }) => {
//Render your button here.
}
```
### AdvancedExpandTable
HOC which allows any Cell in the row to toggle the row's
SubComponent (expand/collapse). Also allows the SubComponent to toggle itself. Technically supports toggling any row's SubComponent.
These are the expand functions available to any SubComponent or Column Cell:
```
toggleRowSubComponent
showRowSubComponent
hideRowSubComponent
```
They are available through the `props.columnProps.rest` object.
On any change to the props, the table will reset the expanded state.
Accepts a onExpandedChange callback to be called whenever the table expanded state changes
Note: only supports 1 level of nesting.
Example usage in a Column Cell Renderer:
```javascript
Cell: props => {
const {
value
columnProps: { rest: { showRowSubComponent } },
nestingPath
} = props;
return (
<div>
<button
onClick={e => showRowSubComponent(nestingPath, e)}
>
{value}
</button>
</div>
);
}
```
Example usage in the ReactTable SubComponent (toggle itself):
```javascript
const AdvancedExpandReactTable = advancedExpandTableHOC(ReactTable);
<AdvancedExpandReactTable>
...
SubComponent={({ row, nestingPath, toggleRowSubComponent }) => {
return (
<div>
<button
onClick={e => toggleRowSubComponent({ nestingPath }, e)}
>
{row.value}
</button>
</div>
);
}}
/>
```
Each Column Renderer (E.g. Cell ) gets the expand functions in its props and each SubComponent gets the expand functions in its props
Expand functions takes the `nestingPath` or `rowInfo` given to each
Column Renderer and SubComponent already by ReactTable.
## 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.
-199
View File
@@ -1,199 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import set from 'lodash.set'
import get from 'lodash.get'
/*
AvancedExpandTableHOC for ReactTable
HOC which allows any Cell in the row to toggle the row's
SubComponent. Also allows the SubComponent to toggle itself.
Expand functions available to any SubComponent or Column Cell:
toggleRowSubComponent
showRowSubComponent
hideRowSubComponent
Each Column Renderer (E.g. Cell ) gets the expand functions in its props
And Each SubComponent gets the expand functions in its props
Expand functions takes the `rowInfo` given to each
Column Renderer and SubComponent already by ReactTable.
*/
export const subComponentWithToggle = (SubComponent, expandFuncs) => props => (
<SubComponent {...props} {...expandFuncs} />
)
// each cell in the column gets passed the function to toggle a sub component
export const columnsWithToggle = (columns, expandFuncs) =>
columns.map(column => {
if (column.columns) {
return {
...column,
columns: columnsWithToggle(column.columns, expandFuncs),
}
}
return {
...column,
getProps () {
return {
...expandFuncs,
}
},
}
})
export const advancedExpandTableHOC = TableComponent =>
class AdvancedExpandTable extends Component {
constructor (props) {
super(props)
this.state = {
expanded: {},
}
this.toggleRowSubComponent = this.toggleRowSubComponent.bind(this)
this.showRowSubComponent = this.showRowSubComponent.bind(this)
this.hideRowSubComponent = this.hideRowSubComponent.bind(this)
this.getTdProps = this.getTdProps.bind(this)
this.fireOnExpandedChange = this.fireOnExpandedChange.bind(this)
this.expandFuncs = {
toggleRowSubComponent: this.toggleRowSubComponent,
showRowSubComponent: this.showRowSubComponent,
hideRowSubComponent: this.hideRowSubComponent,
}
}
// after initial render if we get new
// data, columns, page changes, etc.
// we reset expanded state.
componentWillReceiveProps () {
this.setState({
expanded: {},
})
}
static propTypes = {
columns: PropTypes.array.isRequired,
SubComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element])
.isRequired,
onExpandedChange: PropTypes.func,
};
static defaultProps = {
onExpandedChange: null,
};
static DisplayName = 'AdvancedExpandTable';
// since we pass the expand functions to each Cell,
// we need to filter it out from being passed as an
// actual DOM attribute. See getProps in columnsWithToggle above.
static TdComponent ({
toggleRowSubComponent,
showRowSubComponent,
hideRowSubComponent,
...rest
}) {
return <TableComponent.defaultProps.TdComponent {...rest} />
}
fireOnExpandedChange (rowInfo, e) {
// fire callback once state has changed.
if (this.props.onExpandedChange) {
this.props.onExpandedChange(rowInfo, e)
}
}
resolveNewTableState (rowInfoOrNestingPath, e, expandType) {
// derive nestingPath if only rowInfo is passed
let nestingPath = rowInfoOrNestingPath
if (rowInfoOrNestingPath.nestingPath) {
nestingPath = rowInfoOrNestingPath.nestingPath
}
this.setState(
prevState => {
const isExpanded = get(prevState.expanded, nestingPath)
// since we do not support nested rows, a shallow clone is okay.
const newExpanded = { ...prevState.expanded }
switch (expandType) {
case 'show':
set(newExpanded, nestingPath, {})
break
case 'hide':
set(newExpanded, nestingPath, false)
break
default:
// toggle
set(newExpanded, nestingPath, isExpanded ? false : {})
}
return {
...prevState,
expanded: newExpanded,
}
},
() => this.fireOnExpandedChange(rowInfoOrNestingPath, e)
)
}
toggleRowSubComponent (rowInfo, e) {
this.resolveNewTableState(rowInfo, e)
}
showRowSubComponent (rowInfo, e) {
this.resolveNewTableState(rowInfo, e, 'show')
}
hideRowSubComponent (rowInfo, e) {
this.resolveNewTableState(rowInfo, e, 'hide')
}
getTdProps (tableState, rowInfo, column) {
const { expander } = column
if (!expander) {
// no overrides
return {}
}
return {
// only override onClick for column Td
onClick: e => {
this.toggleRowSubComponent(rowInfo, e)
},
}
}
getWrappedInstance () {
if (!this.wrappedInstance) { console.warn('AdvancedExpandTable - No wrapped instance') }
if (this.wrappedInstance.getWrappedInstance) {
return this.wrappedInstance.getWrappedInstance()
}
return this.wrappedInstance
}
render () {
const {
columns, SubComponent, onExpandedChange, ...rest
} = this.props
const wrappedColumns = columnsWithToggle(columns, this.expandFuncs)
const WrappedSubComponent = subComponentWithToggle(
SubComponent,
this.expandFuncs
)
return (
<TableComponent
{...rest}
columns={wrappedColumns}
expanded={this.state.expanded}
getTdProps={this.getTdProps}
SubComponent={WrappedSubComponent}
TdComponent={AdvancedExpandTable.TdComponent}
/>
)
}
}
-230
View File
@@ -1,230 +0,0 @@
/* eslint-disable */
import React from 'react'
import left from './left.svg'
import right from './right.svg'
const defaultFoldIconComponent = ({ collapsed }) => {
const style = { width: 25 }
if (collapsed) return <img src={right} style={style} alt="right" />
return <img src={left} style={style} alt="left" />
}
const defaultFoldButtonComponent = ({ header, collapsed, icon, onClick }) => {
const style = {
marginLeft: '0px',
marginTop: '-5px',
marginBottom: '-8px',
float: 'left',
cursor: 'pointer',
}
return (
<div>
<div style={style} onClick={onClick}>
{icon}
</div>
{!collapsed && <div>{header}</div>}
</div>
)
}
export default ReactTable => {
const wrapper = class RTFoldableTable extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
folded: props.onFoldChange ? undefined : {},
resized: props.resized || [],
}
}
componentWillReceiveProps(newProps) {
if (this.state.resized !== newProps.resized) {
this.setState(p => ({ resized: newProps.resized }))
}
}
onResizedChange = resized => {
const { onResizedChange } = this.props
if (onResizedChange) onResizedChange(resized)
else {
this.setState(p => ({ resized }))
}
}
removeResized = column => {
const { id } = column
if (!id) return
const { resized } = this.state
if (!resized) return
const rs = resized.find(r => r.id === id)
if (!rs) return
const newResized = resized.filter(r => r !== rs)
this.onResizedChange(newResized)
}
// this is so we can expose the underlying ReactTable.
getWrappedInstance = () => {
if (!this.wrappedInstance) console.warn('RTFoldableTable - No wrapped instance')
if (this.wrappedInstance.getWrappedInstance) return this.wrappedInstance.getWrappedInstance()
return this.wrappedInstance
}
getCopiedKey = key => {
const { foldableOriginalKey } = this.props
return `${foldableOriginalKey}${key}`
}
copyOriginals = column => {
const { FoldedColumn } = this.props
// Stop copy if the column already copied
if (column.original_Header) return
Object.keys(FoldedColumn).forEach(k => {
const copiedKey = this.getCopiedKey(k)
if (k === 'Cell') column[copiedKey] = column[k] ? column[k] : c => c.value
else column[copiedKey] = column[k]
})
// Copy sub Columns
if (column.columns && !column.original_Columns) column.original_Columns = column.columns
// Copy Header
if (!column.original_Header) column.original_Header = column.Header
}
restoreToOriginal = column => {
const { FoldedColumn } = this.props
Object.keys(FoldedColumn).forEach(k => {
// ignore header as handling by foldableHeaderRender
if (k === 'Header') return
const copiedKey = this.getCopiedKey(k)
column[k] = column[copiedKey]
})
if (column.columns && column.original_Columns) column.columns = column.original_Columns
}
getState = () => (this.props.onFoldChange ? this.props.folded : this.state.folded)
isFolded = col => {
const folded = this.getState()
return folded[col.id] === true
}
foldingHandler = col => {
if (!col || !col.id) return
const { onFoldChange } = this.props
const folded = this.getState()
const { id } = col
const newFold = Object.assign({}, folded)
newFold[id] = !newFold[id]
// Remove the Resized if have
this.removeResized(col)
if (onFoldChange) onFoldChange(newFold)
else {
this.setState(previous => ({ folded: newFold }))
}
}
foldableHeaderRender = cell => {
const { FoldButtonComponent, FoldIconComponent } = this.props
const { column } = cell
const collapsed = this.isFolded(column)
const icon = React.createElement(FoldIconComponent, { collapsed })
const onClick = () => this.foldingHandler(column)
return React.createElement(FoldButtonComponent, {
header: column.original_Header,
collapsed,
icon,
onClick,
})
}
applyFoldableForColumn = column => {
const collapsed = this.isFolded(column)
const { FoldedColumn } = this.props
// Handle Column Header
if (column.columns) {
if (collapsed) {
column.columns = [FoldedColumn]
column.width = FoldedColumn.width
column.style = FoldedColumn.style
} else this.restoreToOriginal(column)
}
// Handle Normal Column.
else if (collapsed) column = Object.assign(column, FoldedColumn)
else {
this.restoreToOriginal(column)
}
}
applyFoldableForColumns = columns =>
columns.map((col, index) => {
if (!col.foldable) return col
// If col don't have id then generate id based on index
if (!col.id) col.id = `col_${index}`
this.copyOriginals(col)
// Replace current header with internal header render.
col.Header = c => this.foldableHeaderRender(c)
// apply foldable
this.applyFoldableForColumn(col)
// return the new column out
return col
})
render() {
const {
columns: originalCols,
FoldButtonComponent,
FoldIconComponent,
FoldedColumn,
...rest
} = this.props
const columns = this.applyFoldableForColumns([...originalCols])
const extra = {
columns,
onResizedChange: this.onResizedChange,
resized: this.state.resized,
}
return <ReactTable {...rest} {...extra} ref={r => (this.wrappedInstance = r)} />
}
}
wrapper.displayName = 'RTFoldableTable'
wrapper.defaultProps = {
FoldIconComponent: defaultFoldIconComponent,
FoldButtonComponent: defaultFoldButtonComponent,
foldableOriginalKey: 'original_',
FoldedColumn: {
Cell: c => '',
width: 30,
sortable: false,
resizable: false,
filterable: false,
},
}
return wrapper
}
-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="512px" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M327.3,98.9l-2.1,1.8l-156.5,136c-5.3,4.6-8.6,11.5-8.6,19.2c0,7.7,3.4,14.6,8.6,19.2L324.9,411l2.6,2.3 c2.5,1.7,5.5,2.7,8.7,2.7c8.7,0,15.8-7.4,15.8-16.6h0V112.6h0c0-9.2-7.1-16.6-15.8-16.6C332.9,96,329.8,97.1,327.3,98.9z"/></svg>

Before

Width:  |  Height:  |  Size: 583 B

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="512px" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M184.7,413.1l2.1-1.8l156.5-136c5.3-4.6,8.6-11.5,8.6-19.2c0-7.7-3.4-14.6-8.6-19.2L187.1,101l-2.6-2.3 C182,97,179,96,175.8,96c-8.7,0-15.8,7.4-15.8,16.6h0v286.8h0c0,9.2,7.1,16.6,15.8,16.6C179.1,416,182.2,414.9,184.7,413.1z"/></svg>

Before

Width:  |  Height:  |  Size: 585 B

-119
View File
@@ -1,119 +0,0 @@
/* eslint-disable */
import React from 'react'
const defaultSelectInputComponent = props => {
return (
<input
type={props.selectType || 'checkbox'}
aria-label={`${props.checked ? 'Un-select':'Select'} row with id:${props.id}` }
checked={props.checked}
id={props.id}
onClick={e => {
const { shiftKey } = e
e.stopPropagation()
props.onClick(props.id, shiftKey, props.row)
}}
onChange={() => {}}
/>
)
}
export default (Component, options) => {
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,
row,
id: `select-${row[keyField]}`
}
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,
id: 'select-all'
}
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,
selectWidth,
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: selectWidth || 30,
filterable: false,
sortable: false,
resizable: false,
style: { textAlign: 'center' },
}
const columns = (options !== undefined && options.floatingLeft === true) ? [...originalCols, select] : [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: 'checkbox',
SelectInputComponent: defaultSelectInputComponent,
SelectAllInputComponent: defaultSelectInputComponent,
}
return wrapper
}
-79
View File
@@ -1,79 +0,0 @@
/* 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}`} role="row" 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) || rest.pivotBy.includes(col.id))) {
column = {
id: col.id,
accessor: col.accessor,
width: `${treeTableIndent}px`,
show: false,
Header: '',
Expander: col.Expander,
PivotValue: col.PivotValue,
Pivot: col.Pivot,
}
}
return column
}),
TrComponent,
getTrProps,
}
return <Component {...rest} {...extra} ref={r => (this.wrappedInstance = r)} />
}
}
wrapper.displayName = 'RTTreeTable'
wrapper.defaultProps = {
treeTableIndent: 10,
}
return wrapper
}
+436 -298
View File
@@ -5,12 +5,10 @@ import _ from './utils'
import Lifecycle from './lifecycle' import Lifecycle from './lifecycle'
import Methods from './methods' import Methods from './methods'
import defaultProps from './defaultProps' import defaultProps from './defaultProps'
import propTypes from './propTypes'
export const ReactTableDefaults = defaultProps export const ReactTableDefaults = defaultProps
export default class ReactTable extends Methods(Lifecycle(Component)) { export default class ReactTable extends Methods(Lifecycle(Component)) {
static propTypes = propTypes
static defaultProps = defaultProps static defaultProps = defaultProps
constructor (props) { constructor (props) {
@@ -34,7 +32,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
this.resizeColumnMoving = this.resizeColumnMoving.bind(this) this.resizeColumnMoving = this.resizeColumnMoving.bind(this)
this.state = { this.state = {
page: props.defaultPage, page: 0,
pageSize: props.defaultPageSize, pageSize: props.defaultPageSize,
sorted: props.defaultSorted, sorted: props.defaultSorted,
expanded: props.defaultExpanded, expanded: props.defaultExpanded,
@@ -80,7 +78,6 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
loadingText, loadingText,
noDataText, noDataText,
sortable, sortable,
multiSort,
resizable, resizable,
filterable, filterable,
// Pivoting State // Pivoting State
@@ -142,26 +139,28 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
const hasColumnFooter = allVisibleColumns.some(d => d.Footer) const hasColumnFooter = allVisibleColumns.some(d => d.Footer)
const hasFilters = filterable || allVisibleColumns.some(d => d.filterable) const hasFilters = filterable || allVisibleColumns.some(d => d.filterable)
const recurseRowsViewIndex = (rows, path = [], index = -1) => [ const recurseRowsViewIndex = (rows, path = [], index = -1) => {
rows.map((row, i) => { return [
index += 1 rows.map((row, i) => {
const rowWithViewIndex = { index++
...row, const rowWithViewIndex = {
_viewIndex: index, ...row,
} _viewIndex: index,
const newPath = path.concat([i]) }
if (rowWithViewIndex[subRowsKey] && _.get(expanded, newPath)) { const newPath = path.concat([i])
[rowWithViewIndex[subRowsKey], index] = recurseRowsViewIndex( if (rowWithViewIndex[subRowsKey] && _.get(expanded, newPath)) {
rowWithViewIndex[subRowsKey], ;[rowWithViewIndex[subRowsKey], index] = recurseRowsViewIndex(
newPath, rowWithViewIndex[subRowsKey],
index newPath,
) index
} )
return rowWithViewIndex }
}), return rowWithViewIndex
index, }),
]; index,
[pageRows] = recurseRowsViewIndex(pageRows) ]
}
;[pageRows] = recurseRowsViewIndex(pageRows)
const canPrevious = page > 0 const canPrevious = page > 0
const canNext = page + 1 < pages const canNext = page + 1 < pages
@@ -188,24 +187,52 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
rowMinWidth, rowMinWidth,
} }
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)
// Visual Components // Visual Components
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>
)
}
const makeHeaderGroup = (column, i) => { const makeHeaderGroup = (column, i) => {
const resizedValue = col => (resized.find(x => x.id === col.id) || {}).value const resizedValue = col =>
(resized.find(x => x.id === col.id) || {}).value
const flex = _.sum( const flex = _.sum(
column.columns.map(col => (col.width || resizedValue(col) ? 0 : col.minWidth)) column.columns.map(
col => (col.width || resizedValue(col) ? 0 : col.minWidth)
)
) )
const width = _.sum( const width = _.sum(
column.columns.map(col => _.getFirstDefined(resizedValue(col), col.width, col.minWidth)) column.columns.map(col =>
_.getFirstDefined(resizedValue(col), col.width, col.minWidth)
)
) )
const maxWidth = _.sum( const maxWidth = _.sum(
column.columns.map(col => _.getFirstDefined(resizedValue(col), col.width, col.maxWidth)) column.columns.map(col =>
_.getFirstDefined(resizedValue(col), col.width, col.maxWidth)
)
) )
const theadGroupThProps = _.splitProps( const theadGroupThProps = _.splitProps(
@@ -240,7 +267,7 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
return ( return (
<ThComponent <ThComponent
key={`${i}-${column.id}`} key={i + '-' + column.id}
className={classnames(classes)} className={classnames(classes)}
style={{ style={{
...styles, ...styles,
@@ -250,106 +277,19 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
> >
{_.normalizeComponent(column.Header, { {_.normalizeComponent(column.Header, {
data: sortedData, data: sortedData,
column, column: column,
})} })}
</ThComponent> </ThComponent>
) )
} }
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>
)
}
const makeHeader = (column, i) => {
const resizedCol = resized.find(x => x.id === column.id) || {}
const sort = sorted.find(d => d.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 theadThProps = _.splitProps(getTheadThProps(finalState, undefined, column, this))
const columnHeaderProps = _.splitProps(
column.getHeaderProps(finalState, undefined, column, this)
)
const classes = [column.headerClassName, theadThProps.className, columnHeaderProps.className]
const styles = {
...column.headerStyle,
...theadThProps.style,
...columnHeaderProps.style,
}
const rest = {
...theadThProps.rest,
...columnHeaderProps.rest,
}
const isResizable = _.getFirstDefined(column.resizable, resizable, false)
const resizer = isResizable ? (
<ResizerComponent
onMouseDown={e => this.resizeColumnStart(e, column, false)}
onTouchStart={e => this.resizeColumnStart(e, column, true)}
{...getResizerProps('finalState', undefined, column, this)}
/>
) : null
const isSortable = _.getFirstDefined(column.sortable, sortable, false)
return (
<ThComponent
key={`${i}-${column.id}`}
className={classnames(
classes,
isResizable && 'rt-resizable-header',
sort ? (sort.desc ? '-sort-desc' : '-sort-asc') : '',
isSortable && '-cursor-pointer',
!show && '-hidden',
pivotBy && pivotBy.slice(0, -1).includes(column.id) && 'rt-header-pivot'
)}
style={{
...styles,
flex: `${width} 0 auto`,
width: _.asPx(width),
maxWidth: _.asPx(maxWidth),
}}
toggleSort={e => {
if (isSortable) this.sortColumn(column, multiSort ? e.shiftKey : false)
}}
{...rest}
>
<div className={classnames(isResizable && 'rt-resizable-header-content')}>
{_.normalizeComponent(column.Header, {
data: sortedData,
column,
})}
</div>
{resizer}
</ThComponent>
)
}
const makeHeaders = () => { const makeHeaders = () => {
const theadProps = _.splitProps(getTheadProps(finalState, undefined, undefined, this)) const theadProps = _.splitProps(
const theadTrProps = _.splitProps(getTheadTrProps(finalState, undefined, undefined, this)) getTheadProps(finalState, undefined, undefined, this)
)
const theadTrProps = _.splitProps(
getTheadTrProps(finalState, undefined, undefined, this)
)
return ( return (
<TheadComponent <TheadComponent
className={classnames('-header', theadProps.className)} className={classnames('-header', theadProps.className)}
@@ -370,12 +310,23 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
) )
} }
const makeFilter = (column, i) => { const makeHeader = (column, i) => {
const resizedCol = resized.find(x => x.id === column.id) || {} const resizedCol = resized.find(x => x.id === column.id) || {}
const width = _.getFirstDefined(resizedCol.value, column.width, column.minWidth) const sort = sorted.find(d => d.id === column.id)
const maxWidth = _.getFirstDefined(resizedCol.value, column.width, column.maxWidth) const show =
const theadFilterThProps = _.splitProps( typeof column.show === 'function' ? column.show() : column.show
getTheadFilterThProps(finalState, undefined, column, this) const width = _.getFirstDefined(
resizedCol.value,
column.width,
column.minWidth
)
const maxWidth = _.getFirstDefined(
resizedCol.value,
column.width,
column.maxWidth
)
const theadThProps = _.splitProps(
getTheadThProps(finalState, undefined, column, this)
) )
const columnHeaderProps = _.splitProps( const columnHeaderProps = _.splitProps(
column.getHeaderProps(finalState, undefined, column, this) column.getHeaderProps(finalState, undefined, column, this)
@@ -383,50 +334,63 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
const classes = [ const classes = [
column.headerClassName, column.headerClassName,
theadFilterThProps.className, theadThProps.className,
columnHeaderProps.className, columnHeaderProps.className,
] ]
const styles = { const styles = {
...column.headerStyle, ...column.headerStyle,
...theadFilterThProps.style, ...theadThProps.style,
...columnHeaderProps.style, ...columnHeaderProps.style,
} }
const rest = { const rest = {
...theadFilterThProps.rest, ...theadThProps.rest,
...columnHeaderProps.rest, ...columnHeaderProps.rest,
} }
const filter = filtered.find(filter => filter.id === column.id) const isResizable = _.getFirstDefined(column.resizable, resizable, false)
const resizer = isResizable
? (<ResizerComponent
onMouseDown={e => this.resizeColumnStart(e, column, false)}
onTouchStart={e => this.resizeColumnStart(e, column, true)}
{...resizerProps}
/>)
: null
const ResolvedFilterComponent = column.Filter || FilterComponent const isSortable = _.getFirstDefined(column.sortable, sortable, false)
const isFilterable = _.getFirstDefined(column.filterable, filterable, false)
return ( return (
<ThComponent <ThComponent
key={`${i}-${column.id}`} key={i + '-' + column.id}
className={classnames(classes)} className={classnames(
classes,
isResizable && 'rt-resizable-header',
sort ? (sort.desc ? '-sort-desc' : '-sort-asc') : '',
isSortable && '-cursor-pointer',
!show && '-hidden',
pivotBy &&
pivotBy.slice(0, -1).includes(column.id) &&
'rt-header-pivot'
)}
style={{ style={{
...styles, ...styles,
flex: `${width} 0 auto`, flex: `${width} 0 auto`,
width: _.asPx(width), width: _.asPx(width),
maxWidth: _.asPx(maxWidth), maxWidth: _.asPx(maxWidth),
}} }}
toggleSort={e => {
isSortable && this.sortColumn(column, e.shiftKey)
}}
{...rest} {...rest}
> >
{isFilterable <div className={classnames(isResizable && 'rt-resizable-header-content')}>
? _.normalizeComponent( {_.normalizeComponent(column.Header, {
ResolvedFilterComponent, data: sortedData,
{ column: column,
column, })}
filter, </div>
onChange: value => this.filterColumn(column, value), {resizer}
},
defaultProps.column.Filter
)
: null}
</ThComponent> </ThComponent>
) )
} }
@@ -458,14 +422,85 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
) )
} }
const makeFilter = (column, i) => {
const resizedCol = resized.find(x => x.id === column.id) || {}
const width = _.getFirstDefined(
resizedCol.value,
column.width,
column.minWidth
)
const maxWidth = _.getFirstDefined(
resizedCol.value,
column.width,
column.maxWidth
)
const theadFilterThProps = _.splitProps(
getTheadFilterThProps(finalState, undefined, column, this)
)
const columnHeaderProps = _.splitProps(
column.getHeaderProps(finalState, undefined, column, this)
)
const classes = [
column.headerClassName,
theadFilterThProps.className,
columnHeaderProps.className,
]
const styles = {
...column.headerStyle,
...theadFilterThProps.style,
...columnHeaderProps.style,
}
const rest = {
...theadFilterThProps.rest,
...columnHeaderProps.rest,
}
const filter = filtered.find(filter => filter.id === column.id)
const ResolvedFilterComponent = column.Filter || FilterComponent
const isFilterable = _.getFirstDefined(
column.filterable,
filterable,
false
)
return (
<ThComponent
key={i + '-' + column.id}
className={classnames(classes)}
style={{
...styles,
flex: `${width} 0 auto`,
width: _.asPx(width),
maxWidth: _.asPx(maxWidth),
}}
{...rest}
>
{isFilterable
? _.normalizeComponent(
ResolvedFilterComponent,
{
column,
filter,
onChange: value => this.filterColumn(column, value),
},
defaultProps.column.Filter
)
: null}
</ThComponent>
)
}
const makePageRow = (row, i, path = []) => { const makePageRow = (row, i, path = []) => {
const rowInfo = { const rowInfo = {
original: row[originalKey], original: row[originalKey],
row, row: row,
index: row[indexKey], index: row[indexKey],
viewIndex: (rowIndex += 1), viewIndex: ++rowIndex,
pageSize,
page,
level: path.length, level: path.length,
nestingPath: path.concat([i]), nestingPath: path.concat([i]),
aggregated: row[aggregatedKey], aggregated: row[aggregatedKey],
@@ -474,23 +509,45 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
} }
const isExpanded = _.get(expanded, rowInfo.nestingPath) const isExpanded = _.get(expanded, rowInfo.nestingPath)
const trGroupProps = getTrGroupProps(finalState, rowInfo, undefined, this) const trGroupProps = getTrGroupProps(finalState, rowInfo, undefined, this)
const trProps = _.splitProps(getTrProps(finalState, rowInfo, undefined, this)) const trProps = _.splitProps(
getTrProps(finalState, rowInfo, undefined, this)
)
return ( return (
<TrGroupComponent key={rowInfo.nestingPath.join('_')} {...trGroupProps}> <TrGroupComponent key={rowInfo.nestingPath.join('_')} {...trGroupProps}>
<TrComponent <TrComponent
className={classnames(trProps.className, row._viewIndex % 2 ? '-even' : '-odd')} className={classnames(
trProps.className,
row._viewIndex % 2 ? '-even' : '-odd'
)}
style={trProps.style} style={trProps.style}
{...trProps.rest} {...trProps.rest}
> >
{allVisibleColumns.map((column, i2) => { {allVisibleColumns.map((column, i2) => {
const resizedCol = resized.find(x => x.id === column.id) || {} const resizedCol = resized.find(x => x.id === column.id) || {}
const show = typeof column.show === 'function' ? column.show() : column.show const show =
const width = _.getFirstDefined(resizedCol.value, column.width, column.minWidth) typeof column.show === 'function' ? column.show() : column.show
const maxWidth = _.getFirstDefined(resizedCol.value, column.width, column.maxWidth) const width = _.getFirstDefined(
const tdProps = _.splitProps(getTdProps(finalState, rowInfo, column, this)) resizedCol.value,
const columnProps = _.splitProps(column.getProps(finalState, rowInfo, column, this)) column.width,
column.minWidth
)
const maxWidth = _.getFirstDefined(
resizedCol.value,
column.width,
column.maxWidth
)
const tdProps = _.splitProps(
getTdProps(finalState, rowInfo, column, this)
)
const columnProps = _.splitProps(
column.getProps(finalState, rowInfo, column, this)
)
const classes = [tdProps.className, column.className, columnProps.className] const classes = [
tdProps.className,
column.className,
columnProps.className,
]
const styles = { const styles = {
...tdProps.style, ...tdProps.style,
@@ -533,35 +590,44 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
{ {
expanded: newExpanded, expanded: newExpanded,
}, },
() => onExpandedChange && onExpandedChange(newExpanded, cellInfo.nestingPath, e) () => {
onExpandedChange &&
onExpandedChange(newExpanded, cellInfo.nestingPath, e)
}
) )
} }
// Default to a standard cell // Default to a standard cell
let resolvedCell = _.normalizeComponent(column.Cell, cellInfo, value) let resolvedCell = _.normalizeComponent(
column.Cell,
cellInfo,
value
)
// Resolve Renderers // Resolve Renderers
const ResolvedAggregatedComponent = const ResolvedAggregatedComponent =
column.Aggregated || (!column.aggregate ? AggregatedComponent : column.Cell) column.Aggregated ||
const ResolvedExpanderComponent = column.Expander || ExpanderComponent (!column.aggregate ? AggregatedComponent : column.Cell)
const ResolvedPivotValueComponent = column.PivotValue || PivotValueComponent const ResolvedExpanderComponent =
column.Expander || ExpanderComponent
const ResolvedPivotValueComponent =
column.PivotValue || PivotValueComponent
const DefaultResolvedPivotComponent = const DefaultResolvedPivotComponent =
PivotComponent || PivotComponent ||
(props => ( (props =>
<div> <div>
<ResolvedExpanderComponent {...props} /> <ResolvedExpanderComponent {...props} />
<ResolvedPivotValueComponent {...props} /> <ResolvedPivotValueComponent {...props} />
</div> </div>)
)) const ResolvedPivotComponent =
const ResolvedPivotComponent = column.Pivot || DefaultResolvedPivotComponent column.Pivot || DefaultResolvedPivotComponent
// Is this cell expandable? // Is this cell expandable?
if (cellInfo.pivoted || cellInfo.expander) { if (cellInfo.pivoted || cellInfo.expander) {
// Make it expandable by defualt // Make it expandable by defualt
cellInfo.expandable = true cellInfo.expandable = true
useOnExpanderClick = true useOnExpanderClick = true
// If pivoted, has no subRows, and does not have a subComponent, // If pivoted, has no subRows, and does not have a subComponent, do not make expandable
// do not make expandable
if (cellInfo.pivoted && !cellInfo.subRows && !SubComponent) { if (cellInfo.pivoted && !cellInfo.subRows && !SubComponent) {
cellInfo.expandable = false cellInfo.expandable = false
} }
@@ -569,11 +635,12 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
if (cellInfo.pivoted) { if (cellInfo.pivoted) {
// Is this column a branch? // Is this column a branch?
isBranch = rowInfo.row[pivotIDKey] === column.id && cellInfo.subRows isBranch =
rowInfo.row[pivotIDKey] === column.id && cellInfo.subRows
// Should this column be blank? // Should this column be blank?
isPreview = isPreview =
pivotBy.indexOf(column.id) > pivotBy.indexOf(rowInfo.row[pivotIDKey]) && pivotBy.indexOf(column.id) >
cellInfo.subRows pivotBy.indexOf(rowInfo.row[pivotIDKey]) && cellInfo.subRows
// Pivot Cell Render Override // Pivot Cell Render Override
if (isBranch) { if (isBranch) {
// isPivot // isPivot
@@ -587,12 +654,20 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
) )
} else if (isPreview) { } else if (isPreview) {
// Show the pivot preview // Show the pivot preview
resolvedCell = _.normalizeComponent(ResolvedAggregatedComponent, cellInfo, value) resolvedCell = _.normalizeComponent(
ResolvedAggregatedComponent,
cellInfo,
value
)
} else { } else {
resolvedCell = null resolvedCell = null
} }
} else if (cellInfo.aggregated) { } else if (cellInfo.aggregated) {
resolvedCell = _.normalizeComponent(ResolvedAggregatedComponent, cellInfo, value) resolvedCell = _.normalizeComponent(
ResolvedAggregatedComponent,
cellInfo,
value
)
} }
if (cellInfo.expander) { if (cellInfo.expander) {
@@ -611,11 +686,11 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
} }
} }
const resolvedOnExpanderClick = useOnExpanderClick ? onExpanderClick : () => {} const resolvedOnExpanderClick = useOnExpanderClick
? onExpanderClick
: () => {}
// If there are multiple onClick events, make sure they don't // If there are multiple onClick events, make sure they don't override eachother. This should maybe be expanded to handle all function attributes
// override eachother. This should maybe be expanded to handle all
// function attributes
const interactionProps = { const interactionProps = {
onClick: resolvedOnExpanderClick, onClick: resolvedOnExpanderClick,
} }
@@ -635,11 +710,10 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
// Return the cell // Return the cell
return ( return (
<TdComponent <TdComponent
// eslint-disable-next-line react/no-array-index-key key={i2 + '-' + column.id}
key={`${i2}-${column.id}`}
className={classnames( className={classnames(
classes, classes,
!cellInfo.expandable && !show && 'hidden', !show && 'hidden',
cellInfo.expandable && 'rt-expandable', cellInfo.expandable && 'rt-expandable',
(isBranch || isPreview) && 'rt-pivot' (isBranch || isPreview) && 'rt-pivot'
)} )}
@@ -660,58 +734,29 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
</TrComponent> </TrComponent>
{rowInfo.subRows && {rowInfo.subRows &&
isExpanded && isExpanded &&
rowInfo.subRows.map((d, i) => makePageRow(d, i, rowInfo.nestingPath))} rowInfo.subRows.map((d, i) =>
makePageRow(d, i, rowInfo.nestingPath)
)}
{SubComponent && {SubComponent &&
!rowInfo.subRows && !rowInfo.subRows &&
isExpanded && isExpanded &&
SubComponent(rowInfo, () => { SubComponent(rowInfo)}
const newExpanded = _.clone(expanded)
_.set(newExpanded, rowInfo.nestingPath, false)
})}
</TrGroupComponent> </TrGroupComponent>
) )
} }
const makePadColumn = (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 flex = width
const maxWidth = _.getFirstDefined(resizedCol.value, column.width, column.maxWidth)
const tdProps = _.splitProps(getTdProps(finalState, undefined, column, this))
const columnProps = _.splitProps(column.getProps(finalState, undefined, column, this))
const classes = [tdProps.className, column.className, columnProps.className]
const styles = {
...tdProps.style,
...column.style,
...columnProps.style,
}
return (
<TdComponent
key={`${i}-${column.id}`}
className={classnames(classes, !show && 'hidden')}
style={{
...styles,
flex: `${flex} 0 auto`,
width: _.asPx(width),
maxWidth: _.asPx(maxWidth),
}}
{...tdProps.rest}
>
{_.normalizeComponent(PadRowComponent)}
</TdComponent>
)
}
const makePadRow = (row, i) => { const makePadRow = (row, i) => {
const trGroupProps = getTrGroupProps(finalState, undefined, undefined, this) const trGroupProps = getTrGroupProps(
const trProps = _.splitProps(getTrProps(finalState, undefined, undefined, this)) finalState,
undefined,
undefined,
this
)
const trProps = _.splitProps(
getTrProps(finalState, undefined, undefined, this)
)
return ( return (
<TrGroupComponent key={`pad-${i}`} {...trGroupProps}> <TrGroupComponent key={i} {...trGroupProps}>
<TrComponent <TrComponent
className={classnames( className={classnames(
'-padRow', '-padRow',
@@ -726,56 +771,62 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
) )
} }
const makeColumnFooter = (column, i) => { const makePadColumn = (column, i) => {
const resizedCol = resized.find(x => x.id === column.id) || {} const resizedCol = resized.find(x => x.id === column.id) || {}
const show = typeof column.show === 'function' ? column.show() : column.show const show =
const width = _.getFirstDefined(resizedCol.value, column.width, column.minWidth) typeof column.show === 'function' ? column.show() : column.show
const maxWidth = _.getFirstDefined(resizedCol.value, column.width, column.maxWidth) let width = _.getFirstDefined(
const tFootTdProps = _.splitProps(getTfootTdProps(finalState, undefined, undefined, this)) resizedCol.value,
const columnProps = _.splitProps(column.getProps(finalState, undefined, column, this)) column.width,
const columnFooterProps = _.splitProps( column.minWidth
column.getFooterProps(finalState, undefined, column, this) )
let flex = width
let maxWidth = _.getFirstDefined(
resizedCol.value,
column.width,
column.maxWidth
)
const tdProps = _.splitProps(
getTdProps(finalState, undefined, column, this)
)
const columnProps = _.splitProps(
column.getProps(finalState, undefined, column, this)
) )
const classes = [ const classes = [
tFootTdProps.className, tdProps.className,
column.className, column.className,
columnProps.className, columnProps.className,
columnFooterProps.className,
] ]
const styles = { const styles = {
...tFootTdProps.style, ...tdProps.style,
...column.style, ...column.style,
...columnProps.style, ...columnProps.style,
...columnFooterProps.style,
} }
return ( return (
<TdComponent <TdComponent
key={`${i}-${column.id}`} key={i + '-' + column.id}
className={classnames(classes, !show && 'hidden')} className={classnames(classes, !show && 'hidden')}
style={{ style={{
...styles, ...styles,
flex: `${width} 0 auto`, flex: `${flex} 0 auto`,
width: _.asPx(width), width: _.asPx(width),
maxWidth: _.asPx(maxWidth), maxWidth: _.asPx(maxWidth),
}} }}
{...columnProps.rest} {...tdProps.rest}
{...tFootTdProps.rest}
{...columnFooterProps.rest}
> >
{_.normalizeComponent(column.Footer, { {_.normalizeComponent(PadRowComponent)}
data: sortedData,
column,
})}
</TdComponent> </TdComponent>
) )
} }
const makeColumnFooters = () => { const makeColumnFooters = () => {
const tFootProps = _.splitProps(getTfootProps(finalState, undefined, undefined, this)) const tFootProps = getTfootProps(finalState, undefined, undefined, this)
const tFootTrProps = _.splitProps(getTfootTrProps(finalState, undefined, undefined, this)) const tFootTrProps = _.splitProps(
getTfootTrProps(finalState, undefined, undefined, this)
)
return ( return (
<TfootComponent <TfootComponent
className={tFootProps.className} className={tFootProps.className}
@@ -796,7 +847,67 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
) )
} }
const makePagination = isTop => { 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( const paginationProps = _.splitProps(
getPaginationProps(finalState, undefined, undefined, this) getPaginationProps(finalState, undefined, undefined, this)
) )
@@ -810,54 +921,81 @@ export default class ReactTable extends Methods(Lifecycle(Component)) {
onPageSizeChange={this.onPageSizeChange} onPageSizeChange={this.onPageSizeChange}
className={paginationProps.className} className={paginationProps.className}
style={paginationProps.style} style={paginationProps.style}
isTop={isTop}
{...paginationProps.rest} {...paginationProps.rest}
/> />
) )
} }
const makeTable = () => ( const rootProps = _.splitProps(
<div getProps(finalState, undefined, undefined, this)
className={classnames('ReactTable', className, rootProps.className)}
style={{
...style,
...rootProps.style,
}}
{...rootProps.rest}
>
{showPagination && showPaginationTop ? (
<div className="pagination-top">{makePagination(true)}</div>
) : null}
<TableComponent
className={classnames(tableProps.className, currentlyResizing ? 'rt-resizing' : '')}
style={tableProps.style}
{...tableProps.rest}
>
{hasHeaderGroups ? makeHeaderGroups() : null}
{makeHeaders()}
{hasFilters ? makeFilters() : null}
<TbodyComponent
className={classnames(tBodyProps.className)}
style={{
...tBodyProps.style,
minWidth: `${rowMinWidth}px`,
}}
{...tBodyProps.rest}
>
{pageRows.map((d, i) => makePageRow(d, i))}
{padRows.map(makePadRow)}
</TbodyComponent>
{hasColumnFooter ? makeColumnFooters() : null}
</TableComponent>
{showPagination && showPaginationBottom ? (
<div className="pagination-bottom">{makePagination(false)}</div>
) : null}
{!pageRows.length && (
<NoDataComponent {...noDataProps}>{_.normalizeComponent(noDataText)}</NoDataComponent>
)}
<LoadingComponent loading={loading} loadingText={loadingText} {...loadingProps} />
</div>
) )
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 (
<div
className={classnames('ReactTable', className, rootProps.className)}
style={{
...style,
...rootProps.style,
}}
{...rootProps.rest}
>
{showPagination && showPaginationTop
? <div className='pagination-top'>
{pagination}
</div>
: null}
<TableComponent
className={classnames(
tableProps.className,
currentlyResizing ? 'rt-resizing' : ''
)}
style={tableProps.style}
{...tableProps.rest}
>
{hasHeaderGroups ? makeHeaderGroups() : null}
{makeHeaders()}
{hasFilters ? makeFilters() : null}
<TbodyComponent
className={classnames(tBodyProps.className)}
style={{
...tBodyProps.style,
minWidth: `${rowMinWidth}px`,
}}
{...tBodyProps.rest}
>
{pageRows.map((d, i) => makePageRow(d, i))}
{padRows.map(makePadRow)}
</TbodyComponent>
{hasColumnFooter ? makeColumnFooters() : null}
</TableComponent>
{showPagination && showPaginationBottom
? <div className='pagination-bottom'>
{pagination}
</div>
: null}
{!pageRows.length &&
<NoDataComponent {...noDataProps}>
{_.normalizeComponent(noDataText)}
</NoDataComponent>}
<LoadingComponent
loading={loading}
loadingText={loadingText}
{...loadingProps}
/>
</div>
)
}
// childProps are optionally passed to a function-as-a-child // childProps are optionally passed to a function-as-a-child
return children ? children(finalState, makeTable, this) : makeTable() return children ? children(finalState, makeTable, this) : makeTable()
+7 -18
View File
@@ -2,15 +2,6 @@ $easeOutQuad = cubic-bezier(0.250, 0.460, 0.450, 0.940)
$easeOutBack = cubic-bezier(0.175, 0.885, 0.320, 1.275) $easeOutBack = cubic-bezier(0.175, 0.885, 0.320, 1.275)
$expandSize = 7px $expandSize = 7px
input-select-style()
border: 1px solid rgba(0,0,0,0.1)
background: white
padding: 5px 7px
font-size: inherit
border-radius: 3px
font-weight: normal
outline:none
.ReactTable .ReactTable
position:relative position:relative
display: flex display: flex
@@ -43,10 +34,6 @@ input-select-style()
&.-filters &.-filters
border-bottom: 1px solid alpha(black, 0.05) border-bottom: 1px solid alpha(black, 0.05)
input
select
input-select-style();
.rt-th .rt-th
border-right: 1px solid alpha(black, 0.02) border-right: 1px solid alpha(black, 0.02)
@@ -73,9 +60,6 @@ input-select-style()
&:last-child &:last-child
border-right: 0 border-right: 0
.rt-th:focus
outline: none
.rt-resizable-header .rt-resizable-header
overflow: visible overflow: visible
&:last-child &:last-child
@@ -126,7 +110,6 @@ input-select-style()
border-right:0 border-right:0
.rt-expandable .rt-expandable
cursor: pointer cursor: pointer
text-overflow: clip
.rt-tr-group .rt-tr-group
flex: 1 0 auto flex: 1 0 auto
display: flex display: flex
@@ -215,7 +198,13 @@ input-select-style()
input input
select select
input-select-style() border: 1px solid rgba(0,0,0,0.1)
background: white
padding: 5px 7px
font-size: inherit
border-radius: 3px
font-weight: normal
outline:none
.-btn .-btn
appearance:none appearance:none
+18 -12
View File
@@ -1,7 +1,7 @@
export default Base => export default Base =>
class extends Base { class extends Base {
componentWillMount () { componentWillMount () {
this.setStateWithData(this.getDataModel(this.getResolvedState(), true)) this.setStateWithData(this.getDataModel(this.getResolvedState()))
} }
componentDidMount () { componentDidMount () {
@@ -17,7 +17,10 @@ export default Base =>
const defaultableOptions = ['sorted', 'filtered', 'resized', 'expanded'] const defaultableOptions = ['sorted', 'filtered', 'resized', 'expanded']
defaultableOptions.forEach(x => { defaultableOptions.forEach(x => {
const defaultName = `default${x.charAt(0).toUpperCase() + x.slice(1)}` const defaultName = `default${x.charAt(0).toUpperCase() + x.slice(1)}`
if (JSON.stringify(oldState[defaultName]) !== JSON.stringify(newState[defaultName])) { if (
JSON.stringify(oldState[defaultName]) !==
JSON.stringify(newState[defaultName])
) {
newState[x] = newState[defaultName] newState[x] = newState[defaultName]
} }
}) })
@@ -31,7 +34,8 @@ export default Base =>
if (oldState[x] !== newState[x]) { if (oldState[x] !== newState[x]) {
const baseName = x.replace('able', '') const baseName = x.replace('able', '')
const optionName = `${baseName}ed` const optionName = `${baseName}ed`
const defaultName = `default${optionName.charAt(0).toUpperCase() + optionName.slice(1)}` const defaultName = `default${optionName.charAt(0).toUpperCase() +
optionName.slice(1)}`
newState[optionName] = newState[defaultName] newState[optionName] = newState[defaultName]
} }
}) })
@@ -44,7 +48,7 @@ export default Base =>
oldState.sorted !== newState.sorted || oldState.sorted !== newState.sorted ||
oldState.filtered !== newState.filtered oldState.filtered !== newState.filtered
) { ) {
this.setStateWithData(this.getDataModel(newState, oldState.data !== newState.data)) this.setStateWithData(this.getDataModel(newState))
} }
} }
@@ -60,7 +64,7 @@ export default Base =>
if (freezeWhenExpanded) { if (freezeWhenExpanded) {
// if any rows are expanded, freeze the existing data and sorting // if any rows are expanded, freeze the existing data and sorting
const keys = Object.keys(newResolvedState.expanded) const keys = Object.keys(newResolvedState.expanded)
for (let i = 0; i < keys.length; i += 1) { for (var i = 0; i < keys.length; i++) {
if (newResolvedState.expanded[keys[i]]) { if (newResolvedState.expanded[keys[i]]) {
newResolvedState.frozen = true newResolvedState.frozen = true
break break
@@ -75,11 +79,13 @@ export default Base =>
oldState.sorted !== newResolvedState.sorted || oldState.sorted !== newResolvedState.sorted ||
oldState.filtered !== newResolvedState.filtered || oldState.filtered !== newResolvedState.filtered ||
oldState.showFilters !== newResolvedState.showFilters || oldState.showFilters !== newResolvedState.showFilters ||
(!newResolvedState.frozen && oldState.resolvedData !== newResolvedState.resolvedData) (!newResolvedState.frozen &&
oldState.resolvedData !== newResolvedState.resolvedData)
) { ) {
// Handle collapseOnsortedChange & collapseOnDataChange // Handle collapseOnsortedChange & collapseOnDataChange
if ( if (
(oldState.sorted !== newResolvedState.sorted && this.props.collapseOnSortingChange) || (oldState.sorted !== newResolvedState.sorted &&
this.props.collapseOnSortingChange) ||
oldState.filtered !== newResolvedState.filtered || oldState.filtered !== newResolvedState.filtered ||
oldState.showFilters !== newResolvedState.showFilters || oldState.showFilters !== newResolvedState.showFilters ||
(oldState.sortedData && (oldState.sortedData &&
@@ -102,8 +108,10 @@ export default Base =>
if (newResolvedState.sortedData) { if (newResolvedState.sortedData) {
newResolvedState.pages = newResolvedState.manual newResolvedState.pages = newResolvedState.manual
? newResolvedState.pages ? newResolvedState.pages
: Math.ceil(newResolvedState.sortedData.length / newResolvedState.pageSize) : Math.ceil(
newResolvedState.page = newResolvedState.manual ? newResolvedState.page : Math.max( newResolvedState.sortedData.length / newResolvedState.pageSize
)
newResolvedState.page = Math.max(
newResolvedState.page >= newResolvedState.pages newResolvedState.page >= newResolvedState.pages
? newResolvedState.pages - 1 ? newResolvedState.pages - 1
: newResolvedState.page, : newResolvedState.page,
@@ -112,9 +120,7 @@ export default Base =>
} }
return this.setState(newResolvedState, () => { return this.setState(newResolvedState, () => {
if (cb) { cb && cb()
cb()
}
if ( if (
oldState.page !== newResolvedState.page || oldState.page !== newResolvedState.page ||
oldState.pageSize !== newResolvedState.pageSize || oldState.pageSize !== newResolvedState.pageSize ||
+129 -98
View File
@@ -13,12 +13,11 @@ export default Base =>
return resolvedState return resolvedState
} }
getDataModel (newState, dataChanged) { getDataModel (newState) {
const { const {
columns, columns,
pivotBy = [], pivotBy = [],
data, data,
resolveData,
pivotIDKey, pivotIDKey,
pivotValKey, pivotValKey,
subRowsKey, subRowsKey,
@@ -30,7 +29,7 @@ export default Base =>
SubComponent, SubComponent,
} = newState } = newState
// Determine if there are Header Groups // Determine Header Groups
let hasHeaderGroups = false let hasHeaderGroups = false
columns.forEach(column => { columns.forEach(column => {
if (column.columns) { if (column.columns) {
@@ -41,12 +40,15 @@ export default Base =>
let columnsWithExpander = [...columns] let columnsWithExpander = [...columns]
let expanderColumn = columns.find( let expanderColumn = columns.find(
col => col.expander || (col.columns && col.columns.some(col2 => col2.expander)) col =>
col.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) { if (expanderColumn && !expanderColumn.expander) {
expanderColumn = expanderColumn.columns.find(col => col.expander) expanderColumn = expanderColumn.columns.find(col => col.expander)
} }
// If we have SubComponent's we need to make sure we have an expander column // If we have SubComponent's we need to make sure we have an expander column
if (SubComponent && !expanderColumn) { if (SubComponent && !expanderColumn) {
expanderColumn = { expander: true } expanderColumn = { expander: true }
@@ -95,39 +97,41 @@ export default Base =>
// Fall back to an undefined accessor // Fall back to an undefined accessor
if (!dcol.accessor) { if (!dcol.accessor) {
dcol.accessor = () => undefined dcol.accessor = d => undefined
} }
return dcol return dcol
} }
const allDecoratedColumns = []
// Decorate the columns // Decorate the columns
const decorateAndAddToAll = (column, parentColumn) => { const decorateAndAddToAll = (column, parentColumn) => {
const decoratedColumn = makeDecoratedColumn(column, parentColumn) const decoratedColumn = makeDecoratedColumn(column, parentColumn)
allDecoratedColumns.push(decoratedColumn) allDecoratedColumns.push(decoratedColumn)
return decoratedColumn return decoratedColumn
} }
const allDecoratedColumns = []
const decoratedColumns = columnsWithExpander.map(column => { const decoratedColumns = columnsWithExpander.map((column, i) => {
if (column.columns) { if (column.columns) {
return { return {
...column, ...column,
columns: column.columns.map(d => decorateAndAddToAll(d, 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 // Build the visible columns, headers and flat column list
let visibleColumns = decoratedColumns.slice() let visibleColumns = decoratedColumns.slice()
let allVisibleColumns = [] let allVisibleColumns = []
visibleColumns = visibleColumns.map(column => { visibleColumns = visibleColumns.map((column, i) => {
if (column.columns) { if (column.columns) {
const visibleSubColumns = column.columns.filter( const visibleSubColumns = column.columns.filter(
d => (pivotBy.indexOf(d.id) > -1 ? false : _.getFirstDefined(d.show, true)) d =>
pivotBy.indexOf(d.id) > -1
? false
: _.getFirstDefined(d.show, true)
) )
return { return {
...column, ...column,
@@ -137,14 +141,13 @@ export default Base =>
return column return column
}) })
visibleColumns = visibleColumns.filter( visibleColumns = visibleColumns.filter(column => {
column => return column.columns
column.columns ? column.columns.length
? column.columns.length : pivotBy.indexOf(column.id) > -1
: pivotBy.indexOf(column.id) > -1 ? false
? false : _.getFirstDefined(column.show, true)
: _.getFirstDefined(column.show, true) })
)
// Find any custom pivot location // Find any custom pivot location
const pivotIndex = visibleColumns.findIndex(col => col.pivot) const pivotIndex = visibleColumns.findIndex(col => col.pivot)
@@ -160,8 +163,9 @@ export default Base =>
} }
}) })
const PivotParentColumn = pivotColumns.reduce( let PivotParentColumn = pivotColumns.reduce(
(prev, current) => prev && prev === current.parentColumn && current.parentColumn, (prev, current) =>
prev && prev === current.parentColumn && current.parentColumn,
pivotColumns[0].parentColumn pivotColumns[0].parentColumn
) )
@@ -198,13 +202,13 @@ export default Base =>
headerGroups.push({ headerGroups.push({
...this.props.column, ...this.props.column,
...column, ...column,
columns, columns: columns,
}) })
currentSpan = [] currentSpan = []
} }
// Build flat list of allVisibleColumns and HeaderGroups // Build flast list of allVisibleColumns and HeaderGroups
visibleColumns.forEach(column => { visibleColumns.forEach((column, i) => {
if (column.columns) { if (column.columns) {
allVisibleColumns = allVisibleColumns.concat(column.columns) allVisibleColumns = allVisibleColumns.concat(column.columns)
if (currentSpan.length > 0) { if (currentSpan.length > 0) {
@@ -233,23 +237,13 @@ export default Base =>
row[column.id] = column.accessor(d) row[column.id] = column.accessor(d)
}) })
if (row[subRowsKey]) { if (row[subRowsKey]) {
row[subRowsKey] = row[subRowsKey].map((d, i) => accessRow(d, i, level + 1)) row[subRowsKey] = row[subRowsKey].map((d, i) =>
accessRow(d, i, level + 1)
)
} }
return row return row
} }
let resolvedData = data.map((d, i) => accessRow(d, i))
// // If the data hasn't changed, just use the cached data
let resolvedData = this.resolvedData
// If the data has changed, run the data resolver and cache the result
if (!this.resolvedData || dataChanged) {
resolvedData = resolveData(data)
this.resolvedData = resolvedData
}
// Use the resolved data
resolvedData = resolvedData.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 // If pivoting, recursively group the data
const aggregate = rows => { const aggregate = rows => {
@@ -260,6 +254,11 @@ export default Base =>
}) })
return aggregationValues return aggregationValues
} }
// TODO: Make it possible to fabricate nested rows without pivoting
const aggregatingColumns = allVisibleColumns.filter(
d => !d.expander && d.aggregate
)
if (pivotBy.length) { if (pivotBy.length) {
const groupRecursively = (rows, keys, i = 0) => { const groupRecursively = (rows, keys, i = 0) => {
// This is the last level, just return the rows // This is the last level, just return the rows
@@ -267,17 +266,21 @@ export default Base =>
return rows return rows
} }
// Group the rows together for this level // Group the rows together for this level
let groupedRows = Object.entries(_.groupBy(rows, keys[i])).map(([key, value]) => ({ let groupedRows = Object.entries(
[pivotIDKey]: keys[i], _.groupBy(rows, keys[i])
[pivotValKey]: key, ).map(([key, value]) => {
[keys[i]]: key, return {
[subRowsKey]: value, [pivotIDKey]: keys[i],
[nestingLevelKey]: i, [pivotValKey]: key,
[groupedByPivotKey]: true, [keys[i]]: key,
})) [subRowsKey]: value,
[nestingLevelKey]: i,
[groupedByPivotKey]: true,
}
})
// Recurse into the subRows // Recurse into the subRows
groupedRows = groupedRows.map(rowGroup => { groupedRows = groupedRows.map(rowGroup => {
const subRows = groupRecursively(rowGroup[subRowsKey], keys, i + 1) let subRows = groupRecursively(rowGroup[subRowsKey], keys, i + 1)
return { return {
...rowGroup, ...rowGroup,
[subRowsKey]: subRows, [subRowsKey]: subRows,
@@ -322,7 +325,12 @@ export default Base =>
sortedData: manual sortedData: manual
? resolvedData ? resolvedData
: this.sortData( : this.sortData(
this.filterData(resolvedData, filtered, defaultFilterMethod, allVisibleColumns), this.filterData(
resolvedData,
filtered,
defaultFilterMethod,
allVisibleColumns
),
sorted, sorted,
sortMethodsByColumnID sortMethodsByColumnID
), ),
@@ -330,15 +338,7 @@ export default Base =>
} }
fireFetchData () { fireFetchData () {
// determine the current state, preferring certain state values over props this.props.onFetchData(this.getResolvedState(), this)
const currentState = {
...this.getResolvedState(),
page: this.getStateOrProp('page'),
pageSize: this.getStateOrProp('pageSize'),
filter: this.getStateOrProp('filter'),
}
this.props.onFetchData(currentState, this)
} }
getPropOrState (key) { getPropOrState (key) {
@@ -366,8 +366,11 @@ export default Base =>
// If 'filterAll' is set to true, pass the entire dataset to the filter method // If 'filterAll' is set to true, pass the entire dataset to the filter method
if (column.filterAll) { if (column.filterAll) {
return filterMethod(nextFilter, filteredSoFar, column) return filterMethod(nextFilter, filteredSoFar, column)
} else {
return filteredSoFar.filter(row => {
return filterMethod(nextFilter, row, column)
})
} }
return filteredSoFar.filter(row => filterMethod(nextFilter, row, column))
}, filteredData) }, filteredData)
// Apply the filter to the subrows if we are pivoting, and then // Apply the filter to the subrows if we are pivoting, and then
@@ -408,9 +411,13 @@ export default Base =>
sorted.map(sort => { sorted.map(sort => {
// Support custom sorting methods for each column // Support custom sorting methods for each column
if (sortMethodsByColumnID[sort.id]) { if (sortMethodsByColumnID[sort.id]) {
return (a, b) => sortMethodsByColumnID[sort.id](a[sort.id], b[sort.id], sort.desc) return (a, b) => {
return sortMethodsByColumnID[sort.id](a[sort.id], b[sort.id])
}
}
return (a, b) => {
return this.props.defaultSortMethod(a[sort.id], b[sort.id])
} }
return (a, b) => this.props.defaultSortMethod(a[sort.id], b[sort.id], sort.desc)
}), }),
sorted.map(d => !d.desc), sorted.map(d => !d.desc),
this.props.indexKey this.props.indexKey
@@ -431,7 +438,10 @@ export default Base =>
} }
getMinRows () { getMinRows () {
return _.getFirstDefined(this.props.minRows, this.getStateOrProp('pageSize')) return _.getFirstDefined(
this.props.minRows,
this.getStateOrProp('pageSize')
)
} }
// User actions // User actions
@@ -442,7 +452,9 @@ export default Base =>
if (collapseOnPageChange) { if (collapseOnPageChange) {
newState.expanded = {} newState.expanded = {}
} }
this.setStateWithData(newState, () => onPageChange && onPageChange(page)) this.setStateWithData(newState, () => {
onPageChange && onPageChange(page)
})
} }
onPageSizeChange (newPageSize) { onPageSizeChange (newPageSize) {
@@ -458,14 +470,16 @@ export default Base =>
pageSize: newPageSize, pageSize: newPageSize,
page: newPage, page: newPage,
}, },
() => onPageSizeChange && onPageSizeChange(newPageSize, newPage) () => {
onPageSizeChange && onPageSizeChange(newPageSize, newPage)
}
) )
} }
sortColumn (column, additive) { sortColumn (column, additive) {
const { sorted, skipNextSort, defaultSortDesc } = this.getResolvedState() const { sorted, skipNextSort, defaultSortDesc } = this.getResolvedState()
const firstSortDirection = Object.prototype.hasOwnProperty.call(column, 'defaultSortDesc') const firstSortDirection = column.hasOwnProperty('defaultSortDesc')
? column.defaultSortDesc ? column.defaultSortDesc
: defaultSortDesc : defaultSortDesc
const secondSortDirection = !firstSortDirection const secondSortDirection = !firstSortDirection
@@ -505,18 +519,20 @@ export default Base =>
newSorted = [existing] newSorted = [existing]
} }
} }
} else if (additive) {
newSorted.push({
id: column.id,
desc: firstSortDirection,
})
} else { } else {
newSorted = [ if (additive) {
{ newSorted.push({
id: column.id, id: column.id,
desc: firstSortDirection, desc: firstSortDirection,
}, })
] } else {
newSorted = [
{
id: column.id,
desc: firstSortDirection,
},
]
}
} }
} else { } else {
// Multi-Sort // Multi-Sort
@@ -540,28 +556,35 @@ export default Base =>
if (!additive) { if (!additive) {
newSorted = newSorted.slice(existingIndex, column.length) newSorted = newSorted.slice(existingIndex, column.length)
} }
} else {
// New Sort Column // New Sort Column
} else if (additive) { if (additive) {
newSorted = newSorted.concat( newSorted = newSorted.concat(
column.map(d => ({ column.map(d => ({
id: d.id,
desc: firstSortDirection,
}))
)
} else {
newSorted = column.map(d => ({
id: d.id, id: d.id,
desc: firstSortDirection, desc: firstSortDirection,
})) }))
) }
} else {
newSorted = column.map(d => ({
id: d.id,
desc: firstSortDirection,
}))
} }
} }
this.setStateWithData( this.setStateWithData(
{ {
page: (!sorted.length && newSorted.length) || !additive ? 0 : this.state.page, page:
(!sorted.length && newSorted.length) || !additive
? 0
: this.state.page,
sorted: newSorted, sorted: newSorted,
}, },
() => onSortedChange && onSortedChange(newSorted, column, additive) () => {
onSortedChange && onSortedChange(newSorted, column, additive)
}
) )
} }
@@ -570,12 +593,16 @@ export default Base =>
const { onFilteredChange } = this.props const { onFilteredChange } = this.props
// Remove old filter first if it exists // Remove old filter first if it exists
const newFiltering = (filtered || []).filter(x => x.id !== column.id) const newFiltering = (filtered || []).filter(x => {
if (x.id !== column.id) {
return true
}
})
if (value !== '') { if (value !== '') {
newFiltering.push({ newFiltering.push({
id: column.id, id: column.id,
value, value: value,
}) })
} }
@@ -583,13 +610,16 @@ export default Base =>
{ {
filtered: newFiltering, filtered: newFiltering,
}, },
() => onFilteredChange && onFilteredChange(newFiltering, column, value) () => {
onFilteredChange && onFilteredChange(newFiltering, column, value)
}
) )
} }
resizeColumnStart (event, column, isTouch) { resizeColumnStart (event, column, isTouch) {
event.stopPropagation() event.stopPropagation()
const parentWidth = event.target.parentElement.getBoundingClientRect().width const parentWidth = event.target.parentElement.getBoundingClientRect()
.width
let pageX let pageX
if (isTouch) { if (isTouch) {
@@ -604,7 +634,7 @@ export default Base =>
currentlyResizing: { currentlyResizing: {
id: column.id, id: column.id,
startX: pageX, startX: pageX,
parentWidth, parentWidth: parentWidth,
}, },
}, },
() => { () => {
@@ -623,10 +653,8 @@ export default Base =>
resizeColumnMoving (event) { resizeColumnMoving (event) {
event.stopPropagation() event.stopPropagation()
const { onResizedChange, column } = this.props const { onResizedChange } = this.props
const { resized, currentlyResizing, columns } = this.getResolvedState() const { resized, currentlyResizing } = this.getResolvedState()
const currentColumn = columns.find(c => c.accessor === currentlyResizing.id)
const minResizeWidth = currentColumn ? currentColumn.minResizeWidth : column.minResizeWidth
// Delete old value // Delete old value
const newResized = resized.filter(x => x.id !== currentlyResizing.id) const newResized = resized.filter(x => x.id !== currentlyResizing.id)
@@ -639,9 +667,10 @@ export default Base =>
pageX = event.pageX 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
const newWidth = Math.max( const newWidth = Math.max(
currentlyResizing.parentWidth + pageX - currentlyResizing.startX, currentlyResizing.parentWidth + pageX - currentlyResizing.startX,
minResizeWidth 11
) )
newResized.push({ newResized.push({
@@ -653,13 +682,15 @@ export default Base =>
{ {
resized: newResized, resized: newResized,
}, },
() => onResizedChange && onResizedChange(newResized, event) () => {
onResizedChange && onResizedChange(newResized, event)
}
) )
} }
resizeColumnEnd (event) { resizeColumnEnd (event) {
event.stopPropagation() event.stopPropagation()
const isTouch = event.type === 'touchend' || event.type === 'touchcancel' let isTouch = event.type === 'touchend' || event.type === 'touchcancel'
if (isTouch) { if (isTouch) {
document.removeEventListener('touchmove', this.resizeColumnMoving) document.removeEventListener('touchmove', this.resizeColumnMoving)
+60 -98
View File
@@ -1,58 +1,16 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import classnames from 'classnames' import classnames from 'classnames'
//
// import _ from './utils'
const defaultButton = props => ( const defaultButton = props =>
<button type="button" {...props} className="-btn"> <button type='button' {...props} className='-btn'>
{props.children} {props.children}
</button> </button>
)
export default class ReactTablePagination extends Component { export default class ReactTablePagination extends Component {
static defaultProps = {
PreviousComponent: defaultButton,
NextComponent: defaultButton,
renderPageJump: ({
onChange, value, onBlur, onKeyPress, inputType, pageJumpText,
}) => (
<div className="-pageJump">
<input
aria-label={pageJumpText}
type={inputType}
onChange={onChange}
value={value}
onBlur={onBlur}
onKeyPress={onKeyPress}
/>
</div>
),
renderCurrentPage: page => <span className="-currentPage">{page + 1}</span>,
renderTotalPagesCount: pages => <span className="-totalPages">{pages || 1}</span>,
renderPageSizeOptions: ({
pageSize,
pageSizeOptions,
rowsSelectorText,
onPageSizeChange,
rowsText,
}) => (
<span className="select-wrap -pageSizeOptions">
<select
aria-label={rowsSelectorText}
onChange={e => onPageSizeChange(Number(e.target.value))}
value={pageSize}
>
{pageSizeOptions.map((option, i) => (
// eslint-disable-next-line react/no-array-index-key
<option key={i} value={option}>
{`${option} ${rowsText}`}
</option>
))}
</select>
</span>
),
}
constructor (props) { constructor (props) {
super(props) super()
this.getSafePage = this.getSafePage.bind(this) this.getSafePage = this.getSafePage.bind(this)
this.changePage = this.changePage.bind(this) this.changePage = this.changePage.bind(this)
@@ -64,13 +22,11 @@ export default class ReactTablePagination extends Component {
} }
componentWillReceiveProps (nextProps) { componentWillReceiveProps (nextProps) {
if (this.props.page !== nextProps.page) { this.setState({ page: nextProps.page })
this.setState({ page: nextProps.page })
}
} }
getSafePage (page) { getSafePage (page) {
if (Number.isNaN(page)) { if (isNaN(page)) {
page = this.props.page page = this.props.page
} }
return Math.min(Math.max(page, 0), this.props.pages - 1) return Math.min(Math.max(page, 0), this.props.pages - 1)
@@ -85,35 +41,11 @@ export default class ReactTablePagination extends Component {
} }
applyPage (e) { applyPage (e) {
if (e) { e && e.preventDefault()
e.preventDefault()
}
const page = this.state.page const page = this.state.page
this.changePage(page === '' ? this.props.page : page) this.changePage(page === '' ? this.props.page : page)
} }
getPageJumpProperties () {
return {
onKeyPress: e => {
if (e.which === 13 || e.keyCode === 13) {
this.applyPage()
}
},
onBlur: this.applyPage,
value: this.state.page === '' ? '' : this.state.page + 1,
onChange: e => {
const val = e.target.value
const page = val - 1
if (val === '') {
return this.setState({ page: val })
}
this.setState({ page: this.getSafePage(page) })
},
inputType: this.state.page === '' ? 'text' : 'number',
pageJumpText: this.props.pageJumpText,
}
}
render () { render () {
const { const {
// Computed // Computed
@@ -128,19 +60,18 @@ export default class ReactTablePagination extends Component {
canNext, canNext,
onPageSizeChange, onPageSizeChange,
className, className,
PreviousComponent, PreviousComponent = defaultButton,
NextComponent, NextComponent = defaultButton,
renderPageJump,
renderCurrentPage,
renderTotalPagesCount,
renderPageSizeOptions,
} = this.props } = this.props
return ( return (
<div className={classnames(className, '-pagination')} style={this.props.style}> <div
<div className="-previous"> className={classnames(className, '-pagination')}
style={this.props.paginationStyle}
>
<div className='-previous'>
<PreviousComponent <PreviousComponent
onClick={() => { onClick={e => {
if (!canPrevious) return if (!canPrevious) return
this.changePage(page - 1) this.changePage(page - 1)
}} }}
@@ -149,24 +80,55 @@ export default class ReactTablePagination extends Component {
{this.props.previousText} {this.props.previousText}
</PreviousComponent> </PreviousComponent>
</div> </div>
<div className="-center"> <div className='-center'>
<span className="-pageInfo"> <span className='-pageInfo'>
{this.props.pageText}{' '} {this.props.pageText}{' '}
{showPageJump ? renderPageJump(this.getPageJumpProperties()) : renderCurrentPage(page)}{' '} {showPageJump
{this.props.ofText} {renderTotalPagesCount(pages)} ? <div className='-pageJump'>
<input
type={this.state.page === '' ? 'text' : 'number'}
onChange={e => {
const val = e.target.value
const page = val - 1
if (val === '') {
return this.setState({ page: val })
}
this.setState({ page: this.getSafePage(page) })
}}
value={this.state.page === '' ? '' : this.state.page + 1}
onBlur={this.applyPage}
onKeyPress={e => {
if (e.which === 13 || e.keyCode === 13) {
this.applyPage()
}
}}
/>
</div>
: <span className='-currentPage'>
{page + 1}
</span>}{' '}
{this.props.ofText}{' '}
<span className='-totalPages'>{pages || 1}</span>
</span> </span>
{showPageSizeOptions && {showPageSizeOptions &&
renderPageSizeOptions({ <span className='select-wrap -pageSizeOptions'>
pageSize, <select
rowsSelectorText: this.props.rowsSelectorText, onChange={e => onPageSizeChange(Number(e.target.value))}
pageSizeOptions, value={pageSize}
onPageSizeChange, >
rowsText: this.props.rowsText, {pageSizeOptions.map((option, i) => {
})} return (
<option key={i} value={option}>
{option} {this.props.rowsText}
</option>
)
})}
</select>
</span>}
</div> </div>
<div className="-next"> <div className='-next'>
<NextComponent <NextComponent
onClick={() => { onClick={e => {
if (!canNext) return if (!canNext) return
this.changePage(page + 1) this.changePage(page + 1)
}} }}
-169
View File
@@ -1,169 +0,0 @@
import PropTypes from 'prop-types'
export default {
// General
data: PropTypes.any,
loading: PropTypes.bool,
showPagination: PropTypes.bool,
showPaginationTop: PropTypes.bool,
showPaginationBottom: PropTypes.bool,
showPageSizeOptions: PropTypes.bool,
pageSizeOptions: PropTypes.array,
defaultPageSize: PropTypes.number,
showPageJump: PropTypes.bool,
collapseOnSortingChange: PropTypes.bool,
collapseOnPageChange: PropTypes.bool,
collapseOnDataChange: PropTypes.bool,
freezeWhenExpanded: PropTypes.bool,
sortable: PropTypes.bool,
resizable: PropTypes.bool,
filterable: PropTypes.bool,
defaultSortDesc: PropTypes.bool,
defaultSorted: PropTypes.array,
defaultFiltered: PropTypes.array,
defaultResized: PropTypes.array,
defaultExpanded: PropTypes.object,
defaultFilterMethod: PropTypes.func,
defaultSortMethod: PropTypes.func,
// Controlled State Callbacks
onPageChange: PropTypes.func,
onPageSizeChange: PropTypes.func,
onSortedChange: PropTypes.func,
onFilteredChange: PropTypes.func,
onResizedChange: PropTypes.func,
onExpandedChange: PropTypes.func,
// Pivoting
pivotBy: PropTypes.array,
// Key Constants
pivotValKey: PropTypes.string,
pivotIDKey: PropTypes.string,
subRowsKey: PropTypes.string,
aggregatedKey: PropTypes.string,
nestingLevelKey: PropTypes.string,
originalKey: PropTypes.string,
indexKey: PropTypes.string,
groupedByPivotKey: PropTypes.string,
// Server-side Callbacks
onFetchData: PropTypes.func,
// Classes
className: PropTypes.string,
style: PropTypes.object,
// Component decorators
getProps: PropTypes.func,
getTableProps: PropTypes.func,
getTheadGroupProps: PropTypes.func,
getTheadGroupTrProps: PropTypes.func,
getTheadGroupThProps: PropTypes.func,
getTheadProps: PropTypes.func,
getTheadTrProps: PropTypes.func,
getTheadThProps: PropTypes.func,
getTheadFilterProps: PropTypes.func,
getTheadFilterTrProps: PropTypes.func,
getTheadFilterThProps: PropTypes.func,
getTbodyProps: PropTypes.func,
getTrGroupProps: PropTypes.func,
getTrProps: PropTypes.func,
getTdProps: PropTypes.func,
getTfootProps: PropTypes.func,
getTfootTrProps: PropTypes.func,
getTfootTdProps: PropTypes.func,
getPaginationProps: PropTypes.func,
getLoadingProps: PropTypes.func,
getNoDataProps: PropTypes.func,
getResizerProps: PropTypes.func,
// Global Column Defaults
columns: PropTypes.arrayOf(
PropTypes.shape({
// Renderers
Cell: PropTypes.oneOfType([PropTypes.element, PropTypes.string, PropTypes.func]),
Header: PropTypes.oneOfType([PropTypes.element, PropTypes.string, PropTypes.func]),
Footer: PropTypes.oneOfType([PropTypes.element, PropTypes.string, PropTypes.func]),
Aggregated: PropTypes.oneOfType([PropTypes.element, PropTypes.string, PropTypes.func]),
Pivot: PropTypes.oneOfType([PropTypes.element, PropTypes.string, PropTypes.func]),
PivotValue: PropTypes.oneOfType([PropTypes.element, PropTypes.string, PropTypes.func]),
Expander: PropTypes.oneOfType([PropTypes.element, PropTypes.string, PropTypes.func]),
Filter: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
// All Columns
sortable: PropTypes.bool, // use table default
resizable: PropTypes.bool, // use table default
filterable: PropTypes.bool, // use table default
show: PropTypes.bool,
minWidth: PropTypes.number,
minResizeWidth: PropTypes.number,
// Cells only
className: PropTypes.string,
style: PropTypes.object,
getProps: PropTypes.func,
// Pivot only
aggregate: PropTypes.func,
// Headers only
headerClassName: PropTypes.string,
headerStyle: PropTypes.object,
getHeaderProps: PropTypes.func,
// Footers only
footerClassName: PropTypes.string,
footerStyle: PropTypes.object,
getFooterProps: PropTypes.func,
filterMethod: PropTypes.func,
filterAll: PropTypes.bool,
sortMethod: PropTypes.func,
})
),
// Global Expander Column Defaults
expanderDefaults: PropTypes.shape({
sortable: PropTypes.bool,
resizable: PropTypes.bool,
filterable: PropTypes.bool,
width: PropTypes.number,
}),
pivotDefaults: PropTypes.object,
// Text
previousText: PropTypes.node,
nextText: PropTypes.node,
loadingText: PropTypes.node,
noDataText: PropTypes.node,
pageText: PropTypes.node,
ofText: PropTypes.node,
rowsText: PropTypes.node,
pageJumpText: PropTypes.node,
rowsSelectorText: PropTypes.node,
// Components
TableComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
TheadComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
TbodyComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
TrGroupComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
TrComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
ThComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
TdComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
TfootComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
FilterComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
ExpanderComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
PivotValueComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
AggregatedComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
// 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]),
NextComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
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]),
}
+28 -30
View File
@@ -30,9 +30,7 @@ function get (obj, path, def) {
let val let val
try { try {
val = pathObj.reduce((current, pathPart) => current[pathPart], obj) val = pathObj.reduce((current, pathPart) => current[pathPart], obj)
} catch (e) { } catch (e) {}
// continue regardless of error
}
return typeof val !== 'undefined' ? val : def return typeof val !== 'undefined' ? val : def
} }
@@ -61,7 +59,7 @@ function last (arr) {
function range (n) { function range (n) {
const arr = [] const arr = []
for (let i = 0; i < n; i += 1) { for (let i = 0; i < n; i++) {
arr.push(n) arr.push(n)
} }
return arr return arr
@@ -69,7 +67,7 @@ function range (n) {
function orderBy (arr, funcs, dirs, indexKey) { function orderBy (arr, funcs, dirs, indexKey) {
return arr.sort((rowA, rowB) => { return arr.sort((rowA, rowB) => {
for (let i = 0; i < funcs.length; i += 1) { for (let i = 0; i < funcs.length; i++) {
const comp = funcs[i] const comp = funcs[i]
const desc = dirs[i] === false || dirs[i] === 'desc' const desc = dirs[i] === false || dirs[i] === 'desc'
const sortInt = comp(rowA, rowB) const sortInt = comp(rowA, rowB)
@@ -78,13 +76,15 @@ function orderBy (arr, funcs, dirs, indexKey) {
} }
} }
// Use the row index for tie breakers // Use the row index for tie breakers
return dirs[0] ? rowA[indexKey] - rowB[indexKey] : rowB[indexKey] - rowA[indexKey] return dirs[0]
? rowA[indexKey] - rowB[indexKey]
: rowB[indexKey] - rowA[indexKey]
}) })
} }
function remove (a, b) { function remove (a, b) {
return a.filter((o, i) => { return a.filter(function (o, i) {
const r = b(o) var r = b(o)
if (r) { if (r) {
a.splice(i, 1) a.splice(i, 1)
return true return true
@@ -109,7 +109,7 @@ function clone (a) {
} }
function getFirstDefined (...args) { function getFirstDefined (...args) {
for (let i = 0; i < args.length; i += 1) { for (var i = 0; i < args.length; i++) {
if (typeof args[i] !== 'undefined') { if (typeof args[i] !== 'undefined') {
return args[i] return args[i]
} }
@@ -117,18 +117,19 @@ function getFirstDefined (...args) {
} }
function sum (arr) { function sum (arr) {
return arr.reduce((a, b) => a + b, 0) return arr.reduce((a, b) => {
return a + b
}, 0)
} }
function makeTemplateComponent (compClass, displayName) { function makeTemplateComponent (compClass, displayName) {
if (!displayName) { if (!displayName) {
throw new Error('No displayName found for template component:', compClass) throw new Error('No displayName found for template component:', compClass)
} }
const cmp = ({ children, className, ...rest }) => ( const cmp = ({ children, className, ...rest }) =>
<div className={classnames(compClass, className)} {...rest}> <div className={classnames(compClass, className)} {...rest}>
{children} {children}
</div> </div>
)
cmp.displayName = displayName cmp.displayName = displayName
return cmp return cmp
} }
@@ -144,7 +145,7 @@ function groupBy (xs, key) {
function asPx (value) { function asPx (value) {
value = Number(value) value = Number(value)
return Number.isNaN(value) ? null : `${value}px` return Number.isNaN(value) ? null : value + 'px'
} }
function isArray (a) { function isArray (a) {
@@ -167,7 +168,7 @@ function flattenDeep (arr, newArr = []) {
if (!isArray(arr)) { if (!isArray(arr)) {
newArr.push(arr) newArr.push(arr)
} else { } else {
for (let i = 0; i < arr.length; i += 1) { for (var i = 0; i < arr.length; i++) {
flattenDeep(arr[i], newArr) flattenDeep(arr[i], newArr)
} }
} }
@@ -184,17 +185,14 @@ function splitProps ({ className, style, ...rest }) {
function compactObject (obj) { function compactObject (obj) {
const newObj = {} const newObj = {}
if (obj) { for (var key in obj) {
Object.keys(obj).map(key => { if (
if ( obj.hasOwnProperty(key) &&
Object.prototype.hasOwnProperty.call(obj, key) && obj[key] !== undefined &&
obj[key] !== undefined && typeof obj[key] !== 'undefined'
typeof obj[key] !== 'undefined' ) {
) { newObj[key] = obj[key]
newObj[key] = obj[key] }
}
return true
})
} }
return newObj return newObj
} }
@@ -204,9 +202,9 @@ function isSortingDesc (d) {
} }
function normalizeComponent (Comp, params = {}, fallback = Comp) { function normalizeComponent (Comp, params = {}, fallback = Comp) {
return typeof Comp === 'function' ? ( return typeof Comp === 'function'
<Comp {...params} /> ? Object.getPrototypeOf(Comp).isReactComponent
) : ( ? <Comp {...params} />
fallback : Comp(params)
) : fallback
} }
+20
View File
@@ -0,0 +1,20 @@
const webpack = require('webpack')
module.exports = {
entry: './lib/index.js',
output: {
filename: './react-table.js',
libraryTarget: 'umd',
library: 'ReactTable'
},
externals: {
react: {
root: 'React',
commonjs2: 'react',
commonjs: 'react',
amd: 'react'
}
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
}
+1455 -1599
View File
File diff suppressed because it is too large Load Diff