mirror of
https://github.com/gosticks/react-table.git
synced 2026-08-19 16:30:21 +00:00
Compare commits
119
Commits
v7.0.0-rc.14
...
v7.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dd628144c | ||
|
|
0d0ce01544 | ||
|
|
afa21b0ad4 | ||
|
|
f42300d9e2 | ||
|
|
920420e330 | ||
|
|
6482869e8a | ||
|
|
42b41d1260 | ||
|
|
3aa016e056 | ||
|
|
bfcc08e7eb | ||
|
|
512f615988 | ||
|
|
63754a58dd | ||
|
|
4a483214ff | ||
|
|
b2af02407b | ||
|
|
b9f7bda223 | ||
|
|
acdb28df60 | ||
|
|
34be17b093 | ||
|
|
544c30b358 | ||
|
|
343e76ce65 | ||
|
|
23c9ad23fa | ||
|
|
9e1a3bfe99 | ||
|
|
6d4a733a88 | ||
|
|
c740a0c7af | ||
|
|
48d6fcd60d | ||
|
|
10b84faa29 | ||
|
|
292bbea1bf | ||
|
|
2a72667ec1 | ||
|
|
c92f3fb855 | ||
|
|
fe1c9b4b11 | ||
|
|
e28fa775f2 | ||
|
|
d9b2a9dc54 | ||
|
|
73ed7efbe6 | ||
|
|
7498059e98 | ||
|
|
a52a2e1265 | ||
|
|
facb216f5c | ||
|
|
b1a31b8176 | ||
|
|
5949932e77 | ||
|
|
f330b795dc | ||
|
|
07625844fd | ||
|
|
6c2df78b20 | ||
|
|
917cfcc910 | ||
|
|
b161d6d47b | ||
|
|
a3c0d22dd6 | ||
|
|
da82358d14 | ||
|
|
7eabe4b5cc | ||
|
|
083e81dc81 | ||
|
|
53f379f789 | ||
|
|
2bf99aaea0 | ||
|
|
164b3d6b7e | ||
|
|
2f14310808 | ||
|
|
32ca5a240b | ||
|
|
98b3161b2d | ||
|
|
4f8f7cd118 | ||
|
|
074211003a | ||
|
|
1eb492694a | ||
|
|
5ab8842ccf | ||
|
|
6b11d48b43 | ||
|
|
14ff3ef6c5 | ||
|
|
fce2ebe763 | ||
|
|
475c0a0637 | ||
|
|
af58d06a1e | ||
|
|
42ad10a7d8 | ||
|
|
812b416367 | ||
|
|
c984796190 | ||
|
|
05d946ecfb | ||
|
|
49bdf95205 | ||
|
|
677dcdc250 | ||
|
|
7dc9b85165 | ||
|
|
21826a93e0 | ||
|
|
e62fc0c535 | ||
|
|
b989a8fa76 | ||
|
|
b1b71103c4 | ||
|
|
8f5fc11905 | ||
|
|
29c7cb3483 | ||
|
|
dd3b50cc17 | ||
|
|
2eb5fa9c1b | ||
|
|
6db990250e | ||
|
|
5f9950339b | ||
|
|
5486293677 | ||
|
|
703f83e6f4 | ||
|
|
140ca06086 | ||
|
|
8277bfd4db | ||
|
|
7645107d3d | ||
|
|
8ea93b9fed | ||
|
|
6ebf3183a0 | ||
|
|
5778bc3dce | ||
|
|
3b4a120ff5 | ||
|
|
2a0dc6778d | ||
|
|
8e310bd0e6 | ||
|
|
c7b146cf1e | ||
|
|
68606bb77c | ||
|
|
c880da6c3d | ||
|
|
9d97ac29a6 | ||
|
|
b018ed64ed | ||
|
|
71e7c542c4 | ||
|
|
e3223ed1b9 | ||
|
|
35d36db4c9 | ||
|
|
649259ba77 | ||
|
|
017ad2884e | ||
|
|
59c4558dc8 | ||
|
|
9baf3c275b | ||
|
|
7db50b0f21 | ||
|
|
ac1eeb4756 | ||
|
|
1d8ffb18f7 | ||
|
|
ed2fe54371 | ||
|
|
4a93e0f306 | ||
|
|
66cc06365c | ||
|
|
ede79d8b38 | ||
|
|
d43e4be548 | ||
|
|
58c28b574a | ||
|
|
45e5bed468 | ||
|
|
94a9b49187 | ||
|
|
aa6d859ce9 | ||
|
|
555fc828f5 | ||
|
|
3e789ffe83 | ||
|
|
b430f8e702 | ||
|
|
cef55d58f3 | ||
|
|
d18f1ba4d8 | ||
|
|
73344a47e4 | ||
|
|
bf82d3c656 |
@@ -11,7 +11,8 @@
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": ["@babel/preset-env", "@babel/react"]
|
||||
"presets": ["@babel/preset-env", "@babel/react"],
|
||||
"plugins": ["@babel/plugin-transform-runtime"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
jobs:
|
||||
build-and-test:
|
||||
name: '${{ matrix.platform }}: node.js ${{ matrix.node-version }}'
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ubuntu-latest, windows-latest, macos-latest]
|
||||
node-version: [10, 12]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
CI: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Build and test
|
||||
run: |
|
||||
yarn install
|
||||
yarn test
|
||||
@@ -1,3 +0,0 @@
|
||||
size-files:
|
||||
- sizes-cjs.json
|
||||
- sizes-es.json
|
||||
@@ -0,0 +1,15 @@
|
||||
name: Compressed Size
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2-beta
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: preactjs/compressed-size-action@v1
|
||||
with:
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
@@ -0,0 +1,26 @@
|
||||
name: react-table tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: 'node ${{ matrix.node }} ${{ matrix.os }} '
|
||||
runs-on: '${{ matrix.os }}'
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node: [12, 10]
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- uses: actions/checkout@v2-beta
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- run: npm i -g yarn
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: yarn test:ci
|
||||
@@ -10,4 +10,6 @@ react-table.css
|
||||
.idea
|
||||
.DS_Store
|
||||
.history
|
||||
.vscode
|
||||
package-lock.json
|
||||
coverage
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
hooks: {
|
||||
'pre-commit': 'lint-staged && yarn test:ci',
|
||||
// 'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
|
||||
},
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"dist/index.js": {
|
||||
"bundled": 108756,
|
||||
"minified": 50398,
|
||||
"gzipped": 13421
|
||||
},
|
||||
"dist/index.es.js": {
|
||||
"bundled": 107845,
|
||||
"minified": 49586,
|
||||
"gzipped": 13253,
|
||||
"treeshaked": {
|
||||
"rollup": {
|
||||
"code": 80,
|
||||
"import_statements": 21
|
||||
},
|
||||
"webpack": {
|
||||
"code": 8168
|
||||
}
|
||||
}
|
||||
},
|
||||
"dist\\index.js": {
|
||||
"bundled": 104977,
|
||||
"minified": 48560,
|
||||
"gzipped": 13057
|
||||
},
|
||||
"dist\\index.es.js": {
|
||||
"bundled": 104090,
|
||||
"minified": 47770,
|
||||
"gzipped": 12900,
|
||||
"treeshaked": {
|
||||
"rollup": {
|
||||
"code": 80,
|
||||
"import_statements": 21
|
||||
},
|
||||
"webpack": {
|
||||
"code": 8227
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 10
|
||||
- 12
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- windows
|
||||
|
||||
env:
|
||||
global:
|
||||
- YARN_GPG=no
|
||||
|
||||
cache: yarn
|
||||
|
||||
script: yarn test
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"titleBar.inactiveBackground": "#ff4154", // change this color!
|
||||
"titleBar.activeBackground": "#ff4154", // change this color!
|
||||
"titleBar.inactiveForeground": "#ffffff", // change this color!
|
||||
"titleBar.activeForeground": "#ffffff" // change this color!
|
||||
}
|
||||
}
|
||||
+64
-1
@@ -1,8 +1,71 @@
|
||||
## 7.0.0 🎉
|
||||
|
||||
- Fixed an issue where page options array could be empty
|
||||
- Fixed an issue where duplicate columns would be silently deduped. There is now a warning when duplicate columns are found based on their IDs
|
||||
- Moved some functions around so they will get treeshaked with their respective plugins that use them.
|
||||
- Fixed an issue where filters, sorting, or grouping changes would not reset pagination
|
||||
- Added table and column level options for disabling global filters
|
||||
- Fixed an issue where row selection was not deselecting rows
|
||||
- Fixed an issue where flex table rendering was not giving the table a minimum width with necessary
|
||||
- Fixed an issue where row selection would not work when using other row-transformative plugins like filters or grouping
|
||||
- Fixed an issue where header groups were not memoized correctly
|
||||
|
||||
## 7.0.0-rc.16
|
||||
|
||||
- Moved away from snapshot tests. No more testing implementation details.
|
||||
- Added `visibleColumns` and `visibleColumnsDeps` hooks to manipulate columns after all data is processed. Further visibility processing may result in these columns not being visible, such as `hiddenColumn` state
|
||||
- The `useRows` hook has been deprecated due to its dangerous nature 💀
|
||||
- Added the `instance.rowsById` object
|
||||
- Renamed `instance.flatColumns` to `instance.allColumns` which now accumulates ALL columns created for the table, visible or not.
|
||||
- Added the `instance.visibleColumns` object
|
||||
- Fix an issue where `useAsyncDebounce` would crash when passed arguments
|
||||
- Started development on the `usePivotColumns` plugin, which can be tested currently using the `_UNSTABLE_usePivotColumns` export.
|
||||
- Renamed `cell.isRepeatedValue` to `cell.isPlaceholder`
|
||||
- Removed `useConsumeHookGetter` as it was inefficient most of the time and noisy
|
||||
- All hooks are now "consumed" right after main plugin functions are run. This means that any attempt to add a plugin after that will result in a runtime error (for good reason, since using hook points should not be a conditional or async operation)
|
||||
- Added `instance.getHooks` for getting the list of hooks that was captured after plugins are run
|
||||
- Normalized all "toggle" actions to use an optional `value` property to set the value instead of toggle. Previously properties like `selected`, `groupBy`, etc. were used, but not any more!
|
||||
- Undocument `instance.dispatch`. Both plugins and users should be interacting with the table via methods assigned to the instance and other structures on the table. This should both reduce the surface API that React Table needs to expose and also the amount of documentation that is needed to understand how to use the API.
|
||||
- `useRowState`'s `initialRowStateAccessor` and `initialCellStateAccessor` options now have a default of `row => ({})` and `cell => ({})` respectively.
|
||||
- Removed the concept of complex aggregations (eg. `column.aggregate = ['sum', 'count']`). Instead, a better aggregation function signature is now used to allow for leaf node aggregation when needed.
|
||||
- Added the `column.aggregateValue` option which allows resolving (or pre-aggregating) a cell's value before it is grouped and aggregated across rows. This is useful for cell values that are not primitive, eg. an array of values that you may want to unique and count before summing that count across your groupings
|
||||
- The function signature for aggregation functions has changed to be `(leafValues, aggregatedValues) => aggregatedValue` where `leafValues` is a flat array containing all leaf rows currently grouped at the aggregation level and `aggregatedValues` is an array containing the aggregated values from the immediate child sub rows. Each has purpose in the types of aggregations they power where optimizations are made for either accuracy or performance.
|
||||
- Fixed an issue where `setGlobalFilter` was not a stable callback
|
||||
- Added a Bootstrap UI example
|
||||
- Added fixed with column support for useFlexLayout
|
||||
- Fixed an issue where `manualGlobalFilter` was not resetting pagination properly
|
||||
- Fixed an issue where `useGlobalFilter` could be placed after `usePagination`
|
||||
- Added the sort order direction as a parameter to the sortMethod function
|
||||
- Fixed an issue where user filter types were not being referenced correctly
|
||||
- Renamed the `row.getExpandedToggleProps` to `row.getToggleRowExpandedProps`
|
||||
- Renamed the `row.toggleExpanded` method to `row.toggleRowExpanded`
|
||||
- Added the `instance.toggleRowExpanded` and `instance.toggleAllRowsExpanded` methods
|
||||
- Added the `instance.getToggleAllRowsExpandedProps` prop getter
|
||||
- Added the `instance.filteredRowsById` property
|
||||
- Added the `instance.preFilteredRowsById` property
|
||||
- useFilters now properly updates the `instance.rowsById` property
|
||||
- Added the `instance.globalFilteredRowsById` property
|
||||
- Added the `instance.preGlobalFilteredRowsById` property
|
||||
- useGlobalFilter now properly updates the `instance.rowsById` property
|
||||
- Added the `instance.nonGroupedFlatRows` property
|
||||
- Added the `instance.nonGroupedRowsById` property
|
||||
- Added the `instance.onlyGroupedFlatRows` property
|
||||
- Added the `instance.onlyGroupedRowsById` property
|
||||
- Improved the api around ensuring plugin ordering
|
||||
- Added more plugin order rules to avoid strange plugin integration results
|
||||
|
||||
## 7.0.0-rc.15
|
||||
|
||||
- Added `useGlobalFilter` hook for performing table-wide filtering
|
||||
- Filter function signature has changed to supply an array of column IDs (to support both the tranditional single column style and the new multi-column search style introduced with `useGlobalFilter`).
|
||||
- Removed the `column` parameter from the filter function signature as it was unused and no longer made sense with the array of IDs change above.
|
||||
- Updated the `filtering` example to use a global filter in addition to the column filters
|
||||
|
||||
## 7.0.0-rc.14
|
||||
|
||||
- Changed the function signature for all propGetter hooks to accept a single object of named meta properties instead of a variable length of meta arguments. The user props object has also been added as a property to all prop getters. For example, `hooks.getRowProps.push((props, instance, row) => [...])` is now written `hooks.getRowProps.push((props, { instance, row, userProps }) => [...])`
|
||||
- Changed the function signature for all reduceHooks accept a single object of named meta properties instead of a variable length of meta arguments. For example, `hooks.flatColumns.push((flatColumns, instance) => flatColumns)` is now written `hooks.flatColumns.push((flatColumns, { instance }) => flatColumns)`
|
||||
- Changed the function signature for all loopHooks accept a single object of named meta properties instead of a variable length of meta arguments. For example, `hooks.prepareRow.push((row, instance) => void)` is now written `hooks.prepareRow.push((row { instance }) => void)`
|
||||
- Changed the function signature for all loopHooks accept a single object of named meta properties instead of a variable length of meta arguments. For example, `hooks.prepareRow.push((row, instance) => void)` is now written `hooks.prepareRow.push((row, { instance }) => void)`
|
||||
|
||||
## 7.0.0-rc.13
|
||||
|
||||
|
||||
@@ -4,16 +4,21 @@
|
||||
|
||||
Hooks for building **lightweight, fast and extendable datagrids** for React
|
||||
|
||||
[](https://twitter.com/intent/tweet?button_hashtag=TanStack)
|
||||
|
||||
<!-- [](https://github.com/tannerlinsley/react-table/actions) -->
|
||||
|
||||
[](https://travis-ci.org/tannerlinsley/react-table)
|
||||
[](https://bundlephobia.com/result?p=react-table@next)
|
||||
[](https://npmjs.com/package/react-table)
|
||||
[](https://spectrum.chat/react-table)
|
||||
[](https://github.com/tannerlinsley/react-table)
|
||||
[](https://twitter.com/tannerlinsley)
|
||||
<a href="https://twitter.com/intent/tweet?button_hashtag=TanStack" target="\_parent">
|
||||
<img alt="#TanStack" src="https://img.shields.io/twitter/url?color=%2308a0e9&label=%23TanStack&style=social&url=https%3A%2F%2Ftwitter.com%2Fintent%2Ftweet%3Fbutton_hashtag%3DTanStack">
|
||||
</a><a href="https://github.com/tannerlinsley/react-table/actions?table=workflow%3A%22react-table+tests%22">
|
||||
<img src="https://github.com/tannerlinsley/react-table/workflows/react-table%20tests/badge.svg" />
|
||||
</a><a href="https://npmjs.com/package/react-table" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/npm/dm/react-table.svg" />
|
||||
</a><a href="https://bundlephobia.com/result?p=react-table@latest" target="\_parent">
|
||||
<img alt="" src="https://badgen.net/bundlephobia/minzip/react-table@latest" />
|
||||
</a><a href="https://spectrum.chat/react-table">
|
||||
<img alt="Join the community on Spectrum" src="https://withspectrum.github.io/badge/badge.svg" />
|
||||
</a><a href="https://github.com/tannerlinsley/react-table" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/github/stars/tannerlinsley/react-table.svg?style=social&label=Star" />
|
||||
</a><a href="https://twitter.com/tannerlinsley" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/twitter/follow/tannerlinsley.svg?style=social&label=Follow" />
|
||||
</a>
|
||||
|
||||
Enjoy this library? Try them all! [React Query](https://github.com/tannerlinsley/react-query), [React Form](https://github.com/tannerlinsley/react-form), [React Charts](https://github.com/tannerlinsley/react-charts)
|
||||
|
||||
@@ -81,11 +86,6 @@ This library is being built and maintained by me, @tannerlinsley and I am always
|
||||
<img width='225' src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/sponsor-retool.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="http://bjntech.com/index.html?utm_campaign=react_table" target="_blank">
|
||||
<img width='225' src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/sponsor-bjn.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://github.com/sponsors/tannerlinsley" target="_blank">
|
||||
Become a Sponsor!
|
||||
@@ -108,11 +108,6 @@ This library is being built and maintained by me, @tannerlinsley and I am always
|
||||
<img width='225' src="https://nozzle.io/img/logo-blue.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://zappi.io/web/" target="_blank">
|
||||
<img width='225' src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/sponsor-zappi.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://github.com/sponsors/tannerlinsley" target="_blank">
|
||||
Become a Sponsor!
|
||||
@@ -129,6 +124,11 @@ This library is being built and maintained by me, @tannerlinsley and I am always
|
||||
<a href="https://github.com/sponsors/tannerlinsley/" target="_blank">
|
||||
<img width='150' src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/silver.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tripwire.com" target="_blank">
|
||||
<img width='225' src="https://www.tripwire.com/-/media/tripwiredotcom/icons/tripwire-logo-footer.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://github.com/sponsors/tannerlinsley" target="_blank">
|
||||
@@ -150,7 +150,8 @@ This library is being built and maintained by me, @tannerlinsley and I am always
|
||||
<td>
|
||||
<ul>
|
||||
<li>Jon Eickmeier</li>
|
||||
<li><a href="https://github.com/Shah-Sahab">Syed Hussain<a></li>
|
||||
<li><a href="https://github.com/rhefner">Richard Hefner (@rhefner)</a></li>
|
||||
<li><a href="https://gitHub.com/snorkypie"> Steeve Lennmark (@snorkypie)</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
@@ -172,18 +173,18 @@ This library is being built and maintained by me, @tannerlinsley and I am always
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Sortmy.games</li>
|
||||
<li>Hugo Meissner</li>
|
||||
<li>Benoit Leger-Derville</li>
|
||||
<li>Thomas Funk</li>
|
||||
<li>Dan Houle</li>
|
||||
<li>David Pickut</li>
|
||||
<li>Jordan Soltman</li>
|
||||
<li>Robert Tajnšek</li>
|
||||
<li>Pekka Tapani</li>
|
||||
<li>Eric Lanehart (@pushred)</li>
|
||||
<li>Anish P Patel (@anishpatelyaadada)</li>
|
||||
<li>Alin Porumb (@alinporumb)</li>
|
||||
<li>Hugo Meissner</li>
|
||||
<li>Benoit Leger-Derville</li>
|
||||
<li>Thomas Funk</li>
|
||||
<li>David Pickut</li>
|
||||
<li>Jordan Soltman</li>
|
||||
<li>Robert Tajnšek</li>
|
||||
<li>Eric Lanehart (@pushred)</li>
|
||||
<li>Anish P Patel (@anishpatelyaadada)</li>
|
||||
<li>Alin Porumb (@alinporumb)</li>
|
||||
<li>Janus Reith (@janus-reith)</li>
|
||||
<li>Timo Mämecke (@timomeh)</li>
|
||||
<li>Tyler Reiff (@reiff12)</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = { extends: ['@commitlint/config-conventional'] }
|
||||
@@ -13,5 +13,5 @@ module.exports = {
|
||||
rootDir: path.resolve(__dirname, '../../'),
|
||||
roots: ['<rootDir>/src', __dirname],
|
||||
transformIgnorePatterns: ['node_modules'],
|
||||
snapshotSerializers: [require.resolve('snapshot-diff/serializer.js')],
|
||||
collectCoverageFrom: ['src/**/*.js', '!**/*.test.js'],
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
import '@testing-library/jest-dom/extend-expect'
|
||||
import 'snapshot-diff/extend-expect'
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ React Table is essentially a compatible collection of **custom React hooks**:
|
||||
- Core Plugin Hooks
|
||||
- [`useGroupBy`](./useGroupBy.md)
|
||||
- [`useFilters`](./useFilters.md)
|
||||
- [`useGlobalFilter`](./useGlobalFilter.md)
|
||||
- [`useSortBy`](./useSortBy.md)
|
||||
- [`useExpanded`](./useExpanded.md)
|
||||
- [`usePagination`](./usePagination.md)
|
||||
@@ -54,7 +55,7 @@ const instance = useTable(
|
||||
|
||||
This multi-stage process is the secret sauce that allows React Table plugin hooks to work together and compose nicely, while not stepping on each others toes.
|
||||
|
||||
To dive deeper into plugins, see Plugins](TODO) and the [Plugin Guide
|
||||
To dive deeper into plugins, see Plugins and the Plugin Guide
|
||||
|
||||
### Plugin Hook Order & Consistency
|
||||
|
||||
|
||||
@@ -41,6 +41,13 @@ The following properties are available on the table instance returned from `useT
|
||||
|
||||
- `rows: Array<Row>`
|
||||
- An array of **expanded** rows.
|
||||
- `toggleRowExpanded: Function(rowId, isExpanded?)`
|
||||
- A function to toggle whether a row is expanded or not. The `isExpanded` boolean is optional, otherwise it will be a true toggle action
|
||||
- `toggleAllRowsExpanded: Function(isExpanded?)`
|
||||
- A function to toggle whether all of the rows in the table are expanded or not. The `isExpanded` boolean is optional, otherwise it will be a true toggle action
|
||||
- `isAllRowsExpanded`
|
||||
- `getToggleAllRowsExpandedProps: Function(userProps) => props`
|
||||
- A prop getter function that returns all necessary props for an element to be clicked and toggle all of the rows expanded or not.
|
||||
|
||||
### Row Properties
|
||||
|
||||
@@ -48,7 +55,7 @@ The following additional properties are available on every `row` object returned
|
||||
|
||||
- `isExpanded: Bool`
|
||||
- If `true`, this row is in an expanded state.
|
||||
- `toggleExpanded: Function(?isExpanded: Bool) => void`
|
||||
- `toggleRowExpanded: Function(?isExpanded: Bool) => void`
|
||||
- This function will toggle the expanded state of a row between `true` and `false` or, if an `isExpanded` boolean is passed to the function, it will be set as the new `isExpanded` value.
|
||||
- Rows with a hard-coded `manualExpandedKey` (defaults to `expanded`) set to `true` are not affected by this function or the internal expanded state.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
- Plugin Hook
|
||||
- Optional
|
||||
|
||||
`useFilters` is the hook that implements **row filtering**.
|
||||
`useFilters` is the hook that implements **row filtering** and can even be used in conjunction with `useGlobalFilter`. It's also important to note that this hook can be used either **before or after** `useGlobalFilter`, depending on the performance characteristics you want to code for.
|
||||
|
||||
### Table Options
|
||||
|
||||
@@ -68,8 +68,10 @@ The following values are provided to the table `instance`:
|
||||
- Among many other use-cases, these rows are directly useful for building option lists in filters, since the resulting filtered `rows` do not contain every possible option.
|
||||
- `setFilter: Function(columnId, filterValue) => void`
|
||||
- An instance-level function used to update the filter value for a specific column.
|
||||
- `setAllFilters: Function(filtersObject) => void`
|
||||
- `setAllFilters: Function(filtersObjectArray) => void`
|
||||
- An instance-level function used to update the values for **all** filters on the table, all at once.
|
||||
- filtersObjectArray is an array of objects with id and value keys. Example: `[{ id: 'columnAccessor', value: 'valueToFilter' }]`
|
||||
- Note: You must call setAllFilters with an array, even if that array is empty. eg: `setAllFilters([])`.
|
||||
|
||||
### Column Properties
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# `useGlobalFilter`
|
||||
|
||||
- Plugin Hook
|
||||
- Optional
|
||||
|
||||
`useGlobalFilter` is the hook that implements **global row filtering** and can even be used in conjunction with `useFilters`. It's also important to note that this hook can be used either **before or after** `useFilters`, depending on the performance characteristics you want to code for.
|
||||
|
||||
### Table Options
|
||||
|
||||
The following options are supported via the main options object passed to `useTable(options)`
|
||||
|
||||
- `initialState.globalFilter: any`
|
||||
- Must be **memoized**
|
||||
- An array of objects containing columnId's and their corresponding filter values. This information is stored in state since the table is allowed to manipulate the filter through user interaction.
|
||||
- `globalFilter: String | Function`
|
||||
- Optional
|
||||
- Defaults to `text`
|
||||
- The resolved function from the this string/function will be used to filter the table's data.
|
||||
- If a `string` is passed, the function with that name located on either the custom `filterTypes` option or the built-in filtering types object will be used. If
|
||||
- If a `function` is passed, it will be used directly.
|
||||
- For more information on filter types, see Filtering
|
||||
- If a **function** is passed, it must be **memoized**
|
||||
- `manualGlobalFilter: Bool`
|
||||
- Enables filter detection functionality, but does not automatically perform row filtering.
|
||||
- Turn this on if you wish to implement your own row filter outside of the table (eg. server-side or manual row grouping/nesting)
|
||||
- `disableGlobalFilter: Bool`
|
||||
- Disables global filtering for every column in the entire table.
|
||||
- `filterTypes: Object<filterKey: filterType>`
|
||||
- Must be **memoized**
|
||||
- Allows overriding or adding additional filter types for the table to use. If the globalFilter type isn't found on this object, it will default to using the built-in filter types.
|
||||
- For more information on filter types, see Filtering
|
||||
- `autoResetGlobalFilter: Boolean`
|
||||
- Defaults to `true`
|
||||
- When `true`, the `globalFilter` state will automatically reset if any of the following conditions are met:
|
||||
- `data` is changed
|
||||
- To disable, set to `false`
|
||||
- For more information see the FAQ ["How do I stop my table state from automatically resetting when my data changes?"](./faq#how-do-i-stop-my-table-state-from-automatically-resetting-when-my-data-changes)
|
||||
|
||||
### Column Options
|
||||
|
||||
The following options are supported on any `Column` object passed to the `columns` option in `useTable()`
|
||||
|
||||
- `disableGlobalFilter: Bool`
|
||||
- Optional
|
||||
- If set to `true`, will disable global filtering for this column
|
||||
|
||||
### Instance Properties
|
||||
|
||||
The following values are provided to the table `instance`:
|
||||
|
||||
- `rows: Array<Row>`
|
||||
- An array of **filtered** rows.
|
||||
- `preGlobalFilteredRows: Array<Row>`
|
||||
- The array of rows **used right before filtering**.
|
||||
- Among many other use-cases, these rows are directly useful for building option lists in filters, since the resulting filtered `rows` do not contain every possible option.
|
||||
- `setGlobalFilter: Function(filterValue) => void`
|
||||
- An instance-level function used to update the global filter value.
|
||||
|
||||
### Example
|
||||
|
||||
- [Source](https://github.com/tannerlinsley/react-table/tree/master/examples/filtering)
|
||||
- [Open in CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/filtering)
|
||||
+14
-6
@@ -38,12 +38,18 @@ The following options are supported on any `Column` object passed to the `column
|
||||
- Receives the table instance and cell model as props
|
||||
- Must return valid JSX
|
||||
- This function (or component) formats this column's value when it is being grouped and aggregated, eg. If this column was showing the number of visits for a user to a website and it was currently being grouped to show an **average** of the values, the `Aggregated` function for this column could format that value to `1,000 Avg. Visits`
|
||||
- `aggregate: String | [String, String] | Function(values, rows, isAggregated: Bool) => any`
|
||||
- `aggregate: String | Function(leafValues, aggregatedValues) => any`
|
||||
- Optional
|
||||
- Used to aggregate values across rows, eg. `average`-ing the ages of many cells in a table"
|
||||
- If a single `String` is passed, it must be the key of either a user defined or predefined `aggregations` function.
|
||||
- If a tuple array of `[String, String]` is passed, both must be a key of either a user defined or predefined `aggregations` function.
|
||||
- The first is used to aggregate raw values, eg. `sum`-ing raw values together
|
||||
- The second is used to aggregate values that have already been aggregated, eg. `average`-ing the sums produced by the raw aggregation level
|
||||
- If a `Function` is passed, this function will receive the `values`, original `rows` of those values, and an `isAggregated` `Bool` of whether or not the values and rows have already been aggregated.
|
||||
- If a `Function` is passed, this function will receive both the leaf-row values and (if the rows have already been aggregated, the previously aggregated values) to be aggregated into a single value.
|
||||
- The function signature for all aggregation functions is `(leafValues, aggregatedValues) => aggregatedValue` where `leafValues` is a flat array containing all leaf rows currently grouped at the aggregation level and `aggregatedValues` is an array containing the aggregated values from the immediate child sub rows. Each has purpose in the types of aggregations they power where optimizations are made for either accuracy or performance.
|
||||
- For examples on how an aggregation functions work, see the source code for the built in aggregations in the [src/aggregations.js](../../src/aggregations.js) file.
|
||||
- `aggregateValue: String | Function(values, row, column) => any`
|
||||
- Optional
|
||||
- When attempting to group/aggregate non primitive cell values (eg. arrays of items) you will likely need to resolve a stable primitive value like a number or string to use in normal row aggregations. This property can be used to aggregate or simply access the value to be used in aggregations eg. `count`-ing the unique number of items in a cell's array value before `sum`-ing that count across the table.
|
||||
- If a single `String` is passed, it must be the key of either a user defined or predefined `aggregations` function.
|
||||
- If a `Function` is passed, this function will receive the cell's accessed value, the original `row` object and the `column` associated with the cell
|
||||
- `disableGroupBy: Boolean`
|
||||
- Defaults to `false`
|
||||
- If `true`, will disable grouping for this column.
|
||||
@@ -92,6 +98,8 @@ The following properties are available on every `Row` object returned by the tab
|
||||
- This object contains the **aggregated** values for this row's sub rows
|
||||
- `subRows: Array<Row>`
|
||||
- If the row is a materialized group row, this property is the array of materialized subRows that were grouped inside of this row.
|
||||
- `leafRows: Array<Row>`
|
||||
- If the row is a materialized group row, this property is an array containing all leaf node rows aggregated into this row.
|
||||
- `depth: Int`
|
||||
- If the row is a materialized group row, this is the grouping depth at which this row was created.
|
||||
- `id: String`
|
||||
@@ -108,7 +116,7 @@ The following additional properties are available on every `Cell` object returne
|
||||
|
||||
- `isGrouped: Bool`
|
||||
- If `true`, this cell is a grouped cell, meaning it contains a grouping value and should usually display and expander.
|
||||
- `isRepeatedValue: Bool`
|
||||
- `isPlaceholder: Bool`
|
||||
- If `true`, this cell is a repeated value cell, meaning it contains a value that is already being displayed elsewhere (usually by a parent row's cell).
|
||||
- Most of the time, this cell is not required to be displayed and can safely be hidden during rendering
|
||||
- `isAggregated: Bool`
|
||||
|
||||
@@ -30,10 +30,11 @@ The following options are supported via the main options object passed to `useTa
|
||||
- When `true`, the `pageIndex` state will automatically reset if `manualPagination` is `false` and any of the following conditions are met:
|
||||
- `data` is changed
|
||||
- `manualSortBy` is `false` and `state.sortBy` is changed
|
||||
- `manualGlobalFilter` is `false` and `state.globalFilter` is changed
|
||||
- `manualFilters` is `false` and `state.filters` is changed
|
||||
- `manualGroupBy` is `false` and `state.groupBy` is changed
|
||||
- To disable, set to `false`
|
||||
- For more information see the FAQ ["How do I stop my table state from automatically resetting when my data changes?"](./faq#how-do-i-stop-my-table-state-from-automatically-resetting-when-my-data-changes)
|
||||
- For more information see the FAQ ["How do I stop my table state from automatically resetting when my data changes?"](../faq.md#how-do-i-stop-my-table-state-from-automatically-resetting-when-my-data-changes)
|
||||
- `paginateExpandedRows: Bool`
|
||||
- Optional
|
||||
- Only applies when using the `useExpanded` plugin hook simultaneously
|
||||
|
||||
@@ -58,6 +58,13 @@ The following additional properties are available on every **prepared** `row` ob
|
||||
- `toggleRowSelected: Function(?set)`
|
||||
- Use this function to toggle this row's selected state.
|
||||
- Optionally pass `true` or `false` to set it to that state
|
||||
- `getToggleRowSelectedProps: Function(props) => props`
|
||||
- Use this function to get the props needed for a **select row checkbox**.
|
||||
- Props:
|
||||
- `onChange: Function()`
|
||||
- `style.cursor: 'pointer'`
|
||||
- `checked: Bool`
|
||||
- `title: 'Toggle Row Selected'`
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
@@ -15,10 +15,16 @@ The following options are supported via the main options object passed to `useTa
|
||||
- If a row's ID is found in this array, it will have the state of the value corresponding to that key.
|
||||
- Individual row states can contain anything, but they also contain a `cellState` key, which provides cell-level state based on column ID's to every
|
||||
**prepared** cell in the table.
|
||||
- `initialRowStateAccessor: Function`
|
||||
- `initialRowStateAccessor: Function(originalRow) => Object<any>`
|
||||
- Optional
|
||||
- This function may optionally return the initial state for a row.
|
||||
- Defaults to: `row => ({})`
|
||||
- This function should return the initial state for a row.
|
||||
- If this function is defined, it will be passed a `Row` object, from which you can return a value to use as the initial state, eg. `row => row.original.initialState`
|
||||
- `initialCellStateAccessor: Function(originalRow) => Object<any>`
|
||||
- **Optional**
|
||||
- Defaults to: `cell => ({})`
|
||||
- This function should return the initial state for a cell.
|
||||
- If this function is defined, it will be passed a `Cell` object, from which you can return a value to use as the initial state, eg. `cell => cell.row.original.initialCellState[cell.column.id]`
|
||||
- `autoResetRowState: Boolean`
|
||||
- Defaults to `true`
|
||||
- When `true`, the `rowState` state will automatically reset if any of the following conditions are met:
|
||||
|
||||
@@ -49,7 +49,7 @@ The following options are supported via the main options object passed to `useTa
|
||||
- When `true`, the `sortBy` state will automatically reset if any of the following conditions are met:
|
||||
- `data` is changed
|
||||
- To disable, set to `false`
|
||||
- For more information see the FAQ ["How do I stop my table state from automatically resetting when my data changes?"](./faq#how-do-i-stop-my-table-state-from-automatically-resetting-when-my-data-changes)
|
||||
- For more information see the FAQ ["How do I stop my table state from automatically resetting when my data changes?"](../faq.md#how-do-i-stop-my-table-state-from-automatically-resetting-when-my-data-changes)
|
||||
|
||||
### Column Options
|
||||
|
||||
@@ -72,7 +72,7 @@ The following options are supported on any `Column` object passed to the `column
|
||||
- Defaults to `false`
|
||||
- If set to `true`, the underlying sorting direction will be inverted, but the UI will not.
|
||||
- This may be useful in situations where positive and negative connotation is inverted, eg. a Golfing score where a lower score is considered more positive than a higher one.
|
||||
- `sortType: String | Function`
|
||||
- `sortType: String | Function(rowA: <Row>, rowB: <Row>, columnId: String, desc: Bool)`
|
||||
- Used to compare 2 rows of data and order them correctly.
|
||||
- If a **function** is passed, it must be **memoized**
|
||||
- String options: `basic`, `datetime`, `alphanumeric`. Defaults to `alphanumeric`.
|
||||
@@ -106,7 +106,7 @@ The following properties are available on every `Column` object returned by the
|
||||
- This function is used to resolve any props needed for this column's UI that is responsible for toggling the sort direction when the user clicks it.
|
||||
- You can use the `getSortByToggleProps` hook to extend its functionality.
|
||||
- Custom props may be passed. **NOTE: Custom props may override built-in sortBy props, so be careful!**
|
||||
- `clearSorting: Function() => void`
|
||||
- `clearSortBy: Function() => void`
|
||||
- This function can be used to programmatically clear the sorting for this column.
|
||||
- `isSorted: Boolean`
|
||||
- Denotes whether this column is currently being sorted
|
||||
|
||||
+12
-14
@@ -25,6 +25,11 @@ The following options are supported via the main options object passed to `useTa
|
||||
- Optional
|
||||
- The initial state object for hidden columns
|
||||
- If a column's ID is contained in this array, it will be hidden
|
||||
- `autoResetHiddenColumns: Boolean`
|
||||
- Defaults to `true`
|
||||
- When `true`, the `hiddenColumns` state will automatically reset if any of the following conditions are met:
|
||||
- `columns` is changed
|
||||
- To disable, set to `false`
|
||||
- `stateReducer: Function(newState, action, prevState) => newState`
|
||||
- Optional
|
||||
- With every action that is dispatched to the table's internal `React.useReducer` instance, this reducer is called and is allowed to modify the final state object for updating.
|
||||
@@ -35,7 +40,7 @@ The following options are supported via the main options object passed to `useTa
|
||||
- If you need to control part of the table state, this is the place to do it.
|
||||
- This function is run on every single render, just like a hook and allows you to alter the final state of the table if necessary.
|
||||
- You can use hooks inside of this function, but most of the time, we just suggest using `React.useMemo` to memoize your state overrides.
|
||||
- See the FAQ ["How can I manually control the table state?"](./faq.md#how-can-i-manually-control-the-table-state) for a an example.
|
||||
- See the FAQ ["How can I manually control the table state?"](../faq.md#how-can-i-manually-control-the-table-state) for a an example.
|
||||
- `defaultColumn: Object`
|
||||
- Optional
|
||||
- Defaults to `{}`
|
||||
@@ -67,12 +72,13 @@ The following options are supported via the main options object passed to `useTa
|
||||
|
||||
The following options are supported on any column object you can pass to `columns`.
|
||||
|
||||
- `accessor: String | Function`
|
||||
- `accessor: String | Function(originalRow, rowIndex) => any`
|
||||
- **Required**
|
||||
- This string/function is used to build the data model for your column.
|
||||
- The data returned by an accessor should be **primitive** and sortable.
|
||||
- If a string is passed, the column's value will be looked up on the original row via that key, eg. If your column's accessor is `firstName` then its value would be read from `row['firstName']`. You can also specify deeply nested values with accessors like `info.hobbies` or even `address[0].street`
|
||||
- If a function is passed, the column's value will be looked up on the original row using this accessor function, eg. If your column's accessor is `row => row.firstName`, then its value would be determined by passing the row to this function and using the resulting value.
|
||||
- Technically speaking, this field isn't required if you have a unique `id` for a column. This is used for things like expander or row selection columns. **Warning**: Only omit `accessor` if you really know what you're doing.
|
||||
- `id: String`
|
||||
- **Required if `accessor` is a function**
|
||||
- This is the unique ID for the column. It is used by reference in things like sorting, grouping, filtering etc.
|
||||
@@ -93,11 +99,6 @@ The following options are supported on any column object you can pass to `column
|
||||
- Receives the table instance and cell model as props
|
||||
- Must return valid JSX
|
||||
- This function (or component) is primarily used for formatting the column value, eg. If your column accessor returns a date object, you can use a `Cell` function to format that date to a readable format.
|
||||
- `show: Bool`
|
||||
- Optional
|
||||
- Defaults to `undefined`
|
||||
- If set to `true`, will take priority over any state in `hiddenColumns` and force this column to be visible at all times.
|
||||
- If set to `false` will take priority over any state in `hiddenColumns` and force this column to be hidden at all times.
|
||||
- `width: Int`
|
||||
- Optional
|
||||
- Defaults to `150`
|
||||
@@ -120,18 +121,15 @@ The following properties are available on the table instance returned from `useT
|
||||
- `state: Object`
|
||||
- **Memoized** - This object reference will not change unless the internal table state is modified.
|
||||
- This is the final state object of the table, which is the product of the `initialState`, internal table reducer and (optionally) a custom `reducer` supplied by the user.
|
||||
- `dispatch: Function({ type: Actions[type], ...payload }) => void`
|
||||
- This function is used both internally by React Table, and optionally by you (the developer) to update the table state programmatically.
|
||||
- `type: Actions[type] | String`
|
||||
- The action type corresponding to what action being taken against the state.
|
||||
- `...payload`
|
||||
- Any other action data that is associated with the action
|
||||
- `columns: Array<Column>`
|
||||
- A **nested** array of final column objects, **similar in structure to the original columns configuration option**.
|
||||
- See [Column Properties](#column-properties) for more information
|
||||
- `flatColumns: Array<Column>`
|
||||
- `allColumns: Array<Column>`
|
||||
- A **flat** array of all final column objects.
|
||||
- See [Column Properties](#column-properties) for more information
|
||||
- `visibleColumns: Array<Column>`
|
||||
- A **flat** array of all visible column objects derived from `allColumns`.
|
||||
- See [Column Properties](#column-properties) for more information
|
||||
- `headerGroups: Array<HeaderGroup>`
|
||||
- An array of normalized header groups, each containing a flattened array of final column objects for that row.
|
||||
- **Some of these headers may be materialized as placeholders**
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
## React Table is a "headless" UI library
|
||||
|
||||
React Table is a headless utility, which means out of the box, it doesn't render or supply any actual UI elements. You are in charge of utilizing the state and callbacks of the hooks provided by this library to render your own table markup. [Read this article to understand why React Table is built this way](https://medium.com/merrickchristensen/headless-user-interface-components-565b0c0f2e18). If you don't want to, then here's a quick rundown anyway:
|
||||
React Table is a headless utility, which means out of the box, it doesn't render or supply any actual UI elements. You are in charge of utilizing the state and callbacks of the hooks provided by this library to render your own table markup. [Read this article to understand why React Table is built this way](https://www.merrickchristensen.com/articles/headless-user-interface-components/). If you don't want to, then here's a quick rundown anyway:
|
||||
|
||||
- Separation of Concerns - Not that superficial kind you read about all the time. The real kind. React Table as a library honestly has no business being in charge of your UI. The look, feel, and overall experience of your table is what makes your app or product great. The less React Table gets in the way of that, the better!
|
||||
- Maintenance - By removing the massive (and seemingly endless) API surface area required to support every UI use-case, React Table can remain small, easy-to-use and simple to update/maintain.
|
||||
|
||||
@@ -73,6 +73,9 @@
|
||||
- Material-UI
|
||||
- [Source](https://github.com/tannerlinsley/react-table/tree/master/examples/material-UI-components)
|
||||
- [Open in CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/material-UI-components)
|
||||
- Material-UI Enhanced Table
|
||||
- [Source](https://github.com/tannerlinsley/react-table/tree/master/examples/material-UI-enhanced-table)
|
||||
- [Open in CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/material-UI-enhanced-table)
|
||||
- [ ] Styled-Components
|
||||
- [ ] CSS
|
||||
- [ ] Bootstrap
|
||||
|
||||
+2
-2
@@ -40,10 +40,10 @@ function Table({ data, onFetchData }) {
|
||||
data,
|
||||
})
|
||||
|
||||
// When the these table states change, fetch new data!
|
||||
// When these table states change, fetch new data!
|
||||
React.useEffect(() => {
|
||||
onFetchData({ pageIndex, pageSize, sortBy, filters })
|
||||
}, [fetchData, pageIndex, pageSize, sortBy, filters])
|
||||
}, [onFetchData, pageIndex, pageSize, sortBy, filters])
|
||||
|
||||
return </>
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ function Table({ columns, data }) {
|
||||
getTableBodyProps,
|
||||
headerGroups,
|
||||
rows,
|
||||
flatColumns,
|
||||
visibleColumns,
|
||||
prepareRow,
|
||||
setColumnOrder,
|
||||
state,
|
||||
@@ -230,7 +230,7 @@ function Table({ columns, data }) {
|
||||
)
|
||||
|
||||
const randomizeColumns = () => {
|
||||
setColumnOrder(shuffle(flatColumns.map(d => d.id)))
|
||||
setColumnOrder(shuffle(visibleColumns.map(d => d.id)))
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -267,30 +267,29 @@ function Table({ columns, data }) {
|
||||
</thead>
|
||||
<tbody {...getTableBodyProps()}>
|
||||
<AnimatePresence>
|
||||
{rows.slice(0, 10).map(
|
||||
(row, i) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<motion.tr
|
||||
{...row.getRowProps({
|
||||
layoutTransition: spring,
|
||||
exit: { opacity: 0, maxHeight: 0 },
|
||||
})}
|
||||
>
|
||||
{row.cells.map((cell, i) => {
|
||||
return (
|
||||
<motion.td
|
||||
{...cell.getCellProps({
|
||||
layoutTransition: spring,
|
||||
})}
|
||||
>
|
||||
{cell.render('Cell')}
|
||||
</motion.td>
|
||||
)
|
||||
})}
|
||||
</motion.tr>
|
||||
)}
|
||||
)}
|
||||
{rows.slice(0, 10).map((row, i) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<motion.tr
|
||||
{...row.getRowProps({
|
||||
layoutTransition: spring,
|
||||
exit: { opacity: 0, maxHeight: 0 },
|
||||
})}
|
||||
>
|
||||
{row.cells.map((cell, i) => {
|
||||
return (
|
||||
<motion.td
|
||||
{...cell.getCellProps({
|
||||
layoutTransition: spring,
|
||||
})}
|
||||
>
|
||||
{cell.render('Cell')}
|
||||
</motion.td>
|
||||
)
|
||||
})}
|
||||
</motion.tr>
|
||||
)
|
||||
})}
|
||||
</AnimatePresence>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
+10
-11
@@ -59,17 +59,16 @@ function Table({ columns, data }) {
|
||||
))}
|
||||
</thead>
|
||||
<tbody {...getTableBodyProps()}>
|
||||
{rows.map(
|
||||
(row, i) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||
})}
|
||||
</tr>
|
||||
)}
|
||||
)}
|
||||
{rows.map((row, i) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
|
||||
@@ -2632,7 +2632,7 @@ commander@2.17.x:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||
|
||||
commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.0:
|
||||
commander@^2.11.0, commander@^2.19.0, commander@^2.20.0:
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
|
||||
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
|
||||
@@ -2642,6 +2642,11 @@ commander@~2.19.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
|
||||
|
||||
commander@~2.20.3:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
common-tags@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
|
||||
@@ -4369,9 +4374,9 @@ handle-thing@^2.0.0:
|
||||
integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
|
||||
|
||||
handlebars@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67"
|
||||
integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==
|
||||
version "4.5.3"
|
||||
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"
|
||||
integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==
|
||||
dependencies:
|
||||
neo-async "^2.6.0"
|
||||
optimist "^0.6.1"
|
||||
@@ -9435,11 +9440,11 @@ uglify-js@3.4.x:
|
||||
source-map "~0.6.1"
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"
|
||||
integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
|
||||
version "3.7.3"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
|
||||
integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==
|
||||
dependencies:
|
||||
commander "~2.20.0"
|
||||
commander "~2.20.3"
|
||||
source-map "~0.6.1"
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["react-app"]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
SKIP_PREFLIGHT_CHECK=true
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": ["react-app", "prettier"],
|
||||
"rules": {
|
||||
// "eqeqeq": 0,
|
||||
// "jsx-a11y/anchor-is-valid": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -0,0 +1,29 @@
|
||||
const path = require('path')
|
||||
const resolveFrom = require('resolve-from')
|
||||
|
||||
const fixLinkedDependencies = config => {
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
react$: resolveFrom(path.resolve('node_modules'), 'react'),
|
||||
'react-dom$': resolveFrom(path.resolve('node_modules'), 'react-dom'),
|
||||
},
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
const includeSrcDirectory = config => {
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
modules: [path.resolve('src'), ...config.resolve.modules],
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
['use-babel-config', '.babelrc'],
|
||||
['use-eslint-config', '.eslintrc'],
|
||||
fixLinkedDependencies,
|
||||
// includeSrcDirectory,
|
||||
]
|
||||
@@ -0,0 +1,6 @@
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app) and Rescripts.
|
||||
|
||||
You can:
|
||||
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/material-UI-components)
|
||||
- `yarn` and `yarn start` to run and edit the example
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "rescripts start",
|
||||
"build": "rescripts build",
|
||||
"test": "rescripts test",
|
||||
"eject": "rescripts eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"namor": "^1.1.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"react-table": "latest",
|
||||
"bootstrap": "^4.4.1",
|
||||
"react-bootstrap": "^1.0.0-beta.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rescripts/cli": "^0.0.11",
|
||||
"@rescripts/rescript-use-babel-config": "^0.0.8",
|
||||
"@rescripts/rescript-use-eslint-config": "^0.0.9",
|
||||
"babel-eslint": "10.0.1",
|
||||
"eslint-config-prettier": "^6.3.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import React from 'react'
|
||||
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
|
||||
import BTable from 'react-bootstrap/Table';
|
||||
|
||||
import { useTable } from 'react-table'
|
||||
|
||||
import makeData from './makeData'
|
||||
|
||||
function Table({ columns, data }) {
|
||||
// Use the state and functions returned from useTable to build your UI
|
||||
const { getTableProps, headerGroups, rows, prepareRow } = useTable({
|
||||
columns,
|
||||
data,
|
||||
})
|
||||
|
||||
// Render the UI for your table
|
||||
return (
|
||||
<BTable striped bordered hover size="sm" {...getTableProps()}>
|
||||
<thead>
|
||||
{headerGroups.map(headerGroup => (
|
||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map(column => (
|
||||
<th {...column.getHeaderProps()}>
|
||||
{column.render('Header')}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, i) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<th {...cell.getCellProps()}>
|
||||
{cell.render('Cell')}
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</BTable>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
const columns = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Name',
|
||||
columns: [
|
||||
{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
},
|
||||
{
|
||||
Header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
Header: 'Info',
|
||||
columns: [
|
||||
{
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
},
|
||||
{
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
accessor: 'status',
|
||||
},
|
||||
{
|
||||
Header: 'Profile Progress',
|
||||
accessor: 'progress',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const data = React.useMemo(() => makeData(20), [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table columns={columns} data={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import './index.css'
|
||||
import App from './App'
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
||||
@@ -0,0 +1,40 @@
|
||||
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 default function makeData(...lens) {
|
||||
const makeDataLevel = (depth = 0) => {
|
||||
const len = lens[depth]
|
||||
return range(len).map(d => {
|
||||
return {
|
||||
...newPerson(),
|
||||
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return makeDataLevel()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["react-app"]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
SKIP_PREFLIGHT_CHECK=true
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": ["react-app", "prettier"],
|
||||
"rules": {
|
||||
// "eqeqeq": 0,
|
||||
// "jsx-a11y/anchor-is-valid": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -0,0 +1,29 @@
|
||||
const path = require('path')
|
||||
const resolveFrom = require('resolve-from')
|
||||
|
||||
const fixLinkedDependencies = config => {
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
react$: resolveFrom(path.resolve('node_modules'), 'react'),
|
||||
'react-dom$': resolveFrom(path.resolve('node_modules'), 'react-dom'),
|
||||
},
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
const includeSrcDirectory = config => {
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
modules: [path.resolve('src'), ...config.resolve.modules],
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
['use-babel-config', '.babelrc'],
|
||||
['use-eslint-config', '.eslintrc'],
|
||||
fixLinkedDependencies,
|
||||
// includeSrcDirectory,
|
||||
]
|
||||
@@ -0,0 +1,6 @@
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app) and Rescripts.
|
||||
|
||||
You can:
|
||||
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/material-UI-components)
|
||||
- `yarn` and `yarn start` to run and edit the example
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "rescripts start",
|
||||
"build": "rescripts build",
|
||||
"test": "rescripts test",
|
||||
"eject": "rescripts eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"namor": "^1.1.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"react-table": "latest",
|
||||
"bootstrap": "^4.4.1",
|
||||
"react-bootstrap": "^1.0.0-beta.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rescripts/cli": "^0.0.11",
|
||||
"@rescripts/rescript-use-babel-config": "^0.0.8",
|
||||
"@rescripts/rescript-use-eslint-config": "^0.0.9",
|
||||
"babel-eslint": "10.0.1",
|
||||
"eslint-config-prettier": "^6.3.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import React from 'react'
|
||||
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
|
||||
import BTable from 'react-bootstrap/Table';
|
||||
|
||||
import { useTable } from 'react-table'
|
||||
|
||||
import makeData from './makeData'
|
||||
|
||||
function Table({ columns, data }) {
|
||||
// Use the state and functions returned from useTable to build your UI
|
||||
const { getTableProps, headerGroups, rows, prepareRow } = useTable({
|
||||
columns,
|
||||
data,
|
||||
})
|
||||
|
||||
// Render the UI for your table
|
||||
return (
|
||||
<BTable striped bordered hover size="sm" {...getTableProps()}>
|
||||
<thead>
|
||||
{headerGroups.map(headerGroup => (
|
||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map(column => (
|
||||
<th {...column.getHeaderProps()}>
|
||||
{column.render('Header')}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, i) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<th {...cell.getCellProps()}>
|
||||
{cell.render('Cell')}
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</BTable>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
const columns = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Name',
|
||||
columns: [
|
||||
{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
},
|
||||
{
|
||||
Header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
Header: 'Info',
|
||||
columns: [
|
||||
{
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
},
|
||||
{
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
accessor: 'status',
|
||||
},
|
||||
{
|
||||
Header: 'Profile Progress',
|
||||
accessor: 'progress',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const data = React.useMemo(() => makeData(20), [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table columns={columns} data={data} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import './index.css'
|
||||
import App from './App'
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
||||
@@ -0,0 +1,40 @@
|
||||
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 default function makeData(...lens) {
|
||||
const makeDataLevel = (depth = 0) => {
|
||||
const len = lens[depth]
|
||||
return range(len).map(d => {
|
||||
return {
|
||||
...newPerson(),
|
||||
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return makeDataLevel()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -53,7 +53,7 @@ function Table({ columns, data }) {
|
||||
headerGroups,
|
||||
rows,
|
||||
prepareRow,
|
||||
flatColumns,
|
||||
allColumns,
|
||||
getToggleHideAllColumnsProps,
|
||||
state,
|
||||
} = useTable({
|
||||
@@ -69,7 +69,7 @@ function Table({ columns, data }) {
|
||||
<IndeterminateCheckbox {...getToggleHideAllColumnsProps()} /> Toggle
|
||||
All
|
||||
</div>
|
||||
{flatColumns.map(column => (
|
||||
{allColumns.map(column => (
|
||||
<div key={column.id}>
|
||||
<label>
|
||||
<input type="checkbox" {...column.getToggleHiddenProps()} />{' '}
|
||||
|
||||
@@ -2632,7 +2632,7 @@ commander@2.17.x:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||
|
||||
commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.0:
|
||||
commander@^2.11.0, commander@^2.19.0, commander@^2.20.0:
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
|
||||
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
|
||||
@@ -2642,6 +2642,11 @@ commander@~2.19.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
|
||||
|
||||
commander@~2.20.3:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
common-tags@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
|
||||
@@ -4369,9 +4374,9 @@ handle-thing@^2.0.0:
|
||||
integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
|
||||
|
||||
handlebars@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67"
|
||||
integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.6.0.tgz#33af6c3eda930d7a924f5d8f1c6d8edc3180512e"
|
||||
integrity sha512-i1ZUP7Qp2JdkMaFon2a+b0m5geE8Z4ZTLaGkgrObkEd+OkUKyRbRWw4KxuFCoHfdETSY1yf9/574eVoNSiK7pw==
|
||||
dependencies:
|
||||
neo-async "^2.6.0"
|
||||
optimist "^0.6.1"
|
||||
@@ -9435,11 +9440,11 @@ uglify-js@3.4.x:
|
||||
source-map "~0.6.1"
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"
|
||||
integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
|
||||
version "3.7.4"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.4.tgz#e6d83a1aa32ff448bd1679359ab13d8db0fe0743"
|
||||
integrity sha512-tinYWE8X1QfCHxS1lBS8yiDekyhSXOO6R66yNOCdUJeojxxw+PX2BHAz/BWyW7PQ7pkiWVxJfIEbiDxyLWvUGg==
|
||||
dependencies:
|
||||
commander "~2.20.0"
|
||||
commander "~2.20.3"
|
||||
source-map "~0.6.1"
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||
|
||||
@@ -50,7 +50,7 @@ function Table({ columns, data }) {
|
||||
getTableBodyProps,
|
||||
headerGroups,
|
||||
rows,
|
||||
flatColumns,
|
||||
visibleColumns,
|
||||
prepareRow,
|
||||
setColumnOrder,
|
||||
state,
|
||||
@@ -63,7 +63,7 @@ function Table({ columns, data }) {
|
||||
)
|
||||
|
||||
const randomizeColumns = () => {
|
||||
setColumnOrder(shuffle(flatColumns.map(d => d.id)))
|
||||
setColumnOrder(shuffle(visibleColumns.map(d => d.id)))
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -80,19 +80,16 @@ function Table({ columns, data }) {
|
||||
))}
|
||||
</thead>
|
||||
<tbody {...getTableBodyProps()}>
|
||||
{rows.slice(0, 10).map(
|
||||
(row, i) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map((cell, i) => {
|
||||
return (
|
||||
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)}
|
||||
)}
|
||||
{rows.slice(0, 10).map((row, i) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map((cell, i) => {
|
||||
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<pre>
|
||||
|
||||
@@ -63,7 +63,7 @@ const EditableCell = ({
|
||||
updateMyData(index, id, value)
|
||||
}
|
||||
|
||||
// If the initialValue is changed externall, sync it up with our state
|
||||
// If the initialValue is changed external, sync it up with our state
|
||||
React.useEffect(() => {
|
||||
setValue(initialValue)
|
||||
}, [initialValue])
|
||||
|
||||
@@ -88,14 +88,18 @@ function App() {
|
||||
() => [
|
||||
{
|
||||
// Build our expander column
|
||||
Header: () => null, // No header, please
|
||||
id: 'expander', // Make sure it has an ID
|
||||
Header: ({ getToggleAllRowsExpandedProps, isAllRowsExpanded }) => (
|
||||
<span {...getToggleAllRowsExpandedProps()}>
|
||||
{isAllRowsExpanded ? '👇' : '👉'}
|
||||
</span>
|
||||
),
|
||||
Cell: ({ row }) =>
|
||||
// Use the row.canExpand and row.getExpandedToggleProps prop getter
|
||||
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
|
||||
// to build the toggle for expanding a row
|
||||
row.canExpand ? (
|
||||
<span
|
||||
{...row.getExpandedToggleProps({
|
||||
{...row.getToggleRowExpandedProps({
|
||||
style: {
|
||||
// We can even use the row.depth property
|
||||
// and paddingLeft to indicate the depth
|
||||
|
||||
@@ -8087,10 +8087,10 @@ react-scripts@3.0.1:
|
||||
optionalDependencies:
|
||||
fsevents "2.0.6"
|
||||
|
||||
react-table@next:
|
||||
version "7.0.0-alpha.7"
|
||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-alpha.7.tgz#0cb6da6f32adb397e68505b7cdd4880d15d73017"
|
||||
integrity sha512-oXE9RRkE2CFk1OloNCSTPQ9qxOdujgkCoW5b/srbJsBog/ySkWuozBTQkxH1wGNmnSxGyTrTxJqXdXPQam7VAw==
|
||||
react-table@latest:
|
||||
version "7.0.0-rc.15"
|
||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-rc.15.tgz#bb855e4e2abbb4aaf0ed2334404a41f3ada8e13a"
|
||||
integrity sha512-ofMOlgrioHhhvHjvjsQkxvfQzU98cqwy6BjPGNwhLN1vhgXeWi0mUGreaCPvRenEbTiXsQbMl4k3Xmx3Mut8Rw==
|
||||
|
||||
react@^16.8.6:
|
||||
version "16.8.6"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { useTable, useFilters } from 'react-table'
|
||||
import { useTable, useFilters, useGlobalFilter } from 'react-table'
|
||||
// A great library for fuzzy filtering/sorting items
|
||||
import matchSorter from 'match-sorter'
|
||||
|
||||
@@ -35,6 +35,32 @@ const Styles = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
// Define a default UI for filtering
|
||||
function GlobalFilter({
|
||||
preGlobalFilteredRows,
|
||||
globalFilter,
|
||||
setGlobalFilter,
|
||||
}) {
|
||||
const count = preGlobalFilteredRows.length
|
||||
|
||||
return (
|
||||
<span>
|
||||
Search:{' '}
|
||||
<input
|
||||
value={globalFilter || ''}
|
||||
onChange={e => {
|
||||
setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
|
||||
}}
|
||||
placeholder={`${count} records...`}
|
||||
style={{
|
||||
fontSize: '1.1rem',
|
||||
border: '0',
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
// Define a default UI for filtering
|
||||
function DefaultColumnFilter({
|
||||
column: { filterValue, preFilteredRows, setFilter },
|
||||
@@ -217,6 +243,9 @@ function Table({ columns, data }) {
|
||||
rows,
|
||||
prepareRow,
|
||||
state,
|
||||
visibleColumns,
|
||||
preGlobalFilteredRows,
|
||||
setGlobalFilter,
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
@@ -224,7 +253,8 @@ function Table({ columns, data }) {
|
||||
defaultColumn, // Be sure to pass the defaultColumn option
|
||||
filterTypes,
|
||||
},
|
||||
useFilters // useFilters!
|
||||
useFilters, // useFilters!
|
||||
useGlobalFilter // useGlobalFilter!
|
||||
)
|
||||
|
||||
// We don't want to render all of the rows for this example, so cap
|
||||
@@ -233,11 +263,6 @@ function Table({ columns, data }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<pre>
|
||||
<code>{JSON.stringify(state.filters, null, 2)}</code>
|
||||
</pre>
|
||||
</div>
|
||||
<table {...getTableProps()}>
|
||||
<thead>
|
||||
{headerGroups.map(headerGroup => (
|
||||
@@ -251,25 +276,41 @@ function Table({ columns, data }) {
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
<tr>
|
||||
<th
|
||||
colSpan={visibleColumns.length}
|
||||
style={{
|
||||
textAlign: 'left',
|
||||
}}
|
||||
>
|
||||
<GlobalFilter
|
||||
preGlobalFilteredRows={preGlobalFilteredRows}
|
||||
globalFilter={state.globalFilter}
|
||||
setGlobalFilter={setGlobalFilter}
|
||||
/>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody {...getTableBodyProps()}>
|
||||
{firstPageRows.map(
|
||||
(row, i) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)}
|
||||
)}
|
||||
{firstPageRows.map((row, i) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<div>Showing the first 20 results of {rows.length} rows</div>
|
||||
<div>
|
||||
<pre>
|
||||
<code>{JSON.stringify(state.filters, null, 2)}</code>
|
||||
</pre>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8094,10 +8094,10 @@ react-scripts@3.0.1:
|
||||
optionalDependencies:
|
||||
fsevents "2.0.6"
|
||||
|
||||
react-table@next:
|
||||
version "7.0.0-alpha.7"
|
||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-alpha.7.tgz#0cb6da6f32adb397e68505b7cdd4880d15d73017"
|
||||
integrity sha512-oXE9RRkE2CFk1OloNCSTPQ9qxOdujgkCoW5b/srbJsBog/ySkWuozBTQkxH1wGNmnSxGyTrTxJqXdXPQam7VAw==
|
||||
react-table@latest:
|
||||
version "7.0.0-rc.14"
|
||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-rc.14.tgz#bb4171fbdd56d78dd5f6b9e18694a36c00bd6261"
|
||||
integrity sha512-9NyzAE0kLH8HA+DK86ynxVDxniO5ZdggAqI7nDKauWXQGVYNFId8+JJSescJ2vwP9nR2JKyCEDG9c+CjIaLNkA==
|
||||
|
||||
react@^16.8.6:
|
||||
version "16.8.6"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"namor": "^1.1.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"react-scripts": "3.3.0",
|
||||
"react-table": "latest",
|
||||
"styled-components": "^4.3.2"
|
||||
},
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { useTable, useResizeColumns, useFlexLayout } from 'react-table'
|
||||
import {
|
||||
useTable,
|
||||
useResizeColumns,
|
||||
useFlexLayout,
|
||||
useRowSelect,
|
||||
} from 'react-table'
|
||||
|
||||
import makeData from './makeData'
|
||||
|
||||
const Styles = styled.div`
|
||||
padding: 1rem;
|
||||
${'' /* These styles are suggested for the table fill all available space in its containing element */}
|
||||
display: block;
|
||||
${'' /* These styles are required for a horizontaly scrollable table overflow */}
|
||||
overflow: auto;
|
||||
|
||||
.table {
|
||||
${'' /* These styles are suggested for the table fill all available space in its containing element */}
|
||||
display: block;
|
||||
${'' /* These styles are required for a horizontaly scrollable table overflow */}
|
||||
overflow: auto;
|
||||
|
||||
border-spacing: 0;
|
||||
border: 1px solid black;
|
||||
|
||||
@@ -35,13 +39,13 @@ const Styles = styled.div`
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
.th,
|
||||
.td {
|
||||
margin: 0;
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid black;
|
||||
border-right: 1px solid black;
|
||||
|
||||
${'' /* In this example we use an absolutely position resizer,
|
||||
@@ -53,16 +57,15 @@ const Styles = styled.div`
|
||||
}
|
||||
|
||||
.resizer {
|
||||
display: inline-block;
|
||||
right: 0;
|
||||
background: blue;
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
${'' /* prevents from scrolling while dragging on touch devices */}
|
||||
touch-action:none;
|
||||
touch-action :none;
|
||||
|
||||
&.isResizing {
|
||||
background: red;
|
||||
@@ -72,6 +75,38 @@ const Styles = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
const headerProps = (props, { column }) => getStyles(props, column.align)
|
||||
|
||||
const cellProps = (props, { cell }) => getStyles(props, cell.column.align)
|
||||
|
||||
const getStyles = (props, align = 'left') => [
|
||||
props,
|
||||
{
|
||||
style: {
|
||||
justifyContent: align === 'right' ? 'flex-end' : 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
display: 'flex',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const IndeterminateCheckbox = React.forwardRef(
|
||||
({ indeterminate, ...rest }, ref) => {
|
||||
const defaultRef = React.useRef()
|
||||
const resolvedRef = ref || defaultRef
|
||||
|
||||
React.useEffect(() => {
|
||||
resolvedRef.current.indeterminate = indeterminate
|
||||
}, [resolvedRef, indeterminate])
|
||||
|
||||
return (
|
||||
<>
|
||||
<input type="checkbox" ref={resolvedRef} {...rest} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
function Table({ columns, data }) {
|
||||
const defaultColumn = React.useMemo(
|
||||
() => ({
|
||||
@@ -83,49 +118,84 @@ function Table({ columns, data }) {
|
||||
[]
|
||||
)
|
||||
|
||||
const {
|
||||
getTableProps,
|
||||
getTableBodyProps,
|
||||
headerGroups,
|
||||
rows,
|
||||
prepareRow,
|
||||
} = useTable(
|
||||
const { getTableProps, headerGroups, rows, prepareRow } = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
defaultColumn,
|
||||
},
|
||||
useResizeColumns,
|
||||
useFlexLayout
|
||||
useFlexLayout,
|
||||
useRowSelect,
|
||||
hooks => {
|
||||
hooks.allColumns.push(columns => [
|
||||
// Let's make a column for selection
|
||||
{
|
||||
id: 'selection',
|
||||
disableResizing: true,
|
||||
minWidth: 35,
|
||||
width: 35,
|
||||
maxWidth: 35,
|
||||
// The header can use the table's getToggleAllRowsSelectedProps method
|
||||
// to render a checkbox
|
||||
Header: ({ getToggleAllRowsSelectedProps }) => (
|
||||
<div>
|
||||
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
|
||||
</div>
|
||||
),
|
||||
// The cell can use the individual row's getToggleRowSelectedProps method
|
||||
// to the render a checkbox
|
||||
Cell: ({ row }) => (
|
||||
<div>
|
||||
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
...columns,
|
||||
])
|
||||
hooks.useInstanceBeforeDimensions.push(({ headerGroups }) => {
|
||||
// fix the parent group of the selection button to not be resizable
|
||||
const selectionGroupHeader = headerGroups[0].headers[0]
|
||||
selectionGroupHeader.canResize = false
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<div {...getTableProps()} className="table">
|
||||
<div>
|
||||
{headerGroups.map(headerGroup => (
|
||||
<div {...headerGroup.getHeaderGroupProps()} className="tr">
|
||||
<div
|
||||
{...headerGroup.getHeaderGroupProps({
|
||||
// style: { paddingRight: '15px' },
|
||||
})}
|
||||
className="tr"
|
||||
>
|
||||
{headerGroup.headers.map(column => (
|
||||
<div {...column.getHeaderProps()} className="th">
|
||||
<div {...column.getHeaderProps(headerProps)} className="th">
|
||||
{column.render('Header')}
|
||||
{/* Use column.getResizerProps to hook up the events correctly */}
|
||||
<div
|
||||
{...column.getResizerProps()}
|
||||
className={`resizer ${column.isResizing ? 'isResizing' : ''}`}
|
||||
/>
|
||||
{column.canResize && (
|
||||
<div
|
||||
{...column.getResizerProps()}
|
||||
className={`resizer ${
|
||||
column.isResizing ? 'isResizing' : ''
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div {...getTableBodyProps()} className="tbody">
|
||||
{rows.map((row, i) => {
|
||||
<div className="tbody">
|
||||
{rows.map(row => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<div {...row.getRowProps()} className="tr">
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<div {...cell.getCellProps()} className="td">
|
||||
<div {...cell.getCellProps(cellProps)} className="td">
|
||||
{cell.render('Cell')}
|
||||
</div>
|
||||
)
|
||||
@@ -161,11 +231,13 @@ function App() {
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
width: 50,
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
width: 50,
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,7 +65,7 @@ function Table({ columns, data }) {
|
||||
// Our custom plugin to add the expander column
|
||||
hooks => {
|
||||
hooks.useControlledState.push(useControlledState)
|
||||
hooks.flatColumns.push((columns, { instance }) => {
|
||||
hooks.visibleColumns.push((columns, { instance }) => {
|
||||
if (!instance.state.groupBy.length) {
|
||||
return columns
|
||||
}
|
||||
@@ -74,9 +74,9 @@ function Table({ columns, data }) {
|
||||
{
|
||||
id: 'expander', // Make sure it has an ID
|
||||
// Build our expander column
|
||||
Header: ({ flatColumns, state: { groupBy } }) => {
|
||||
Header: ({ allColumns, state: { groupBy } }) => {
|
||||
return groupBy.map(columnId => {
|
||||
const column = flatColumns.find(d => d.id === columnId)
|
||||
const column = allColumns.find(d => d.id === columnId)
|
||||
|
||||
return (
|
||||
<span {...column.getHeaderProps()}>
|
||||
@@ -97,7 +97,7 @@ function Table({ columns, data }) {
|
||||
|
||||
return (
|
||||
<span
|
||||
{...row.getExpandedToggleProps({
|
||||
{...row.getToggleRowExpandedProps({
|
||||
style: {
|
||||
// We can even use the row.depth property
|
||||
// and paddingLeft to indicate the depth
|
||||
@@ -122,7 +122,7 @@ function Table({ columns, data }) {
|
||||
)
|
||||
|
||||
// We don't want to render all of the rows for this example, so cap
|
||||
// it at 20 for this use case
|
||||
// it at 100 for this use case
|
||||
const firstPageRows = rows.slice(0, 100)
|
||||
|
||||
return (
|
||||
@@ -166,7 +166,7 @@ function Table({ columns, data }) {
|
||||
? '#0aff0082'
|
||||
: cell.isAggregated
|
||||
? '#ffa50078'
|
||||
: cell.isRepeatedValue
|
||||
: cell.isPlaceholder
|
||||
? '#ff000042'
|
||||
: 'white',
|
||||
}}
|
||||
@@ -175,7 +175,7 @@ function Table({ columns, data }) {
|
||||
? // If the cell is aggregated, use the Aggregated
|
||||
// renderer for cell
|
||||
cell.render('Aggregated')
|
||||
: cell.isRepeatedValue
|
||||
: cell.isPlaceholder
|
||||
? null // For cells with repeated values, render null
|
||||
: // Otherwise, just render the regular cell
|
||||
cell.render('Cell')}
|
||||
@@ -225,20 +225,20 @@ function Legend() {
|
||||
padding: '0.5rem',
|
||||
}}
|
||||
>
|
||||
Repeated Value
|
||||
Placeholder
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// This is a custom aggregator that
|
||||
// takes in an array of values and
|
||||
// takes in an array of leaf values and
|
||||
// returns the rounded median
|
||||
function roundedMedian(values) {
|
||||
let min = values[0] || ''
|
||||
let max = values[0] || ''
|
||||
function roundedMedian(leafValues) {
|
||||
let min = leafValues[0] || 0
|
||||
let max = leafValues[0] || 0
|
||||
|
||||
values.forEach(value => {
|
||||
leafValues.forEach(value => {
|
||||
min = Math.min(min, value)
|
||||
max = Math.max(max, value)
|
||||
})
|
||||
@@ -259,7 +259,7 @@ function App() {
|
||||
// count the total rows being aggregated,
|
||||
// then sum any of those counts if they are
|
||||
// aggregated further
|
||||
aggregate: ['sum', 'count'],
|
||||
aggregate: 'count',
|
||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
||||
},
|
||||
{
|
||||
@@ -269,7 +269,7 @@ function App() {
|
||||
// first count the UNIQUE values from the rows
|
||||
// being aggregated, then sum those counts if
|
||||
// they are aggregated further
|
||||
aggregate: ['sum', 'uniqueCount'],
|
||||
aggregate: 'uniqueCount',
|
||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -51,7 +51,7 @@ function Table({ columns, data }) {
|
||||
)
|
||||
|
||||
// We don't want to render all of the rows for this example, so cap
|
||||
// it at 20 for this use case
|
||||
// it at 100 for this use case
|
||||
const firstPageRows = rows.slice(0, 100)
|
||||
|
||||
return (
|
||||
@@ -79,50 +79,49 @@ function Table({ columns, data }) {
|
||||
))}
|
||||
</thead>
|
||||
<tbody {...getTableBodyProps()}>
|
||||
{firstPageRows.map(
|
||||
(row, i) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<td
|
||||
// For educational purposes, let's color the
|
||||
// cell depending on what type it is given
|
||||
// from the useGroupBy hook
|
||||
{...cell.getCellProps()}
|
||||
style={{
|
||||
background: cell.isGrouped
|
||||
? '#0aff0082'
|
||||
: cell.isAggregated
|
||||
? '#ffa50078'
|
||||
: cell.isRepeatedValue
|
||||
? '#ff000042'
|
||||
: 'white',
|
||||
}}
|
||||
>
|
||||
{cell.isGrouped ? (
|
||||
// If it's a grouped cell, add an expander and row count
|
||||
<>
|
||||
<span {...row.getExpandedToggleProps()}>
|
||||
{row.isExpanded ? '👇' : '👉'}
|
||||
</span>{' '}
|
||||
{cell.render('Cell')} ({row.subRows.length})
|
||||
</>
|
||||
) : cell.isAggregated ? (
|
||||
// If the cell is aggregated, use the Aggregated
|
||||
// renderer for cell
|
||||
cell.render('Aggregated')
|
||||
) : cell.isRepeatedValue ? null : ( // For cells with repeated values, render null
|
||||
// Otherwise, just render the regular cell
|
||||
cell.render('Cell')
|
||||
)}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)}
|
||||
)}
|
||||
{firstPageRows.map((row, i) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<td
|
||||
// For educational purposes, let's color the
|
||||
// cell depending on what type it is given
|
||||
// from the useGroupBy hook
|
||||
{...cell.getCellProps()}
|
||||
style={{
|
||||
background: cell.isGrouped
|
||||
? '#0aff0082'
|
||||
: cell.isAggregated
|
||||
? '#ffa50078'
|
||||
: cell.isPlaceholder
|
||||
? '#ff000042'
|
||||
: 'white',
|
||||
}}
|
||||
>
|
||||
{cell.isGrouped ? (
|
||||
// If it's a grouped cell, add an expander and row count
|
||||
<>
|
||||
<span {...row.getToggleRowExpandedProps()}>
|
||||
{row.isExpanded ? '👇' : '👉'}
|
||||
</span>{' '}
|
||||
{cell.render('Cell')} ({row.subRows.length})
|
||||
</>
|
||||
) : cell.isAggregated ? (
|
||||
// If the cell is aggregated, use the Aggregated
|
||||
// renderer for cell
|
||||
cell.render('Aggregated')
|
||||
) : cell.isPlaceholder ? null : ( // For cells with repeated values, render null
|
||||
// Otherwise, just render the regular cell
|
||||
cell.render('Cell')
|
||||
)}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
@@ -170,13 +169,13 @@ function Legend() {
|
||||
}
|
||||
|
||||
// This is a custom aggregator that
|
||||
// takes in an array of values and
|
||||
// takes in an array of leaf values and
|
||||
// returns the rounded median
|
||||
function roundedMedian(values) {
|
||||
let min = values[0] || ''
|
||||
let max = values[0] || ''
|
||||
function roundedMedian(leafValues) {
|
||||
let min = leafValues[0] || 0
|
||||
let max = leafValues[0] || 0
|
||||
|
||||
values.forEach(value => {
|
||||
leafValues.forEach(value => {
|
||||
min = Math.min(min, value)
|
||||
max = Math.max(max, value)
|
||||
})
|
||||
@@ -197,7 +196,7 @@ function App() {
|
||||
// count the total rows being aggregated,
|
||||
// then sum any of those counts if they are
|
||||
// aggregated further
|
||||
aggregate: ['sum', 'count'],
|
||||
aggregate: 'count',
|
||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
||||
},
|
||||
{
|
||||
@@ -207,7 +206,7 @@ function App() {
|
||||
// first count the UNIQUE values from the rows
|
||||
// being aggregated, then sum those counts if
|
||||
// they are aggregated further
|
||||
aggregate: ['sum', 'uniqueCount'],
|
||||
aggregate: 'uniqueCount',
|
||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
||||
},
|
||||
],
|
||||
@@ -220,7 +219,8 @@ function App() {
|
||||
accessor: 'age',
|
||||
// Aggregate the average age of visitors
|
||||
aggregate: 'average',
|
||||
Aggregated: ({ cell: { value } }) => `${value} (avg)`,
|
||||
Aggregated: ({ cell: { value } }) =>
|
||||
`${Math.round(value * 100) / 100} (avg)`,
|
||||
},
|
||||
{
|
||||
Header: 'Visits',
|
||||
|
||||
@@ -2632,7 +2632,7 @@ commander@2.17.x:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||
|
||||
commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.0:
|
||||
commander@^2.11.0, commander@^2.19.0, commander@^2.20.0:
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
|
||||
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
|
||||
@@ -2642,6 +2642,11 @@ commander@~2.19.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
|
||||
|
||||
commander@~2.20.3:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
common-tags@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
|
||||
@@ -4369,9 +4374,9 @@ handle-thing@^2.0.0:
|
||||
integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
|
||||
|
||||
handlebars@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67"
|
||||
integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==
|
||||
version "4.5.3"
|
||||
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"
|
||||
integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==
|
||||
dependencies:
|
||||
neo-async "^2.6.0"
|
||||
optimist "^0.6.1"
|
||||
@@ -9435,11 +9440,11 @@ uglify-js@3.4.x:
|
||||
source-map "~0.6.1"
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"
|
||||
integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
|
||||
version "3.7.3"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
|
||||
integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==
|
||||
dependencies:
|
||||
commander "~2.20.0"
|
||||
commander "~2.20.3"
|
||||
source-map "~0.6.1"
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||
|
||||
@@ -301,14 +301,14 @@ function Table({ columns, data, updateMyData, skipPageReset }) {
|
||||
// when we edit the data, undefined means using the default
|
||||
autoResetPage: !skipPageReset,
|
||||
},
|
||||
useGroupBy,
|
||||
useFilters,
|
||||
useGroupBy,
|
||||
useSortBy,
|
||||
useExpanded,
|
||||
usePagination,
|
||||
useRowSelect,
|
||||
hooks => {
|
||||
hooks.flatColumns.push(columns => [
|
||||
hooks.visibleColumns.push(columns => [
|
||||
{
|
||||
id: 'selection',
|
||||
// Make this column a groupByBoundary. This ensures that groupBy columns
|
||||
@@ -378,7 +378,7 @@ function Table({ columns, data, updateMyData, skipPageReset }) {
|
||||
{cell.isGrouped ? (
|
||||
// If it's a grouped cell, add an expander and row count
|
||||
<>
|
||||
<span {...row.getExpandedToggleProps()}>
|
||||
<span {...row.getToggleRowExpandedProps()}>
|
||||
{row.isExpanded ? '👇' : '👉'}
|
||||
</span>{' '}
|
||||
{cell.render('Cell', { editable: false })} (
|
||||
@@ -388,7 +388,7 @@ function Table({ columns, data, updateMyData, skipPageReset }) {
|
||||
// If the cell is aggregated, use the Aggregated
|
||||
// renderer for cell
|
||||
cell.render('Aggregated')
|
||||
) : cell.isRepeatedValue ? null : ( // For cells with repeated values, render null
|
||||
) : cell.isPlaceholder ? null : ( // For cells with repeated values, render null
|
||||
// Otherwise, just render the regular cell
|
||||
cell.render('Cell', { editable: true })
|
||||
)}
|
||||
@@ -486,13 +486,13 @@ function filterGreaterThan(rows, id, filterValue) {
|
||||
filterGreaterThan.autoRemove = val => typeof val !== 'number'
|
||||
|
||||
// This is a custom aggregator that
|
||||
// takes in an array of values and
|
||||
// takes in an array of leaf values and
|
||||
// returns the rounded median
|
||||
function roundedMedian(values) {
|
||||
let min = values[0] || ''
|
||||
let max = values[0] || ''
|
||||
function roundedMedian(leafValues) {
|
||||
let min = leafValues[0] || 0
|
||||
let max = leafValues[0] || 0
|
||||
|
||||
values.forEach(value => {
|
||||
leafValues.forEach(value => {
|
||||
min = Math.min(min, value)
|
||||
max = Math.max(max, value)
|
||||
})
|
||||
@@ -513,7 +513,7 @@ function App() {
|
||||
// count the total rows being aggregated,
|
||||
// then sum any of those counts if they are
|
||||
// aggregated further
|
||||
aggregate: ['sum', 'count'],
|
||||
aggregate: 'count',
|
||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
||||
},
|
||||
{
|
||||
@@ -525,7 +525,7 @@ function App() {
|
||||
// first count the UNIQUE values from the rows
|
||||
// being aggregated, then sum those counts if
|
||||
// they are aggregated further
|
||||
aggregate: ['sum', 'uniqueCount'],
|
||||
aggregate: 'uniqueCount',
|
||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -8095,10 +8095,10 @@ react-scripts@3.0.1:
|
||||
optionalDependencies:
|
||||
fsevents "2.0.6"
|
||||
|
||||
react-table@next:
|
||||
version "7.0.0-alpha.7"
|
||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-alpha.7.tgz#0cb6da6f32adb397e68505b7cdd4880d15d73017"
|
||||
integrity sha512-oXE9RRkE2CFk1OloNCSTPQ9qxOdujgkCoW5b/srbJsBog/ySkWuozBTQkxH1wGNmnSxGyTrTxJqXdXPQam7VAw==
|
||||
react-table@latest:
|
||||
version "7.0.0-rc.15"
|
||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-rc.15.tgz#bb855e4e2abbb4aaf0ed2334404a41f3ada8e13a"
|
||||
integrity sha512-ofMOlgrioHhhvHjvjsQkxvfQzU98cqwy6BjPGNwhLN1vhgXeWi0mUGreaCPvRenEbTiXsQbMl4k3Xmx3Mut8Rw==
|
||||
|
||||
react@^16.8.6:
|
||||
version "16.8.6"
|
||||
|
||||
@@ -282,7 +282,15 @@ function Table({ columns, data, updateMyData, skipReset }) {
|
||||
nextPage,
|
||||
previousPage,
|
||||
setPageSize,
|
||||
state: { pageIndex, pageSize, groupBy, expanded, filters, selectedRowIds },
|
||||
state: {
|
||||
pageIndex,
|
||||
pageSize,
|
||||
sortBy,
|
||||
groupBy,
|
||||
expanded,
|
||||
filters,
|
||||
selectedRowIds,
|
||||
},
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
@@ -299,6 +307,7 @@ function Table({ columns, data, updateMyData, skipReset }) {
|
||||
// when we edit the data.
|
||||
autoResetPage: !skipReset,
|
||||
autoResetSelectedRows: !skipReset,
|
||||
disableMultiSort: true,
|
||||
},
|
||||
useFilters,
|
||||
useGroupBy,
|
||||
@@ -308,7 +317,7 @@ function Table({ columns, data, updateMyData, skipReset }) {
|
||||
useRowSelect,
|
||||
// Here we will use a plugin to add our selection column
|
||||
hooks => {
|
||||
hooks.flatColumns.push(columns => {
|
||||
hooks.visibleColumns.push(columns => {
|
||||
return [
|
||||
{
|
||||
id: 'selection',
|
||||
@@ -380,7 +389,7 @@ function Table({ columns, data, updateMyData, skipReset }) {
|
||||
{cell.isGrouped ? (
|
||||
// If it's a grouped cell, add an expander and row count
|
||||
<>
|
||||
<span {...row.getExpandedToggleProps()}>
|
||||
<span {...row.getToggleRowExpandedProps()}>
|
||||
{row.isExpanded ? '👇' : '👉'}
|
||||
</span>{' '}
|
||||
{cell.render('Cell', { editable: false })} (
|
||||
@@ -390,7 +399,7 @@ function Table({ columns, data, updateMyData, skipReset }) {
|
||||
// If the cell is aggregated, use the Aggregated
|
||||
// renderer for cell
|
||||
cell.render('Aggregated')
|
||||
) : cell.isRepeatedValue ? null : ( // For cells with repeated values, render null
|
||||
) : cell.isPlaceholder ? null : ( // For cells with repeated values, render null
|
||||
// Otherwise, just render the regular cell
|
||||
cell.render('Cell', { editable: true })
|
||||
)}
|
||||
@@ -459,6 +468,7 @@ function Table({ columns, data, updateMyData, skipReset }) {
|
||||
pageCount,
|
||||
canNextPage,
|
||||
canPreviousPage,
|
||||
sortBy,
|
||||
groupBy,
|
||||
expanded: expanded,
|
||||
filters,
|
||||
@@ -488,13 +498,13 @@ function filterGreaterThan(rows, id, filterValue) {
|
||||
filterGreaterThan.autoRemove = val => typeof val !== 'number'
|
||||
|
||||
// This is a custom aggregator that
|
||||
// takes in an array of values and
|
||||
// takes in an array of leaf values and
|
||||
// returns the rounded median
|
||||
function roundedMedian(values) {
|
||||
let min = values[0] || ''
|
||||
let max = values[0] || ''
|
||||
function roundedMedian(leafValues) {
|
||||
let min = leafValues[0] || 0
|
||||
let max = leafValues[0] || 0
|
||||
|
||||
values.forEach(value => {
|
||||
leafValues.forEach(value => {
|
||||
min = Math.min(min, value)
|
||||
max = Math.max(max, value)
|
||||
})
|
||||
@@ -532,7 +542,7 @@ function App() {
|
||||
// count the total rows being aggregated,
|
||||
// then sum any of those counts if they are
|
||||
// aggregated further
|
||||
aggregate: ['sum', 'count'],
|
||||
aggregate: 'count',
|
||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
||||
},
|
||||
{
|
||||
@@ -544,7 +554,7 @@ function App() {
|
||||
// first count the UNIQUE values from the rows
|
||||
// being aggregated, then sum those counts if
|
||||
// they are aggregated further
|
||||
aggregate: ['sum', 'uniqueCount'],
|
||||
aggregate: 'uniqueCount',
|
||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
||||
},
|
||||
],
|
||||
@@ -617,7 +627,7 @@ function App() {
|
||||
)
|
||||
}
|
||||
|
||||
// After data chagnes, we turn the flag back off
|
||||
// After data changes, we turn the flag back off
|
||||
// so that if data actually changes when we're not
|
||||
// editing it, the page is reset
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["react-app"]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
SKIP_PREFLIGHT_CHECK=true
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": ["react-app", "prettier"],
|
||||
"rules": {
|
||||
// "eqeqeq": 0,
|
||||
// "jsx-a11y/anchor-is-valid": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -0,0 +1,29 @@
|
||||
const path = require('path')
|
||||
const resolveFrom = require('resolve-from')
|
||||
|
||||
const fixLinkedDependencies = config => {
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
react$: resolveFrom(path.resolve('node_modules'), 'react'),
|
||||
'react-dom$': resolveFrom(path.resolve('node_modules'), 'react-dom'),
|
||||
},
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
const includeSrcDirectory = config => {
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
modules: [path.resolve('src'), ...config.resolve.modules],
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
['use-babel-config', '.babelrc'],
|
||||
['use-eslint-config', '.eslintrc'],
|
||||
fixLinkedDependencies,
|
||||
// includeSrcDirectory,
|
||||
]
|
||||
@@ -0,0 +1,8 @@
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app) and Rescripts.
|
||||
|
||||
You can:
|
||||
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/material-UI-enhanced-table)
|
||||
- `yarn` and `yarn start` to run and edit the example
|
||||
|
||||
It demonstrates client side pagination, sorting, global search, add row, and delete row.
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "rescripts start",
|
||||
"build": "rescripts build",
|
||||
"test": "rescripts test",
|
||||
"eject": "rescripts eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.8.3",
|
||||
"@material-ui/icons": "^4.5.1",
|
||||
"namor": "^1.1.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"react-table": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rescripts/cli": "^0.0.11",
|
||||
"@rescripts/rescript-use-babel-config": "^0.0.8",
|
||||
"@rescripts/rescript-use-eslint-config": "^0.0.9",
|
||||
"babel-eslint": "10.0.1",
|
||||
"eslint-config-prettier": "^6.3.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import React from 'react'
|
||||
|
||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||
import EnhancedTable from './components/EnhancedTable'
|
||||
import makeData from './makeData'
|
||||
|
||||
const App = () => {
|
||||
const columns = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
},
|
||||
{
|
||||
Header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
},
|
||||
{
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
},
|
||||
{
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
accessor: 'status',
|
||||
},
|
||||
{
|
||||
Header: 'Profile Progress',
|
||||
accessor: 'progress',
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const [data, setData] = React.useState(React.useMemo(() => makeData(20), []))
|
||||
const [skipPageReset, setSkipPageReset] = React.useState(false)
|
||||
|
||||
// We need to keep the table from resetting the pageIndex when we
|
||||
// Update data. So we can keep track of that flag with a ref.
|
||||
|
||||
// When our cell renderer calls updateMyData, we'll use
|
||||
// the rowIndex, columnId and new value to update the
|
||||
// original data
|
||||
const updateMyData = (rowIndex, columnId, value) => {
|
||||
// We also turn on the flag to not reset the page
|
||||
setSkipPageReset(true)
|
||||
setData(old =>
|
||||
old.map((row, index) => {
|
||||
if (index === rowIndex) {
|
||||
return {
|
||||
...old[rowIndex],
|
||||
[columnId]: value,
|
||||
}
|
||||
}
|
||||
return row
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CssBaseline />
|
||||
<EnhancedTable
|
||||
columns={columns}
|
||||
data={data}
|
||||
setData={setData}
|
||||
updateMyData={updateMyData}
|
||||
skipPageReset={skipPageReset}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div')
|
||||
ReactDOM.render(<App />, div)
|
||||
ReactDOM.unmountComponentAtNode(div)
|
||||
})
|
||||
@@ -0,0 +1,152 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import AddIcon from '@material-ui/icons/Add'
|
||||
import Button from '@material-ui/core/Button'
|
||||
import Dialog from '@material-ui/core/Dialog'
|
||||
import DialogActions from '@material-ui/core/DialogActions'
|
||||
import DialogContent from '@material-ui/core/DialogContent'
|
||||
import DialogContentText from '@material-ui/core/DialogContentText'
|
||||
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import PropTypes from 'prop-types'
|
||||
import Switch from '@material-ui/core/Switch'
|
||||
import TextField from '@material-ui/core/TextField'
|
||||
import Tooltip from '@material-ui/core/Tooltip'
|
||||
|
||||
const initialUser = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
age: 0,
|
||||
visits: 0,
|
||||
status: 'single',
|
||||
progress: 0,
|
||||
subRows: undefined,
|
||||
}
|
||||
|
||||
const AddUserDialog = props => {
|
||||
const [user, setUser] = useState(initialUser)
|
||||
const { addUserHandler } = props
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
const [switchState, setSwitchState] = React.useState({
|
||||
addMultiple: false,
|
||||
})
|
||||
|
||||
const handleSwitchChange = name => event => {
|
||||
setSwitchState({ ...switchState, [name]: event.target.checked })
|
||||
}
|
||||
|
||||
const resetSwitch = () => {
|
||||
setSwitchState({ addMultiple: false })
|
||||
}
|
||||
|
||||
const handleClickOpen = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false)
|
||||
resetSwitch()
|
||||
}
|
||||
|
||||
const handleAdd = event => {
|
||||
addUserHandler(user)
|
||||
setUser(initialUser)
|
||||
switchState.addMultiple ? setOpen(true) : setOpen(false)
|
||||
}
|
||||
|
||||
const handleChange = name => ({ target: { value } }) => {
|
||||
setUser({ ...user, [name]: value })
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tooltip title="Add">
|
||||
<IconButton aria-label="add" onClick={handleClickOpen}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
aria-labelledby="form-dialog-title"
|
||||
>
|
||||
<DialogTitle id="form-dialog-title">Add User</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>Demo add item to react table.</DialogContentText>
|
||||
<TextField
|
||||
autoFocus
|
||||
margin="dense"
|
||||
label="First Name"
|
||||
type="text"
|
||||
fullWidth
|
||||
value={user.firstName}
|
||||
onChange={handleChange('firstName')}
|
||||
/>
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="Last Name"
|
||||
type="text"
|
||||
fullWidth
|
||||
value={user.lastName}
|
||||
onChange={handleChange('lastName')}
|
||||
/>
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="Age"
|
||||
type="number"
|
||||
fullWidth
|
||||
value={user.age}
|
||||
onChange={handleChange('age')}
|
||||
/>
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="Visits"
|
||||
type="number"
|
||||
fullWidth
|
||||
value={user.visits}
|
||||
onChange={handleChange('visits')}
|
||||
/>
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="Status"
|
||||
type="text"
|
||||
fullWidth
|
||||
value={user.status}
|
||||
onChange={handleChange('status')}
|
||||
/>
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="Profile Progress"
|
||||
type="number"
|
||||
fullWidth
|
||||
value={user.progress}
|
||||
onChange={handleChange('progress')}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Tooltip title="Add multiple">
|
||||
<Switch
|
||||
checked={switchState.addMultiple}
|
||||
onChange={handleSwitchChange('addMultiple')}
|
||||
value="addMultiple"
|
||||
inputProps={{ 'aria-label': 'secondary checkbox' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Button onClick={handleClose} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleAdd} color="primary">
|
||||
Add
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AddUserDialog.propTypes = {
|
||||
addUserHandler: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default AddUserDialog
|
||||
@@ -0,0 +1,274 @@
|
||||
import React from 'react'
|
||||
|
||||
import Checkbox from '@material-ui/core/Checkbox'
|
||||
import MaUTable from '@material-ui/core/Table'
|
||||
import PropTypes from 'prop-types'
|
||||
import TableBody from '@material-ui/core/TableBody'
|
||||
import TableCell from '@material-ui/core/TableCell'
|
||||
import TableContainer from '@material-ui/core/TableContainer'
|
||||
import TableFooter from '@material-ui/core/TableFooter'
|
||||
import TableHead from '@material-ui/core/TableHead'
|
||||
import TablePagination from '@material-ui/core/TablePagination'
|
||||
import TablePaginationActions from './TablePaginationActions'
|
||||
import TableRow from '@material-ui/core/TableRow'
|
||||
import TableSortLabel from '@material-ui/core/TableSortLabel'
|
||||
import TableToolbar from './TableToolbar'
|
||||
import {
|
||||
useGlobalFilter,
|
||||
usePagination,
|
||||
useRowSelect,
|
||||
useSortBy,
|
||||
useTable,
|
||||
} from 'react-table'
|
||||
|
||||
const IndeterminateCheckbox = React.forwardRef(
|
||||
({ indeterminate, ...rest }, ref) => {
|
||||
const defaultRef = React.useRef()
|
||||
const resolvedRef = ref || defaultRef
|
||||
|
||||
React.useEffect(() => {
|
||||
resolvedRef.current.indeterminate = indeterminate
|
||||
}, [resolvedRef, indeterminate])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Checkbox ref={resolvedRef} {...rest} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
const inputStyle = {
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
border: 0,
|
||||
background: 'transparent',
|
||||
}
|
||||
|
||||
// Create an editable cell renderer
|
||||
const EditableCell = ({
|
||||
cell: { value: initialValue },
|
||||
row: { index },
|
||||
column: { id },
|
||||
updateMyData, // This is a custom function that we supplied to our table instance
|
||||
}) => {
|
||||
// We need to keep and update the state of the cell normally
|
||||
const [value, setValue] = React.useState(initialValue)
|
||||
|
||||
const onChange = e => {
|
||||
setValue(e.target.value)
|
||||
}
|
||||
|
||||
// We'll only update the external data when the input is blurred
|
||||
const onBlur = () => {
|
||||
updateMyData(index, id, value)
|
||||
}
|
||||
|
||||
// If the initialValue is changed externall, sync it up with our state
|
||||
React.useEffect(() => {
|
||||
setValue(initialValue)
|
||||
}, [initialValue])
|
||||
|
||||
return (
|
||||
<input
|
||||
style={inputStyle}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
EditableCell.propTypes = {
|
||||
cell: PropTypes.shape({
|
||||
value: PropTypes.any.isRequired,
|
||||
}),
|
||||
row: PropTypes.shape({
|
||||
index: PropTypes.number.isRequired,
|
||||
}),
|
||||
column: PropTypes.shape({
|
||||
id: PropTypes.number.isRequired,
|
||||
}),
|
||||
updateMyData: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
// Set our editable cell renderer as the default Cell renderer
|
||||
const defaultColumn = {
|
||||
Cell: EditableCell,
|
||||
}
|
||||
|
||||
const EnhancedTable = ({
|
||||
columns,
|
||||
data,
|
||||
setData,
|
||||
updateMyData,
|
||||
skipPageReset,
|
||||
}) => {
|
||||
const {
|
||||
getTableProps,
|
||||
headerGroups,
|
||||
prepareRow,
|
||||
page,
|
||||
gotoPage,
|
||||
setPageSize,
|
||||
preGlobalFilteredRows,
|
||||
setGlobalFilter,
|
||||
state: { pageIndex, pageSize, selectedRowIds, globalFilter },
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
defaultColumn,
|
||||
autoResetPage: !skipPageReset,
|
||||
// updateMyData isn't part of the API, but
|
||||
// anything we put into these options will
|
||||
// automatically be available on the instance.
|
||||
// That way we can call this function from our
|
||||
// cell renderer!
|
||||
updateMyData,
|
||||
},
|
||||
useGlobalFilter,
|
||||
useSortBy,
|
||||
usePagination,
|
||||
useRowSelect,
|
||||
hooks => {
|
||||
hooks.allColumns.push(columns => [
|
||||
// Let's make a column for selection
|
||||
{
|
||||
id: 'selection',
|
||||
// The header can use the table's getToggleAllRowsSelectedProps method
|
||||
// to render a checkbox. Pagination is a problem since this will select all
|
||||
// rows even though not all rows are on the current page. The solution should
|
||||
// be server side pagination. For one, the clients should not download all
|
||||
// rows in most cases. The client should only download data for the current page.
|
||||
// In that case, getToggleAllRowsSelectedProps works fine.
|
||||
Header: ({ getToggleAllRowsSelectedProps }) => (
|
||||
<div>
|
||||
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
|
||||
</div>
|
||||
),
|
||||
// The cell can use the individual row's getToggleRowSelectedProps method
|
||||
// to the render a checkbox
|
||||
Cell: ({ row }) => (
|
||||
<div>
|
||||
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
...columns,
|
||||
])
|
||||
}
|
||||
)
|
||||
|
||||
const handleChangePage = (event, newPage) => {
|
||||
gotoPage(newPage)
|
||||
}
|
||||
|
||||
const handleChangeRowsPerPage = event => {
|
||||
setPageSize(Number(event.target.value))
|
||||
}
|
||||
|
||||
const removeByIndexs = (array, indexs) =>
|
||||
array.filter((_, i) => !indexs.includes(i))
|
||||
|
||||
const deleteUserHandler = event => {
|
||||
const newData = removeByIndexs(
|
||||
data,
|
||||
Object.keys(selectedRowIds).map(x => parseInt(x, 10))
|
||||
)
|
||||
setData(newData)
|
||||
}
|
||||
|
||||
const addUserHandler = user => {
|
||||
const newData = data.concat([user])
|
||||
setData(newData)
|
||||
}
|
||||
|
||||
// Render the UI for your table
|
||||
return (
|
||||
<TableContainer>
|
||||
<TableToolbar
|
||||
numSelected={Object.keys(selectedRowIds).length}
|
||||
deleteUserHandler={deleteUserHandler}
|
||||
addUserHandler={addUserHandler}
|
||||
preGlobalFilteredRows={preGlobalFilteredRows}
|
||||
setGlobalFilter={setGlobalFilter}
|
||||
globalFilter={globalFilter}
|
||||
/>
|
||||
<MaUTable {...getTableProps()}>
|
||||
<TableHead>
|
||||
{headerGroups.map(headerGroup => (
|
||||
<TableRow {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map(column => (
|
||||
<TableCell
|
||||
{...(column.id === 'selection'
|
||||
? column.getHeaderProps()
|
||||
: column.getHeaderProps(column.getSortByToggleProps()))}
|
||||
>
|
||||
{column.render('Header')}
|
||||
{column.id !== 'selection' ? (
|
||||
<TableSortLabel
|
||||
active={column.isSorted}
|
||||
// react-table has a unsorted state which is not treated here
|
||||
direction={column.isSortedDesc ? 'desc' : 'asc'}
|
||||
/>
|
||||
) : null}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{page.map((row, i) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<TableRow {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<TableCell {...cell.getCellProps()}>
|
||||
{cell.render('Cell')}
|
||||
</TableCell>
|
||||
)
|
||||
})}
|
||||
</TableRow>
|
||||
)
|
||||
})}
|
||||
</TableBody>
|
||||
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[
|
||||
5,
|
||||
10,
|
||||
25,
|
||||
{ label: 'All', value: data.length },
|
||||
]}
|
||||
colSpan={3}
|
||||
count={data.length}
|
||||
rowsPerPage={pageSize}
|
||||
page={pageIndex}
|
||||
SelectProps={{
|
||||
inputProps: { 'aria-label': 'rows per page' },
|
||||
native: true,
|
||||
}}
|
||||
onChangePage={handleChangePage}
|
||||
onChangeRowsPerPage={handleChangeRowsPerPage}
|
||||
ActionsComponent={TablePaginationActions}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</MaUTable>
|
||||
</TableContainer>
|
||||
)
|
||||
}
|
||||
|
||||
EnhancedTable.propTypes = {
|
||||
columns: PropTypes.array.isRequired,
|
||||
data: PropTypes.array.isRequired,
|
||||
updateMyData: PropTypes.func.isRequired,
|
||||
setData: PropTypes.func.isRequired,
|
||||
skipPageReset: PropTypes.bool.isRequired,
|
||||
}
|
||||
|
||||
export default EnhancedTable
|
||||
@@ -0,0 +1,85 @@
|
||||
import React from 'react'
|
||||
|
||||
import InputBase from '@material-ui/core/InputBase'
|
||||
import { fade, makeStyles } from '@material-ui/core/styles'
|
||||
import PropTypes from 'prop-types'
|
||||
import SearchIcon from '@material-ui/icons/Search'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
search: {
|
||||
position: 'relative',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: fade(theme.palette.common.white, 0.15),
|
||||
'&:hover': {
|
||||
backgroundColor: fade(theme.palette.common.white, 0.25),
|
||||
},
|
||||
marginRight: theme.spacing(2),
|
||||
marginLeft: 0,
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
marginLeft: theme.spacing(3),
|
||||
width: 'auto',
|
||||
},
|
||||
},
|
||||
searchIcon: {
|
||||
width: theme.spacing(7),
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
pointerEvents: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
inputRoot: {
|
||||
color: 'inherit',
|
||||
},
|
||||
inputInput: {
|
||||
padding: theme.spacing(1, 1, 1, 7),
|
||||
transition: theme.transitions.create('width'),
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: 200,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
const GlobalFilter = ({
|
||||
preGlobalFilteredRows,
|
||||
globalFilter,
|
||||
setGlobalFilter,
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
const count = preGlobalFilteredRows.length
|
||||
|
||||
// Global filter only works with pagination from the first page.
|
||||
// This may not be a problem for server side pagination when
|
||||
// only the current page is downloaded.
|
||||
|
||||
return (
|
||||
<div className={classes.search}>
|
||||
<div className={classes.searchIcon}>
|
||||
<SearchIcon />
|
||||
</div>
|
||||
<InputBase
|
||||
value={globalFilter || ''}
|
||||
onChange={e => {
|
||||
setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
|
||||
}}
|
||||
placeholder={`${count} records...`}
|
||||
classes={{
|
||||
root: classes.inputRoot,
|
||||
input: classes.inputInput,
|
||||
}}
|
||||
inputProps={{ 'aria-label': 'search' }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
GlobalFilter.propTypes = {
|
||||
preGlobalFilteredRows: PropTypes.array.isRequired,
|
||||
globalFilter: PropTypes.string.isRequired,
|
||||
setGlobalFilter: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default GlobalFilter
|
||||
@@ -0,0 +1,88 @@
|
||||
import React from 'react'
|
||||
|
||||
import FirstPageIcon from '@material-ui/icons/FirstPage'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'
|
||||
import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'
|
||||
import LastPageIcon from '@material-ui/icons/LastPage'
|
||||
import { makeStyles, useTheme } from '@material-ui/core/styles'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
flexShrink: 0,
|
||||
marginLeft: theme.spacing(2.5),
|
||||
},
|
||||
}))
|
||||
|
||||
const TablePaginationActions = props => {
|
||||
const classes = useStyles()
|
||||
const theme = useTheme()
|
||||
const { count, page, rowsPerPage, onChangePage } = props
|
||||
|
||||
const handleFirstPageButtonClick = event => {
|
||||
onChangePage(event, 0)
|
||||
}
|
||||
|
||||
const handleBackButtonClick = event => {
|
||||
onChangePage(event, page - 1)
|
||||
}
|
||||
|
||||
const handleNextButtonClick = event => {
|
||||
onChangePage(event, page + 1)
|
||||
}
|
||||
|
||||
const handleLastPageButtonClick = event => {
|
||||
onChangePage(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<IconButton
|
||||
onClick={handleFirstPageButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label="first page"
|
||||
>
|
||||
{theme.direction === 'rtl' ? <LastPageIcon /> : <FirstPageIcon />}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleBackButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label="previous page"
|
||||
>
|
||||
{theme.direction === 'rtl' ? (
|
||||
<KeyboardArrowRight />
|
||||
) : (
|
||||
<KeyboardArrowLeft />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleNextButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label="next page"
|
||||
>
|
||||
{theme.direction === 'rtl' ? (
|
||||
<KeyboardArrowLeft />
|
||||
) : (
|
||||
<KeyboardArrowRight />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleLastPageButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label="last page"
|
||||
>
|
||||
{theme.direction === 'rtl' ? <FirstPageIcon /> : <LastPageIcon />}
|
||||
</IconButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
TablePaginationActions.propTypes = {
|
||||
count: PropTypes.number.isRequired,
|
||||
onChangePage: PropTypes.func.isRequired,
|
||||
page: PropTypes.number.isRequired,
|
||||
rowsPerPage: PropTypes.number.isRequired,
|
||||
}
|
||||
|
||||
export default TablePaginationActions
|
||||
@@ -0,0 +1,91 @@
|
||||
import React from 'react'
|
||||
|
||||
import AddUserDialog from './AddUserDialog'
|
||||
import clsx from 'clsx'
|
||||
import DeleteIcon from '@material-ui/icons/Delete'
|
||||
import GlobalFilter from './GlobalFilter'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import { lighten, makeStyles } from '@material-ui/core/styles'
|
||||
import PropTypes from 'prop-types'
|
||||
import Toolbar from '@material-ui/core/Toolbar'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import Tooltip from '@material-ui/core/Tooltip'
|
||||
|
||||
const useToolbarStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(1),
|
||||
},
|
||||
highlight:
|
||||
theme.palette.type === 'light'
|
||||
? {
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: lighten(theme.palette.secondary.light, 0.85),
|
||||
}
|
||||
: {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.secondary.dark,
|
||||
},
|
||||
title: {
|
||||
flex: '1 1 100%',
|
||||
},
|
||||
}))
|
||||
|
||||
const TableToolbar = props => {
|
||||
const classes = useToolbarStyles()
|
||||
const {
|
||||
numSelected,
|
||||
addUserHandler,
|
||||
deleteUserHandler,
|
||||
preGlobalFilteredRows,
|
||||
setGlobalFilter,
|
||||
globalFilter,
|
||||
} = props
|
||||
return (
|
||||
<Toolbar
|
||||
className={clsx(classes.root, {
|
||||
[classes.highlight]: numSelected > 0,
|
||||
})}
|
||||
>
|
||||
<AddUserDialog addUserHandler={addUserHandler} />
|
||||
{numSelected > 0 ? (
|
||||
<Typography
|
||||
className={classes.title}
|
||||
color="inherit"
|
||||
variant="subtitle1"
|
||||
>
|
||||
{numSelected} selected
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography className={classes.title} variant="h6" id="tableTitle">
|
||||
Users
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{numSelected > 0 ? (
|
||||
<Tooltip title="Delete">
|
||||
<IconButton aria-label="delete" onClick={deleteUserHandler}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<GlobalFilter
|
||||
preGlobalFilteredRows={preGlobalFilteredRows}
|
||||
globalFilter={globalFilter}
|
||||
setGlobalFilter={setGlobalFilter}
|
||||
/>
|
||||
)}
|
||||
</Toolbar>
|
||||
)
|
||||
}
|
||||
|
||||
TableToolbar.propTypes = {
|
||||
numSelected: PropTypes.number.isRequired,
|
||||
addUserHandler: PropTypes.func.isRequired,
|
||||
deleteUserHandler: PropTypes.func.isRequired,
|
||||
setGlobalFilter: PropTypes.func.isRequired,
|
||||
preGlobalFilteredRows: PropTypes.array.isRequired,
|
||||
globalFilter: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
export default TableToolbar
|
||||
@@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import './index.css'
|
||||
import App from './App'
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
||||
@@ -0,0 +1,40 @@
|
||||
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 default function makeData(...lens) {
|
||||
const makeDataLevel = (depth = 0) => {
|
||||
const len = lens[depth]
|
||||
return range(len).map(d => {
|
||||
return {
|
||||
...newPerson(),
|
||||
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return makeDataLevel()
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user