mirror of
https://github.com/gosticks/react-table.git
synced 2026-10-04 14:56:59 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
932f529105 | ||
|
|
bc35e3262d | ||
|
|
1a6056859a | ||
|
|
834ab4b4a7 |
@@ -1,22 +1,15 @@
|
|||||||
{
|
{
|
||||||
"parser": "babel-eslint",
|
"parser": "babel-eslint",
|
||||||
"extends": ["react-app", "prettier"],
|
"extends": ["standard", "standard-react"],
|
||||||
"plugins": [
|
"env": {
|
||||||
"react-hooks"
|
"es6": true
|
||||||
],
|
},
|
||||||
"env": {
|
"plugins": ["react"],
|
||||||
"es6": true
|
"parserOptions": {
|
||||||
},
|
"sourceType": "module"
|
||||||
"parserOptions": {
|
},
|
||||||
"sourceType": "module"
|
"rules": {
|
||||||
},
|
"space-before-function-paren": 0,
|
||||||
"rules": {
|
"react/jsx-boolean-value": 0
|
||||||
"space-before-function-paren": 0,
|
}
|
||||||
"react/jsx-boolean-value": 0
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"react": {
|
|
||||||
"version": "latest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: tannerlinsley
|
|
||||||
patreon: tannerlinsley
|
|
||||||
custom: https://youtube.com/tannerlinsley
|
|
||||||
+4
-4
@@ -1,7 +1,7 @@
|
|||||||
node_modules
|
node_modules/
|
||||||
lib
|
lib/
|
||||||
dist
|
dist/
|
||||||
es
|
es/
|
||||||
docs/build
|
docs/build
|
||||||
react-table.js
|
react-table.js
|
||||||
react-table.min.js
|
react-table.min.js
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
hooks: {
|
|
||||||
'pre-commit': 'lint-staged',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
moduleDirectories: [
|
|
||||||
'node_modules',
|
|
||||||
/*
|
|
||||||
* make 'test/utils' available in tests, e.g.
|
|
||||||
*
|
|
||||||
* const {myModule} = require('utils/my-test-helper')
|
|
||||||
*/
|
|
||||||
__dirname,
|
|
||||||
],
|
|
||||||
|
|
||||||
rootDir: path.resolve(__dirname, '../..'),
|
|
||||||
|
|
||||||
roots: ['<rootDir>/src', __dirname],
|
|
||||||
};
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
const {rootDir} = require('./jest.common');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
rootDir,
|
|
||||||
|
|
||||||
displayName: 'lint',
|
|
||||||
|
|
||||||
runner: 'jest-runner-eslint',
|
|
||||||
|
|
||||||
testMatch: ['<rootDir>/src/**/*.js'],
|
|
||||||
|
|
||||||
testPathIgnorePatterns: ['node_modules', 'coverage', 'dist', '.test.js'],
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
const commonConfig = require('./jest.common');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
...commonConfig,
|
|
||||||
|
|
||||||
displayName: 'unit',
|
|
||||||
|
|
||||||
coverageDirectory: '../../coverage',
|
|
||||||
|
|
||||||
testMatch: ['<rootDir>/tests/**/*.js'],
|
|
||||||
|
|
||||||
transform: {
|
|
||||||
// '^.+\\.js$': '<rootDir>/node_modules/babel-jest',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Vendored
+2
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": ["react-app"],
|
|
||||||
"plugins": ["styled-components"]
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
SKIP_PREFLIGHT_CHECK=true
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["react-app", "prettier"],
|
|
||||||
"rules": {
|
|
||||||
// "eqeqeq": 0,
|
|
||||||
// "jsx-a11y/anchor-is-valid": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# 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*
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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,
|
|
||||||
]
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
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/basic)
|
|
||||||
- `yarn` and `yarn start` to run and edit the example
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"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": "next",
|
|
||||||
"styled-components": "^4.3.2"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"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.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,38 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import styled from 'styled-components'
|
|
||||||
import { useTable } from 'react-table'
|
|
||||||
|
|
||||||
import makeData from './makeData'
|
|
||||||
|
|
||||||
const Styles = styled.div`
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-spacing: 0;
|
|
||||||
border: 1px solid black;
|
|
||||||
|
|
||||||
tr {
|
|
||||||
:last-child {
|
|
||||||
td {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-bottom: 1px solid black;
|
|
||||||
border-right: 1px solid black;
|
|
||||||
|
|
||||||
:last-child {
|
|
||||||
border-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<table {...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) || (
|
|
||||||
<tr {...row.getRowProps()}>
|
|
||||||
{row.cells.map(cell => {
|
|
||||||
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<Styles>
|
|
||||||
<Table columns={columns} data={data} />
|
|
||||||
</Styles>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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);
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import './index.css';
|
|
||||||
import App from './App';
|
|
||||||
import * as serviceWorker from './serviceWorker';
|
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.getElementById('root'));
|
|
||||||
|
|
||||||
// If you want your app to work offline and load faster, you can change
|
|
||||||
// unregister() to register() below. Note this comes with some pitfalls.
|
|
||||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
|
||||||
serviceWorker.unregister();
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import namor from 'namor'
|
|
||||||
|
|
||||||
const range = len => {
|
|
||||||
const arr = []
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
arr.push(i)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
const newPerson = () => {
|
|
||||||
const statusChance = Math.random()
|
|
||||||
return {
|
|
||||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
|
||||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
|
||||||
age: Math.floor(Math.random() * 30),
|
|
||||||
visits: Math.floor(Math.random() * 100),
|
|
||||||
progress: Math.floor(Math.random() * 100),
|
|
||||||
status:
|
|
||||||
statusChance > 0.66
|
|
||||||
? 'relationship'
|
|
||||||
: statusChance > 0.33
|
|
||||||
? 'complicated'
|
|
||||||
: 'single',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export 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()
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
// This optional code is used to register a service worker.
|
|
||||||
// register() is not called by default.
|
|
||||||
|
|
||||||
// This lets the app load faster on subsequent visits in production, and gives
|
|
||||||
// it offline capabilities. However, it also means that developers (and users)
|
|
||||||
// will only see deployed updates on subsequent visits to a page, after all the
|
|
||||||
// existing tabs open on the page have been closed, since previously cached
|
|
||||||
// resources are updated in the background.
|
|
||||||
|
|
||||||
// To learn more about the benefits of this model and instructions on how to
|
|
||||||
// opt-in, read https://bit.ly/CRA-PWA
|
|
||||||
|
|
||||||
const isLocalhost = Boolean(
|
|
||||||
window.location.hostname === 'localhost' ||
|
|
||||||
// [::1] is the IPv6 localhost address.
|
|
||||||
window.location.hostname === '[::1]' ||
|
|
||||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
|
||||||
window.location.hostname.match(
|
|
||||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export function register(config) {
|
|
||||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
|
||||||
// The URL constructor is available in all browsers that support SW.
|
|
||||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
|
||||||
if (publicUrl.origin !== window.location.origin) {
|
|
||||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
|
||||||
// from what our page is served on. This might happen if a CDN is used to
|
|
||||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
|
||||||
|
|
||||||
if (isLocalhost) {
|
|
||||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
|
||||||
checkValidServiceWorker(swUrl, config);
|
|
||||||
|
|
||||||
// Add some additional logging to localhost, pointing developers to the
|
|
||||||
// service worker/PWA documentation.
|
|
||||||
navigator.serviceWorker.ready.then(() => {
|
|
||||||
console.log(
|
|
||||||
'This web app is being served cache-first by a service ' +
|
|
||||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Is not localhost. Just register service worker
|
|
||||||
registerValidSW(swUrl, config);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerValidSW(swUrl, config) {
|
|
||||||
navigator.serviceWorker
|
|
||||||
.register(swUrl)
|
|
||||||
.then(registration => {
|
|
||||||
registration.onupdatefound = () => {
|
|
||||||
const installingWorker = registration.installing;
|
|
||||||
if (installingWorker == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
installingWorker.onstatechange = () => {
|
|
||||||
if (installingWorker.state === 'installed') {
|
|
||||||
if (navigator.serviceWorker.controller) {
|
|
||||||
// At this point, the updated precached content has been fetched,
|
|
||||||
// but the previous service worker will still serve the older
|
|
||||||
// content until all client tabs are closed.
|
|
||||||
console.log(
|
|
||||||
'New content is available and will be used when all ' +
|
|
||||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Execute callback
|
|
||||||
if (config && config.onUpdate) {
|
|
||||||
config.onUpdate(registration);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// At this point, everything has been precached.
|
|
||||||
// It's the perfect time to display a
|
|
||||||
// "Content is cached for offline use." message.
|
|
||||||
console.log('Content is cached for offline use.');
|
|
||||||
|
|
||||||
// Execute callback
|
|
||||||
if (config && config.onSuccess) {
|
|
||||||
config.onSuccess(registration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error during service worker registration:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkValidServiceWorker(swUrl, config) {
|
|
||||||
// Check if the service worker can be found. If it can't reload the page.
|
|
||||||
fetch(swUrl)
|
|
||||||
.then(response => {
|
|
||||||
// Ensure service worker exists, and that we really are getting a JS file.
|
|
||||||
const contentType = response.headers.get('content-type');
|
|
||||||
if (
|
|
||||||
response.status === 404 ||
|
|
||||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
|
||||||
) {
|
|
||||||
// No service worker found. Probably a different app. Reload the page.
|
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
|
||||||
registration.unregister().then(() => {
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Service worker found. Proceed as normal.
|
|
||||||
registerValidSW(swUrl, config);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
console.log(
|
|
||||||
'No internet connection found. App is running in offline mode.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unregister() {
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
|
||||||
registration.unregister();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": ["react-app"],
|
|
||||||
"plugins": ["styled-components"]
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
SKIP_PREFLIGHT_CHECK=true
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["react-app", "prettier"],
|
|
||||||
"rules": {
|
|
||||||
// "eqeqeq": 0,
|
|
||||||
// "jsx-a11y/anchor-is-valid": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# 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*
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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,
|
|
||||||
]
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
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/basic)
|
|
||||||
- `yarn` and `yarn start` to run and edit the example
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"start": "rescripts start",
|
|
||||||
"build": "rescripts build",
|
|
||||||
"test": "rescripts test",
|
|
||||||
"eject": "rescripts eject"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"match-sorter": "^4.0.0",
|
|
||||||
"namor": "^1.1.2",
|
|
||||||
"react": "^16.8.6",
|
|
||||||
"react-dom": "^16.8.6",
|
|
||||||
"react-scripts": "3.0.1",
|
|
||||||
"react-table": "next",
|
|
||||||
"styled-components": "^4.3.2"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"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.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,38 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
@@ -1,322 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import styled from 'styled-components'
|
|
||||||
import { useTable, useFilters } from 'react-table'
|
|
||||||
// A great library for fuzzy filtering/sorting items
|
|
||||||
import matchSorter from 'match-sorter'
|
|
||||||
|
|
||||||
import makeData from './makeData'
|
|
||||||
|
|
||||||
const Styles = styled.div`
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-spacing: 0;
|
|
||||||
border: 1px solid black;
|
|
||||||
|
|
||||||
tr {
|
|
||||||
:last-child {
|
|
||||||
td {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-bottom: 1px solid black;
|
|
||||||
border-right: 1px solid black;
|
|
||||||
|
|
||||||
:last-child {
|
|
||||||
border-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
// Define a default UI for filtering
|
|
||||||
function DefaultColumnFilter({ filterValue, setFilter }) {
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
value={filterValue || ''}
|
|
||||||
onChange={e => {
|
|
||||||
setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
|
|
||||||
}}
|
|
||||||
placeholder="Search..."
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a custom filter UI for selecting
|
|
||||||
// a unique option from a list
|
|
||||||
function SelectColumnFilter({ filterValue, setFilter, preFilteredRows, id }) {
|
|
||||||
// Calculate the options for filtering
|
|
||||||
// using the preFilteredRows
|
|
||||||
const options = React.useMemo(() => {
|
|
||||||
const options = new Set()
|
|
||||||
preFilteredRows.forEach(row => {
|
|
||||||
options.add(row.values[id])
|
|
||||||
})
|
|
||||||
return [...options.values()]
|
|
||||||
}, [id, preFilteredRows])
|
|
||||||
|
|
||||||
// Render a multi-select box
|
|
||||||
return (
|
|
||||||
<select
|
|
||||||
value={filterValue}
|
|
||||||
onChange={e => {
|
|
||||||
setFilter(e.target.value || undefined)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<option value="">All</option>
|
|
||||||
{options.map((option, i) => (
|
|
||||||
<option key={i} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a custom filter UI that uses a
|
|
||||||
// slider to set the filter value between a column's
|
|
||||||
// min and max values
|
|
||||||
function SliderColumnFilter({ filterValue, setFilter, preFilteredRows, id }) {
|
|
||||||
// Calculate the min and max
|
|
||||||
// using the preFilteredRows
|
|
||||||
const [min, max] = React.useMemo(() => {
|
|
||||||
let min = 0
|
|
||||||
let max = 0
|
|
||||||
preFilteredRows.forEach(row => {
|
|
||||||
min = Math.min(row.values[id], min)
|
|
||||||
max = Math.max(row.values[id], max)
|
|
||||||
})
|
|
||||||
return [min, max]
|
|
||||||
}, [id, preFilteredRows])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
value={filterValue || min}
|
|
||||||
onChange={e => {
|
|
||||||
setFilter(parseInt(e.target.value, 10))
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<button onClick={() => setFilter(undefined)}>Off</button>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a custom UI for our 'between' or number range
|
|
||||||
// filter. It uses two number boxes and filters rows to
|
|
||||||
// ones that have values between the two
|
|
||||||
function NumberRangeColumnFilter({ filterValue = [], setFilter }) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
css={`
|
|
||||||
display: flex;
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
value={filterValue[0] || ''}
|
|
||||||
type="number"
|
|
||||||
onChange={e => {
|
|
||||||
const val = e.target.value
|
|
||||||
setFilter((old = []) => [val ? parseInt(val, 10) : undefined, old[1]])
|
|
||||||
}}
|
|
||||||
placeholder="Min"
|
|
||||||
css={`
|
|
||||||
width: 70px;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
to
|
|
||||||
<input
|
|
||||||
value={filterValue[1] || ''}
|
|
||||||
type="number"
|
|
||||||
onChange={e => {
|
|
||||||
const val = e.target.value
|
|
||||||
setFilter((old = []) => [old[0], val ? parseInt(val, 10) : undefined])
|
|
||||||
}}
|
|
||||||
placeholder="Max"
|
|
||||||
css={`
|
|
||||||
width: 70px;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function fuzzyText(rows, id, filterValue) {
|
|
||||||
return matchSorter(rows, filterValue, { keys: [row => row.values[id]] })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let the table remove the filter if the string is empty
|
|
||||||
fuzzyText.autoRemove = val => !val
|
|
||||||
|
|
||||||
// Our table component
|
|
||||||
function Table({ columns, data }) {
|
|
||||||
const filterTypes = React.useMemo(
|
|
||||||
() => ({
|
|
||||||
// Add a new fuzzyText filter type.
|
|
||||||
fuzzyText,
|
|
||||||
// Or, override the default text filter to use
|
|
||||||
// "startWith"
|
|
||||||
text: (rows, id, filterValue) => {
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return rowValue !== undefined
|
|
||||||
? String(rowValue)
|
|
||||||
.toLowerCase()
|
|
||||||
.startsWith(String(filterValue).toLowerCase())
|
|
||||||
: true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultColumn = React.useMemo(
|
|
||||||
() => ({
|
|
||||||
// Let's set up our default Filter UI
|
|
||||||
Filter: DefaultColumnFilter,
|
|
||||||
}),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const { getTableProps, headerGroups, rows, prepareRow, state } = useTable(
|
|
||||||
{
|
|
||||||
columns,
|
|
||||||
data,
|
|
||||||
defaultColumn, // Be sure to pass the defaultColumn option
|
|
||||||
filterTypes,
|
|
||||||
},
|
|
||||||
useFilters // useFilters!
|
|
||||||
)
|
|
||||||
|
|
||||||
// We don't want to render all 2000 rows for this example, so cap
|
|
||||||
// it at 20 for this use case
|
|
||||||
const firstPageRows = rows.slice(0, 20)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<pre>
|
|
||||||
<code>{JSON.stringify(state[0].filters, null, 2)}</code>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
<table {...getTableProps()}>
|
|
||||||
<thead>
|
|
||||||
{headerGroups.map(headerGroup => (
|
|
||||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
|
||||||
{headerGroup.headers.map(column => (
|
|
||||||
<th {...column.getHeaderProps()}>
|
|
||||||
{column.render('Header')}
|
|
||||||
{/* Render the columns filter UI */}
|
|
||||||
<div>{column.canFilter ? column.render('Filter') : null}</div>
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{firstPageRows.map(
|
|
||||||
(row, i) =>
|
|
||||||
prepareRow(row) || (
|
|
||||||
<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>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define a custom filter filter function!
|
|
||||||
function filterGreaterThan(rows, id, filterValue) {
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return rowValue >= filterValue
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is an autoRemove method on the filter function that
|
|
||||||
// when given the new filter value and returns true, the filter
|
|
||||||
// will be automatically removed. Normally this is just an undefined
|
|
||||||
// check, but here, we want to remove the filter if it's not a number
|
|
||||||
filterGreaterThan.autoRemove = val => typeof val !== 'number'
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
const columns = React.useMemo(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
Header: 'Name',
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
Header: 'First Name',
|
|
||||||
accessor: 'firstName',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Last Name',
|
|
||||||
accessor: 'lastName',
|
|
||||||
// Use our custom `fuzzyText` filter on this column
|
|
||||||
filter: 'fuzzyText',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Info',
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
Header: 'Age',
|
|
||||||
accessor: 'age',
|
|
||||||
Filter: SliderColumnFilter,
|
|
||||||
filter: 'equals',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Visits',
|
|
||||||
accessor: 'visits',
|
|
||||||
Filter: NumberRangeColumnFilter,
|
|
||||||
filter: 'between',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Status',
|
|
||||||
accessor: 'status',
|
|
||||||
Filter: SelectColumnFilter,
|
|
||||||
filter: 'includes',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Profile Progress',
|
|
||||||
accessor: 'progress',
|
|
||||||
Filter: SliderColumnFilter,
|
|
||||||
filter: filterGreaterThan,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const data = React.useMemo(() => makeData(10000), [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Styles>
|
|
||||||
<Table columns={columns} data={data} />
|
|
||||||
</Styles>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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);
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import './index.css';
|
|
||||||
import App from './App';
|
|
||||||
import * as serviceWorker from './serviceWorker';
|
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.getElementById('root'));
|
|
||||||
|
|
||||||
// If you want your app to work offline and load faster, you can change
|
|
||||||
// unregister() to register() below. Note this comes with some pitfalls.
|
|
||||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
|
||||||
serviceWorker.unregister();
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import namor from 'namor'
|
|
||||||
|
|
||||||
const range = len => {
|
|
||||||
const arr = []
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
arr.push(i)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
const newPerson = () => {
|
|
||||||
const statusChance = Math.random()
|
|
||||||
return {
|
|
||||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
|
||||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
|
||||||
age: Math.floor(Math.random() * 30),
|
|
||||||
visits: Math.floor(Math.random() * 100),
|
|
||||||
progress: Math.floor(Math.random() * 100),
|
|
||||||
status:
|
|
||||||
statusChance > 0.66
|
|
||||||
? 'relationship'
|
|
||||||
: statusChance > 0.33
|
|
||||||
? 'complicated'
|
|
||||||
: 'single',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export 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()
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
// This optional code is used to register a service worker.
|
|
||||||
// register() is not called by default.
|
|
||||||
|
|
||||||
// This lets the app load faster on subsequent visits in production, and gives
|
|
||||||
// it offline capabilities. However, it also means that developers (and users)
|
|
||||||
// will only see deployed updates on subsequent visits to a page, after all the
|
|
||||||
// existing tabs open on the page have been closed, since previously cached
|
|
||||||
// resources are updated in the background.
|
|
||||||
|
|
||||||
// To learn more about the benefits of this model and instructions on how to
|
|
||||||
// opt-in, read https://bit.ly/CRA-PWA
|
|
||||||
|
|
||||||
const isLocalhost = Boolean(
|
|
||||||
window.location.hostname === 'localhost' ||
|
|
||||||
// [::1] is the IPv6 localhost address.
|
|
||||||
window.location.hostname === '[::1]' ||
|
|
||||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
|
||||||
window.location.hostname.match(
|
|
||||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export function register(config) {
|
|
||||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
|
||||||
// The URL constructor is available in all browsers that support SW.
|
|
||||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
|
||||||
if (publicUrl.origin !== window.location.origin) {
|
|
||||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
|
||||||
// from what our page is served on. This might happen if a CDN is used to
|
|
||||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
|
||||||
|
|
||||||
if (isLocalhost) {
|
|
||||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
|
||||||
checkValidServiceWorker(swUrl, config);
|
|
||||||
|
|
||||||
// Add some additional logging to localhost, pointing developers to the
|
|
||||||
// service worker/PWA documentation.
|
|
||||||
navigator.serviceWorker.ready.then(() => {
|
|
||||||
console.log(
|
|
||||||
'This web app is being served cache-first by a service ' +
|
|
||||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Is not localhost. Just register service worker
|
|
||||||
registerValidSW(swUrl, config);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerValidSW(swUrl, config) {
|
|
||||||
navigator.serviceWorker
|
|
||||||
.register(swUrl)
|
|
||||||
.then(registration => {
|
|
||||||
registration.onupdatefound = () => {
|
|
||||||
const installingWorker = registration.installing;
|
|
||||||
if (installingWorker == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
installingWorker.onstatechange = () => {
|
|
||||||
if (installingWorker.state === 'installed') {
|
|
||||||
if (navigator.serviceWorker.controller) {
|
|
||||||
// At this point, the updated precached content has been fetched,
|
|
||||||
// but the previous service worker will still serve the older
|
|
||||||
// content until all client tabs are closed.
|
|
||||||
console.log(
|
|
||||||
'New content is available and will be used when all ' +
|
|
||||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Execute callback
|
|
||||||
if (config && config.onUpdate) {
|
|
||||||
config.onUpdate(registration);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// At this point, everything has been precached.
|
|
||||||
// It's the perfect time to display a
|
|
||||||
// "Content is cached for offline use." message.
|
|
||||||
console.log('Content is cached for offline use.');
|
|
||||||
|
|
||||||
// Execute callback
|
|
||||||
if (config && config.onSuccess) {
|
|
||||||
config.onSuccess(registration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error during service worker registration:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkValidServiceWorker(swUrl, config) {
|
|
||||||
// Check if the service worker can be found. If it can't reload the page.
|
|
||||||
fetch(swUrl)
|
|
||||||
.then(response => {
|
|
||||||
// Ensure service worker exists, and that we really are getting a JS file.
|
|
||||||
const contentType = response.headers.get('content-type');
|
|
||||||
if (
|
|
||||||
response.status === 404 ||
|
|
||||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
|
||||||
) {
|
|
||||||
// No service worker found. Probably a different app. Reload the page.
|
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
|
||||||
registration.unregister().then(() => {
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Service worker found. Proceed as normal.
|
|
||||||
registerValidSW(swUrl, config);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
console.log(
|
|
||||||
'No internet connection found. App is running in offline mode.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unregister() {
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
|
||||||
registration.unregister();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": ["react-app"],
|
|
||||||
"plugins": ["styled-components"]
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
SKIP_PREFLIGHT_CHECK=true
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["react-app", "prettier"],
|
|
||||||
"rules": {
|
|
||||||
// "eqeqeq": 0,
|
|
||||||
// "jsx-a11y/anchor-is-valid": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# 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*
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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,
|
|
||||||
]
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
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/basic)
|
|
||||||
- `yarn` and `yarn start` to run and edit the example
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"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": "next",
|
|
||||||
"styled-components": "^4.3.2"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"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.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,38 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import styled from 'styled-components'
|
|
||||||
import { useTable, useSortBy } from 'react-table'
|
|
||||||
|
|
||||||
import makeData from './makeData'
|
|
||||||
|
|
||||||
const Styles = styled.div`
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-spacing: 0;
|
|
||||||
border: 1px solid black;
|
|
||||||
|
|
||||||
tr {
|
|
||||||
:last-child {
|
|
||||||
td {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-bottom: 1px solid black;
|
|
||||||
border-right: 1px solid black;
|
|
||||||
|
|
||||||
:last-child {
|
|
||||||
border-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
function Table({ columns, data }) {
|
|
||||||
const { getTableProps, headerGroups, rows, prepareRow } = useTable(
|
|
||||||
{
|
|
||||||
columns,
|
|
||||||
data,
|
|
||||||
},
|
|
||||||
useSortBy
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<table {...getTableProps()}>
|
|
||||||
<thead>
|
|
||||||
{headerGroups.map(headerGroup => (
|
|
||||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
|
||||||
{headerGroup.headers.map(column => (
|
|
||||||
// Add the sorting props to control sorting. For this example
|
|
||||||
// we can add them into the header props
|
|
||||||
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
|
|
||||||
{column.render('Header')}
|
|
||||||
{/* Add a sort direction indicator */}
|
|
||||||
<span>
|
|
||||||
{column.sorted ? (column.sortedDesc ? ' 🔽' : ' 🔼') : ''}
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{rows.map(
|
|
||||||
(row, i) =>
|
|
||||||
prepareRow(row) || (
|
|
||||||
<tr {...row.getRowProps()}>
|
|
||||||
{row.cells.map(cell => {
|
|
||||||
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<Styles>
|
|
||||||
<Table columns={columns} data={data} />
|
|
||||||
</Styles>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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);
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import './index.css';
|
|
||||||
import App from './App';
|
|
||||||
import * as serviceWorker from './serviceWorker';
|
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.getElementById('root'));
|
|
||||||
|
|
||||||
// If you want your app to work offline and load faster, you can change
|
|
||||||
// unregister() to register() below. Note this comes with some pitfalls.
|
|
||||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
|
||||||
serviceWorker.unregister();
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import namor from 'namor'
|
|
||||||
|
|
||||||
const range = len => {
|
|
||||||
const arr = []
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
arr.push(i)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
const newPerson = () => {
|
|
||||||
const statusChance = Math.random()
|
|
||||||
return {
|
|
||||||
firstName: namor.generate({ words: 1, numbers: 0 }),
|
|
||||||
lastName: namor.generate({ words: 1, numbers: 0 }),
|
|
||||||
age: Math.floor(Math.random() * 30),
|
|
||||||
visits: Math.floor(Math.random() * 100),
|
|
||||||
progress: Math.floor(Math.random() * 100),
|
|
||||||
status:
|
|
||||||
statusChance > 0.66
|
|
||||||
? 'relationship'
|
|
||||||
: statusChance > 0.33
|
|
||||||
? 'complicated'
|
|
||||||
: 'single',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export 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()
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
// This optional code is used to register a service worker.
|
|
||||||
// register() is not called by default.
|
|
||||||
|
|
||||||
// This lets the app load faster on subsequent visits in production, and gives
|
|
||||||
// it offline capabilities. However, it also means that developers (and users)
|
|
||||||
// will only see deployed updates on subsequent visits to a page, after all the
|
|
||||||
// existing tabs open on the page have been closed, since previously cached
|
|
||||||
// resources are updated in the background.
|
|
||||||
|
|
||||||
// To learn more about the benefits of this model and instructions on how to
|
|
||||||
// opt-in, read https://bit.ly/CRA-PWA
|
|
||||||
|
|
||||||
const isLocalhost = Boolean(
|
|
||||||
window.location.hostname === 'localhost' ||
|
|
||||||
// [::1] is the IPv6 localhost address.
|
|
||||||
window.location.hostname === '[::1]' ||
|
|
||||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
|
||||||
window.location.hostname.match(
|
|
||||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export function register(config) {
|
|
||||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
|
||||||
// The URL constructor is available in all browsers that support SW.
|
|
||||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
|
||||||
if (publicUrl.origin !== window.location.origin) {
|
|
||||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
|
||||||
// from what our page is served on. This might happen if a CDN is used to
|
|
||||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
|
||||||
|
|
||||||
if (isLocalhost) {
|
|
||||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
|
||||||
checkValidServiceWorker(swUrl, config);
|
|
||||||
|
|
||||||
// Add some additional logging to localhost, pointing developers to the
|
|
||||||
// service worker/PWA documentation.
|
|
||||||
navigator.serviceWorker.ready.then(() => {
|
|
||||||
console.log(
|
|
||||||
'This web app is being served cache-first by a service ' +
|
|
||||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Is not localhost. Just register service worker
|
|
||||||
registerValidSW(swUrl, config);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerValidSW(swUrl, config) {
|
|
||||||
navigator.serviceWorker
|
|
||||||
.register(swUrl)
|
|
||||||
.then(registration => {
|
|
||||||
registration.onupdatefound = () => {
|
|
||||||
const installingWorker = registration.installing;
|
|
||||||
if (installingWorker == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
installingWorker.onstatechange = () => {
|
|
||||||
if (installingWorker.state === 'installed') {
|
|
||||||
if (navigator.serviceWorker.controller) {
|
|
||||||
// At this point, the updated precached content has been fetched,
|
|
||||||
// but the previous service worker will still serve the older
|
|
||||||
// content until all client tabs are closed.
|
|
||||||
console.log(
|
|
||||||
'New content is available and will be used when all ' +
|
|
||||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Execute callback
|
|
||||||
if (config && config.onUpdate) {
|
|
||||||
config.onUpdate(registration);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// At this point, everything has been precached.
|
|
||||||
// It's the perfect time to display a
|
|
||||||
// "Content is cached for offline use." message.
|
|
||||||
console.log('Content is cached for offline use.');
|
|
||||||
|
|
||||||
// Execute callback
|
|
||||||
if (config && config.onSuccess) {
|
|
||||||
config.onSuccess(registration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error during service worker registration:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkValidServiceWorker(swUrl, config) {
|
|
||||||
// Check if the service worker can be found. If it can't reload the page.
|
|
||||||
fetch(swUrl)
|
|
||||||
.then(response => {
|
|
||||||
// Ensure service worker exists, and that we really are getting a JS file.
|
|
||||||
const contentType = response.headers.get('content-type');
|
|
||||||
if (
|
|
||||||
response.status === 404 ||
|
|
||||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
|
||||||
) {
|
|
||||||
// No service worker found. Probably a different app. Reload the page.
|
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
|
||||||
registration.unregister().then(() => {
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Service worker found. Proceed as normal.
|
|
||||||
registerValidSW(swUrl, config);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
console.log(
|
|
||||||
'No internet connection found. App is running in offline mode.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unregister() {
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
|
||||||
registration.unregister();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
|
|
||||||
const lintProject = require('./configs/tests/jest.lint');
|
|
||||||
const unitProject = require('./configs/tests/jest.unit');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
...require('./configs/tests/jest.common'),
|
|
||||||
|
|
||||||
projects: [lintProject, unitProject],
|
|
||||||
|
|
||||||
watchPlugins: [
|
|
||||||
'jest-watch-typeahead/filename',
|
|
||||||
'jest-watch-typeahead/testname',
|
|
||||||
'jest-watch-select-projects',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
'*.js': ['prettier --write', 'git add'],
|
|
||||||
}
|
|
||||||
Generated
+4388
-7277
File diff suppressed because it is too large
Load Diff
+9
-32
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-table",
|
"name": "react-table",
|
||||||
"version": "7.0.0-alpha.9",
|
"version": "7.0.0-alpha.2",
|
||||||
"description": "A fast, lightweight, opinionated table and datagrid built on React",
|
"description": "A fast, lightweight, opinionated table and datagrid built on React",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/tannerlinsley/react-table#readme",
|
"homepage": "https://github.com/tannerlinsley/react-table#readme",
|
||||||
@@ -15,13 +15,11 @@
|
|||||||
"datagrid"
|
"datagrid"
|
||||||
],
|
],
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.es.js",
|
"_module": "dist/index.es.js",
|
||||||
"jsnext:main": "dist/index.es.js",
|
"_jsnext:main": "dist/index.es.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"commit": "git-cz",
|
"test": "cross-env CI=1 react-scripts test --env=jsdom",
|
||||||
"test": "is-ci 'test:ci' 'test:dev'",
|
"test:watch": "react-scripts test --env=jsdom",
|
||||||
"test:dev": "jest --watch",
|
|
||||||
"test:ci": "jest",
|
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
"start": "rollup -c -w",
|
"start": "rollup -c -w",
|
||||||
"prepare": "yarn build",
|
"prepare": "yarn build",
|
||||||
@@ -48,43 +46,22 @@
|
|||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"@babel/runtime": "^7.2.0",
|
"@babel/runtime": "^7.2.0",
|
||||||
"@svgr/rollup": "^4.1.0",
|
"@svgr/rollup": "^4.1.0",
|
||||||
"@testing-library/react": "^8.0.4",
|
|
||||||
"babel-core": "7.0.0-bridge.0",
|
"babel-core": "7.0.0-bridge.0",
|
||||||
"babel-eslint": "9.x",
|
"babel-eslint": "^10.0.1",
|
||||||
"commitizen": "^4.0.3",
|
|
||||||
"cross-env": "^5.1.4",
|
"cross-env": "^5.1.4",
|
||||||
"eslint": "5.x",
|
"eslint": "^5.10.0",
|
||||||
"eslint-config-prettier": "^4.3.0",
|
|
||||||
"eslint-config-react-app": "^4.0.1",
|
|
||||||
"eslint-config-standard": "^12.0.0",
|
"eslint-config-standard": "^12.0.0",
|
||||||
"eslint-config-standard-react": "^7.0.2",
|
"eslint-config-standard-react": "^7.0.2",
|
||||||
"eslint-plugin-flowtype": "2.x",
|
"eslint-plugin-import": "^2.13.0",
|
||||||
"eslint-plugin-import": "2.x",
|
|
||||||
"eslint-plugin-jsx-a11y": "6.x",
|
|
||||||
"eslint-plugin-node": "^8.0.0",
|
"eslint-plugin-node": "^8.0.0",
|
||||||
"eslint-plugin-prettier": "^3.1.0",
|
|
||||||
"eslint-plugin-promise": "^4.0.0",
|
"eslint-plugin-promise": "^4.0.0",
|
||||||
"eslint-plugin-react": "7.x",
|
"eslint-plugin-react": "^7.10.0",
|
||||||
"eslint-plugin-react-hooks": "1.5.0",
|
|
||||||
"eslint-plugin-standard": "^4.0.0",
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
"husky": "^3.0.1",
|
|
||||||
"is-ci-cli": "^1.1.1",
|
|
||||||
"jest": "^24.8.0",
|
|
||||||
"jest-cli": "^24.8.0",
|
|
||||||
"jest-runner-eslint": "^0.7.4",
|
|
||||||
"jest-watch-select-projects": "^0.1.2",
|
|
||||||
"jest-watch-typeahead": "^0.3.1",
|
|
||||||
"lint-staged": "^9.2.1",
|
|
||||||
"rollup": "^0.68.0",
|
"rollup": "^0.68.0",
|
||||||
"rollup-plugin-babel": "^4.1.0",
|
"rollup-plugin-babel": "^4.1.0",
|
||||||
"rollup-plugin-commonjs": "^9.1.3",
|
"rollup-plugin-commonjs": "^9.1.3",
|
||||||
"rollup-plugin-node-resolve": "^4.0.0",
|
"rollup-plugin-node-resolve": "^4.0.0",
|
||||||
"rollup-plugin-peer-deps-external": "^2.2.0",
|
"rollup-plugin-peer-deps-external": "^2.2.0",
|
||||||
"rollup-plugin-uglify": "^6.0.2"
|
"rollup-plugin-uglify": "^6.0.2"
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"commitizen": {
|
|
||||||
"path": "node_modules/cz-conventional-changelog"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
trailingComma: 'es5',
|
|
||||||
tabWidth: 2,
|
|
||||||
semi: false,
|
|
||||||
singleQuote: true,
|
|
||||||
}
|
|
||||||
+21
-30
@@ -6,36 +6,27 @@ import { uglify } from 'rollup-plugin-uglify'
|
|||||||
|
|
||||||
import pkg from './package.json'
|
import pkg from './package.json'
|
||||||
|
|
||||||
export default [
|
export default {
|
||||||
{
|
input: 'src/index.js',
|
||||||
input: 'src/index.js',
|
output: [
|
||||||
output: {
|
{
|
||||||
file: pkg.main,
|
file: pkg.main,
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
sourcemap: true
|
sourcemap: true
|
||||||
},
|
}
|
||||||
plugins: [
|
// {
|
||||||
external(),
|
// file: pkg.module,
|
||||||
babel({
|
// format: "es",
|
||||||
exclude: 'node_modules/**'
|
// sourcemap: true
|
||||||
}),
|
// }
|
||||||
resolve(),
|
],
|
||||||
commonjs(),
|
plugins: [
|
||||||
uglify()
|
external(),
|
||||||
]
|
babel({
|
||||||
},
|
exclude: 'node_modules/**'
|
||||||
{
|
}),
|
||||||
input: 'src/index.js',
|
resolve(),
|
||||||
external: Object.keys(pkg.peerDependencies),
|
commonjs(),
|
||||||
output: {
|
uglify()
|
||||||
file: pkg.module,
|
]
|
||||||
format: 'es',
|
}
|
||||||
sourcemap: true
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
babel({
|
|
||||||
exclude: ['/**/node_modules/**']
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|||||||
+4
-15
@@ -1,20 +1,9 @@
|
|||||||
const actions = {}
|
const actions = {}
|
||||||
const types = {}
|
|
||||||
|
|
||||||
export { actions, types }
|
export { actions }
|
||||||
|
|
||||||
export const addActions = (...acts) => {
|
export const addActions = acts => {
|
||||||
acts.forEach(action => {
|
Object.keys(acts).forEach(key => {
|
||||||
if (actions[action]) {
|
actions[key] = acts[key]
|
||||||
throw new Error(
|
|
||||||
`An React Table action type called ${action} has already been registered!`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// Action values are formatted this way to discourage
|
|
||||||
// you (the dev) from interacting with them in any way
|
|
||||||
// other than importing `{ actions } from 'react-table'`
|
|
||||||
// and referencing an action via `actions[actionName]`
|
|
||||||
actions[action] = `React Table Action: ${action}`
|
|
||||||
types[actions[action]] = true
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
export const text = (rows, id, filterValue) => {
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return rowValue !== undefined
|
|
||||||
? String(rowValue)
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(String(filterValue).toLowerCase())
|
|
||||||
: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
text.autoRemove = val => !val
|
|
||||||
|
|
||||||
export const exactText = (rows, id, filterValue) => {
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return rowValue !== undefined
|
|
||||||
? String(rowValue).toLowerCase() === String(filterValue).toLowerCase()
|
|
||||||
: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exactText.autoRemove = val => !val
|
|
||||||
|
|
||||||
export const exactTextCase = (rows, id, filterValue) => {
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return rowValue !== undefined
|
|
||||||
? String(rowValue) === String(filterValue)
|
|
||||||
: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exactTextCase.autoRemove = val => !val
|
|
||||||
|
|
||||||
export const includes = (rows, id, filterValue) => {
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return filterValue.includes(rowValue)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
includes.autoRemove = val => !val || !val.length
|
|
||||||
|
|
||||||
export const includesAll = (rows, id, filterValue) => {
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return filterValue.every(val => rowValue.includes(val))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
includesAll.autoRemove = val => !val || !val.length
|
|
||||||
|
|
||||||
export const exact = (rows, id, filterValue) => {
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return rowValue === filterValue
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exact.autoRemove = val => typeof val === 'undefined'
|
|
||||||
|
|
||||||
export const equals = (rows, id, filterValue) => {
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return rowValue == filterValue
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
equals.autoRemove = val => val == null
|
|
||||||
|
|
||||||
export const between = (rows, id, filterValue) => {
|
|
||||||
let [min, max] = filterValue || []
|
|
||||||
|
|
||||||
min = typeof min === 'number' ? min : -Infinity
|
|
||||||
max = typeof max === 'number' ? max : Infinity
|
|
||||||
|
|
||||||
if (min > max) {
|
|
||||||
const temp = min
|
|
||||||
min = max
|
|
||||||
max = temp
|
|
||||||
}
|
|
||||||
|
|
||||||
return rows.filter(row => {
|
|
||||||
const rowValue = row.values[id]
|
|
||||||
return rowValue >= min && rowValue <= max
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
between.autoRemove = val =>
|
|
||||||
console.log(val) ||
|
|
||||||
!val ||
|
|
||||||
(typeof val[0] !== 'number' && typeof val[1] !== 'number')
|
|
||||||
+140
-164
@@ -8,166 +8,16 @@ const propTypes = {
|
|||||||
columns: PropTypes.arrayOf(
|
columns: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
Cell: PropTypes.any,
|
Cell: PropTypes.any,
|
||||||
Header: PropTypes.any,
|
Header: PropTypes.any
|
||||||
})
|
})
|
||||||
),
|
)
|
||||||
defaultColumn: PropTypes.any,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the depth of the columns
|
|
||||||
function findMaxDepth(columns, depth = 0) {
|
|
||||||
return columns.reduce((prev, curr) => {
|
|
||||||
if (curr.columns) {
|
|
||||||
return Math.max(prev, findMaxDepth(curr.columns, depth + 1))
|
|
||||||
}
|
|
||||||
return depth
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
function decorateColumn(column, defaultColumn, parent, depth, index) {
|
|
||||||
// Apply the defaultColumn
|
|
||||||
column = { ...defaultColumn, ...column }
|
|
||||||
|
|
||||||
// First check for string accessor
|
|
||||||
let { id, accessor, Header } = column
|
|
||||||
|
|
||||||
if (typeof accessor === 'string') {
|
|
||||||
id = id || accessor
|
|
||||||
const accessorString = accessor
|
|
||||||
accessor = row => getBy(row, accessorString)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!id && typeof Header === 'string') {
|
|
||||||
id = Header
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!id && column.columns) {
|
|
||||||
console.error(column)
|
|
||||||
throw new Error('A column ID (or unique "Header" value) is required!')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!id) {
|
|
||||||
console.error(column)
|
|
||||||
throw new Error('A column ID (or string accessor) is required!')
|
|
||||||
}
|
|
||||||
|
|
||||||
column = {
|
|
||||||
Header: ({ id }) => id,
|
|
||||||
Cell: ({ value }) => value,
|
|
||||||
show: true,
|
|
||||||
...column,
|
|
||||||
id,
|
|
||||||
accessor,
|
|
||||||
parent,
|
|
||||||
depth,
|
|
||||||
index,
|
|
||||||
}
|
|
||||||
|
|
||||||
return column
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the visible columns, headers and flat column list
|
|
||||||
function decorateColumnTree(columns, defaultColumn, parent, depth = 0) {
|
|
||||||
return columns.map((column, columnIndex) => {
|
|
||||||
column = decorateColumn(column, defaultColumn, parent, depth, columnIndex)
|
|
||||||
if (column.columns) {
|
|
||||||
column.columns = decorateColumnTree(
|
|
||||||
column.columns,
|
|
||||||
defaultColumn,
|
|
||||||
column,
|
|
||||||
depth + 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return column
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the header groups from the bottom up
|
|
||||||
function makeHeaderGroups(columns, maxDepth, defaultColumn) {
|
|
||||||
const headerGroups = []
|
|
||||||
|
|
||||||
const removeChildColumns = column => {
|
|
||||||
delete column.columns
|
|
||||||
if (column.parent) {
|
|
||||||
removeChildColumns(column.parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
columns.forEach(removeChildColumns)
|
|
||||||
|
|
||||||
const buildGroup = (columns, depth = 0) => {
|
|
||||||
const headerGroup = {
|
|
||||||
headers: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentColumns = []
|
|
||||||
|
|
||||||
const hasParents = columns.some(col => col.parent)
|
|
||||||
|
|
||||||
columns.forEach(column => {
|
|
||||||
const isFirst = !parentColumns.length
|
|
||||||
let latestParentColumn = [...parentColumns].reverse()[0]
|
|
||||||
|
|
||||||
// If the column has a parent, add it if necessary
|
|
||||||
if (column.parent) {
|
|
||||||
if (isFirst || latestParentColumn.originalID !== column.parent.id) {
|
|
||||||
parentColumns.push({
|
|
||||||
...column.parent,
|
|
||||||
originalID: column.parent.id,
|
|
||||||
id: [column.parent.id, parentColumns.length].join('_'),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if (hasParents) {
|
|
||||||
// If other columns have parents, add a place holder if necessary
|
|
||||||
const placeholderColumn = decorateColumn(
|
|
||||||
{
|
|
||||||
originalID: [column.id, 'placeholder', maxDepth - depth].join('_'),
|
|
||||||
id: [
|
|
||||||
column.id,
|
|
||||||
'placeholder',
|
|
||||||
maxDepth - depth,
|
|
||||||
parentColumns.length,
|
|
||||||
].join('_'),
|
|
||||||
},
|
|
||||||
defaultColumn
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
isFirst ||
|
|
||||||
latestParentColumn.originalID !== placeholderColumn.originalID
|
|
||||||
) {
|
|
||||||
parentColumns.push(placeholderColumn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establish the new columns[] relationship on the parent
|
|
||||||
if (column.parent || hasParents) {
|
|
||||||
latestParentColumn = [...parentColumns].reverse()[0]
|
|
||||||
latestParentColumn.columns = latestParentColumn.columns || []
|
|
||||||
if (!latestParentColumn.columns.includes(column)) {
|
|
||||||
latestParentColumn.columns.push(column)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
headerGroup.headers.push(column)
|
|
||||||
})
|
|
||||||
|
|
||||||
headerGroups.push(headerGroup)
|
|
||||||
|
|
||||||
if (parentColumns.length) {
|
|
||||||
buildGroup(parentColumns)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildGroup(columns)
|
|
||||||
|
|
||||||
return headerGroups.reverse()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useColumns = props => {
|
export const useColumns = props => {
|
||||||
const {
|
const {
|
||||||
debug,
|
debug,
|
||||||
columns: userColumns,
|
columns: userColumns,
|
||||||
defaultColumn = {},
|
state: [{ groupBy }]
|
||||||
state: [{ groupBy }],
|
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
PropTypes.checkPropTypes(propTypes, props, 'property', 'useColumns')
|
PropTypes.checkPropTypes(propTypes, props, 'property', 'useColumns')
|
||||||
@@ -176,37 +26,86 @@ export const useColumns = props => {
|
|||||||
if (debug) console.info('getColumns')
|
if (debug) console.info('getColumns')
|
||||||
|
|
||||||
// Decorate All the columns
|
// Decorate All the columns
|
||||||
let columnTree = decorateColumnTree(userColumns, defaultColumn)
|
let columnTree = decorateColumnTree(userColumns)
|
||||||
|
|
||||||
// Get the flat list of all columns
|
// Get the flat list of all columns
|
||||||
let columns = flattenBy(columnTree, 'columns')
|
let columns = flattenBy(columnTree, 'columns')
|
||||||
|
|
||||||
columns = [
|
columns = [
|
||||||
...groupBy.map(g => columns.find(col => col.id === g)),
|
...groupBy.map(g => columns.find(col => col.id === g)),
|
||||||
...columns.filter(col => !groupBy.includes(col.id)),
|
...columns.filter(col => !groupBy.includes(col.id))
|
||||||
]
|
]
|
||||||
|
|
||||||
// Get headerGroups
|
// Get headerGroups
|
||||||
const headerGroups = makeHeaderGroups(
|
const headerGroups = makeHeaderGroups(columns, findMaxDepth(columnTree))
|
||||||
columns,
|
|
||||||
findMaxDepth(columnTree),
|
|
||||||
defaultColumn
|
|
||||||
)
|
|
||||||
|
|
||||||
const headers = flattenBy(headerGroups, 'headers')
|
const headers = flattenBy(headerGroups, 'headers')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
columns,
|
columns,
|
||||||
headerGroups,
|
headerGroups,
|
||||||
headers,
|
headers
|
||||||
}
|
}
|
||||||
}, [debug, defaultColumn, groupBy, userColumns])
|
}, [groupBy, userColumns])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
columns,
|
columns,
|
||||||
headerGroups,
|
headerGroups,
|
||||||
headers,
|
headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the depth of the columns
|
||||||
|
function findMaxDepth(columns, depth = 0) {
|
||||||
|
return columns.reduce((prev, curr) => {
|
||||||
|
if (curr.columns) {
|
||||||
|
return Math.max(prev, findMaxDepth(curr.columns, depth + 1))
|
||||||
|
}
|
||||||
|
return depth
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function decorateColumn(column, parent) {
|
||||||
|
// First check for string accessor
|
||||||
|
let { id, accessor, Header } = column
|
||||||
|
|
||||||
|
if (typeof accessor === 'string') {
|
||||||
|
id = id || accessor
|
||||||
|
const accessorString = accessor
|
||||||
|
accessor = row => getBy(row, accessorString)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id && typeof Header === 'string') {
|
||||||
|
id = Header
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
// Accessor, but no column id? This is bad.
|
||||||
|
console.error(column)
|
||||||
|
throw new Error('A column id is required!')
|
||||||
|
}
|
||||||
|
|
||||||
|
column = {
|
||||||
|
Header: '',
|
||||||
|
Cell: cell => cell.value,
|
||||||
|
show: true,
|
||||||
|
...column,
|
||||||
|
id,
|
||||||
|
accessor,
|
||||||
|
parent
|
||||||
|
}
|
||||||
|
|
||||||
|
return column
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the visible columns, headers and flat column list
|
||||||
|
function decorateColumnTree(columns, parent, depth = 0) {
|
||||||
|
return columns.map(column => {
|
||||||
|
column = decorateColumn(column, parent)
|
||||||
|
if (column.columns) {
|
||||||
|
column.columns = decorateColumnTree(column.columns, column, depth + 1)
|
||||||
|
}
|
||||||
|
return column
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenBy(columns, childKey) {
|
function flattenBy(columns, childKey) {
|
||||||
@@ -226,4 +125,81 @@ export const useColumns = props => {
|
|||||||
|
|
||||||
return flatColumns
|
return flatColumns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the header groups from the bottom up
|
||||||
|
function makeHeaderGroups(columns, maxDepth) {
|
||||||
|
const headerGroups = []
|
||||||
|
|
||||||
|
const removeChildColumns = column => {
|
||||||
|
delete column.columns
|
||||||
|
if (column.parent) {
|
||||||
|
removeChildColumns(column.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
columns.forEach(removeChildColumns)
|
||||||
|
|
||||||
|
const buildGroup = (columns, depth = 0) => {
|
||||||
|
const headerGroup = {
|
||||||
|
headers: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentColumns = []
|
||||||
|
|
||||||
|
const hasParents = columns.some(col => col.parent)
|
||||||
|
|
||||||
|
columns.forEach(column => {
|
||||||
|
const isFirst = !parentColumns.length
|
||||||
|
let latestParentColumn = [...parentColumns].reverse()[0]
|
||||||
|
|
||||||
|
// If the column has a parent, add it if necessary
|
||||||
|
if (column.parent) {
|
||||||
|
if (isFirst || latestParentColumn.originalID !== column.parent.id) {
|
||||||
|
parentColumns.push({
|
||||||
|
...column.parent,
|
||||||
|
originalID: column.parent.id,
|
||||||
|
id: [column.parent.id, parentColumns.length].join('_')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (hasParents) {
|
||||||
|
// If other columns have parents, add a place holder if necessary
|
||||||
|
const placeholderColumn = decorateColumn({
|
||||||
|
originalID: [column.id, 'placeholder', maxDepth - depth].join('_'),
|
||||||
|
id: [
|
||||||
|
column.id,
|
||||||
|
'placeholder',
|
||||||
|
maxDepth - depth,
|
||||||
|
parentColumns.length
|
||||||
|
].join('_')
|
||||||
|
})
|
||||||
|
if (
|
||||||
|
isFirst ||
|
||||||
|
latestParentColumn.originalID !== placeholderColumn.originalID
|
||||||
|
) {
|
||||||
|
parentColumns.push(placeholderColumn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establish the new columns[] relationship on the parent
|
||||||
|
if (column.parent || hasParents) {
|
||||||
|
latestParentColumn = [...parentColumns].reverse()[0]
|
||||||
|
latestParentColumn.columns = latestParentColumn.columns || []
|
||||||
|
if (!latestParentColumn.columns.includes(column)) {
|
||||||
|
latestParentColumn.columns.push(column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headerGroup.headers.push(column)
|
||||||
|
})
|
||||||
|
|
||||||
|
headerGroups.push(headerGroup)
|
||||||
|
|
||||||
|
if (parentColumns.length) {
|
||||||
|
buildGroup(parentColumns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildGroup(columns)
|
||||||
|
|
||||||
|
return headerGroups.reverse()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,17 @@ import PropTypes from 'prop-types'
|
|||||||
|
|
||||||
import { getBy, getFirstDefined, setBy } from '../utils'
|
import { getBy, getFirstDefined, setBy } from '../utils'
|
||||||
import { addActions, actions } from '../actions'
|
import { addActions, actions } from '../actions'
|
||||||
import { defaultState } from '../hooks/useTableState'
|
import { defaultState } from './useTableState'
|
||||||
|
|
||||||
defaultState.expanded = {}
|
defaultState.expanded = {}
|
||||||
|
|
||||||
addActions('toggleExpanded', 'useExpanded')
|
addActions({
|
||||||
|
toggleExpanded: '__toggleExpanded__',
|
||||||
|
useExpanded: '__useExpanded__'
|
||||||
|
})
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
manualExpandedKey: PropTypes.string,
|
expandedKey: PropTypes.string
|
||||||
paginateSubRows: PropTypes.bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useExpanded = props => {
|
export const useExpanded = props => {
|
||||||
@@ -19,11 +21,11 @@ export const useExpanded = props => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
debug,
|
debug,
|
||||||
|
columns,
|
||||||
rows,
|
rows,
|
||||||
manualExpandedKey = 'expanded',
|
expandedKey = 'expanded',
|
||||||
hooks,
|
hooks,
|
||||||
state: [{ expanded }, setState],
|
state: [{ expanded }, setState]
|
||||||
paginateSubRows = true,
|
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const toggleExpandedByPath = (path, set) => {
|
const toggleExpandedByPath = (path, set) => {
|
||||||
@@ -33,7 +35,7 @@ export const useExpanded = props => {
|
|||||||
set = getFirstDefined(set, !existing)
|
set = getFirstDefined(set, !existing)
|
||||||
return {
|
return {
|
||||||
...old,
|
...old,
|
||||||
expanded: setBy(expanded, path, set),
|
expanded: setBy(expanded, path, set)
|
||||||
}
|
}
|
||||||
}, actions.toggleExpanded)
|
}, actions.toggleExpanded)
|
||||||
}
|
}
|
||||||
@@ -41,7 +43,6 @@ export const useExpanded = props => {
|
|||||||
hooks.row.push(row => {
|
hooks.row.push(row => {
|
||||||
const { path } = row
|
const { path } = row
|
||||||
row.toggleExpanded = set => toggleExpandedByPath(path, set)
|
row.toggleExpanded = set => toggleExpandedByPath(path, set)
|
||||||
return row
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const expandedRows = useMemo(() => {
|
const expandedRows = useMemo(() => {
|
||||||
@@ -51,32 +52,27 @@ export const useExpanded = props => {
|
|||||||
|
|
||||||
// Here we do some mutation, but it's the last stage in the
|
// Here we do some mutation, but it's the last stage in the
|
||||||
// immutable process so this is safe
|
// immutable process so this is safe
|
||||||
const handleRow = (row, depth = 0, parentPath = []) => {
|
const handleRow = (row, index, depth = 0, parentPath = []) => {
|
||||||
// Compute some final state for the row
|
// Compute some final state for the row
|
||||||
const path = [...parentPath, row.index]
|
const path = [...parentPath, index]
|
||||||
|
|
||||||
row.path = path
|
row.path = path
|
||||||
row.depth = depth
|
row.depth = depth
|
||||||
|
|
||||||
row.isExpanded =
|
row.isExpanded =
|
||||||
(row.original && row.original[manualExpandedKey]) ||
|
(row.original && row.original[expandedKey]) || getBy(expanded, path)
|
||||||
getBy(expanded, path)
|
|
||||||
|
|
||||||
if (paginateSubRows || (!paginateSubRows && row.depth === 0)) {
|
expandedRows.push(row)
|
||||||
expandedRows.push(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row.isExpanded && row.subRows && row.subRows.length) {
|
if (row.isExpanded && row.subRows && row.subRows.length) {
|
||||||
row.subRows.forEach((row, i) => handleRow(row, depth + 1, path))
|
row.subRows.forEach((row, i) => handleRow(row, i, depth + 1, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
return row
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.forEach(row => handleRow(row))
|
rows.forEach((row, i) => handleRow(row, i))
|
||||||
|
|
||||||
return expandedRows
|
return expandedRows
|
||||||
}, [debug, rows, manualExpandedKey, expanded, paginateSubRows])
|
}, [rows, expanded, columns])
|
||||||
|
|
||||||
const expandedDepth = findExpandedDepth(expanded)
|
const expandedDepth = findExpandedDepth(expanded)
|
||||||
|
|
||||||
@@ -84,7 +80,7 @@ export const useExpanded = props => {
|
|||||||
...props,
|
...props,
|
||||||
toggleExpandedByPath,
|
toggleExpandedByPath,
|
||||||
expandedDepth,
|
expandedDepth,
|
||||||
rows: expandedRows,
|
rows: expandedRows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Executable
+161
@@ -0,0 +1,161 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { defaultFilterFn, getFirstDefined } from '../utils'
|
||||||
|
import { addActions, actions } from '../actions'
|
||||||
|
import { defaultState } from './useTableState'
|
||||||
|
|
||||||
|
defaultState.filters = {}
|
||||||
|
addActions({
|
||||||
|
setFilter: '__setFilter__',
|
||||||
|
setAllFilters: '__setAllFilters__'
|
||||||
|
})
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
// General
|
||||||
|
columns: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
filterFn: PropTypes.func,
|
||||||
|
filterAll: PropTypes.bool,
|
||||||
|
canFilter: PropTypes.bool,
|
||||||
|
Filter: PropTypes.any
|
||||||
|
})
|
||||||
|
),
|
||||||
|
|
||||||
|
filterFn: PropTypes.func,
|
||||||
|
manualFilters: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFilters = props => {
|
||||||
|
PropTypes.checkPropTypes(propTypes, props, 'property', 'useFilters')
|
||||||
|
|
||||||
|
const {
|
||||||
|
debug,
|
||||||
|
rows,
|
||||||
|
columns,
|
||||||
|
filterFn = defaultFilterFn,
|
||||||
|
manualFilters,
|
||||||
|
disableFilters,
|
||||||
|
hooks,
|
||||||
|
state: [{ filters }, setState]
|
||||||
|
} = props
|
||||||
|
|
||||||
|
columns.forEach(column => {
|
||||||
|
const { id, accessor, canFilter } = column
|
||||||
|
column.canFilter = accessor
|
||||||
|
? getFirstDefined(
|
||||||
|
canFilter,
|
||||||
|
disableFilters === true ? false : undefined,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
: false
|
||||||
|
// Was going to add this to the filter hook
|
||||||
|
column.filterValue = filters[id]
|
||||||
|
})
|
||||||
|
|
||||||
|
const setFilter = (id, val) => {
|
||||||
|
return setState(old => {
|
||||||
|
if (typeof val === 'undefined') {
|
||||||
|
const { [id]: prev, ...rest } = filters
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
filters: {
|
||||||
|
...rest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
filters: {
|
||||||
|
...filters,
|
||||||
|
[id]: val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, actions.setFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setAllFilters = filters => {
|
||||||
|
return setState(old => {
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
filters
|
||||||
|
}
|
||||||
|
}, actions.setAllFilters)
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks.columns.push(columns => {
|
||||||
|
columns.forEach(column => {
|
||||||
|
if (column.canFilter) {
|
||||||
|
column.setFilter = val => setFilter(column.id, val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return columns
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredRows = useMemo(() => {
|
||||||
|
if (manualFilters || !Object.keys(filters).length) {
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) console.info('getFilteredRows')
|
||||||
|
|
||||||
|
// Filters top level and nested rows
|
||||||
|
const filterRows = rows => {
|
||||||
|
let filteredRows = rows
|
||||||
|
|
||||||
|
filteredRows = Object.entries(filters).reduce(
|
||||||
|
(filteredSoFar, [columnID, filterValue]) => {
|
||||||
|
// Find the filters column
|
||||||
|
const column = columns.find(d => d.id === columnID)
|
||||||
|
|
||||||
|
// Don't filter hidden columns or columns that have had their filters disabled
|
||||||
|
if (!column || column.filterable === false) {
|
||||||
|
return filteredSoFar
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterMethod = column.filterMethod || filterFn
|
||||||
|
|
||||||
|
// If 'filterAll' is set to true, pass the entire dataset to the filter method
|
||||||
|
if (column.filterAll) {
|
||||||
|
return filterMethod(filteredSoFar, columnID, filterValue, column)
|
||||||
|
}
|
||||||
|
return filteredSoFar.filter(row =>
|
||||||
|
filterMethod(row, columnID, filterValue, column)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
rows
|
||||||
|
)
|
||||||
|
|
||||||
|
// Apply the filter to any subRows
|
||||||
|
filteredRows = filteredRows.map(row => {
|
||||||
|
if (!row.subRows) {
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...row,
|
||||||
|
subRows: filterRows(row.subRows)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// then filter any rows without subcolumns because it would be strange to show
|
||||||
|
filteredRows = filteredRows.filter(row => {
|
||||||
|
if (!row.subRows) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return row.subRows.length > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return filteredRows
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterRows(rows)
|
||||||
|
}, [rows, filters, manualFilters])
|
||||||
|
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
setFilter,
|
||||||
|
setAllFilters,
|
||||||
|
rows: filteredRows
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import { getFirstDefined, sum } from '../utils'
|
|||||||
export const actions = {}
|
export const actions = {}
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
defaultFlex: PropTypes.number,
|
defaultFlex: PropTypes.number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFlexLayout = props => {
|
export const useFlexLayout = props => {
|
||||||
@@ -16,14 +16,19 @@ export const useFlexLayout = props => {
|
|||||||
hooks: {
|
hooks: {
|
||||||
columns: columnsHooks,
|
columns: columnsHooks,
|
||||||
getRowProps,
|
getRowProps,
|
||||||
getHeaderGroupProps,
|
getHeaderRowProps,
|
||||||
getHeaderProps,
|
getHeaderProps,
|
||||||
getCellProps,
|
getCellProps
|
||||||
},
|
}
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
columnsHooks.push((columns, api) => {
|
columnsHooks.push((columns, api) => {
|
||||||
const visibleColumns = columns.filter(column => column.visible)
|
const visibleColumns = columns.filter(column => {
|
||||||
|
column.visible =
|
||||||
|
typeof column.show === 'function' ? column.show(api) : !!column.show
|
||||||
|
return column.visible
|
||||||
|
})
|
||||||
|
|
||||||
const columnMeasurements = {}
|
const columnMeasurements = {}
|
||||||
|
|
||||||
let sumWidth = 0
|
let sumWidth = 0
|
||||||
@@ -47,33 +52,47 @@ export const useFlexLayout = props => {
|
|||||||
const rowStyles = {
|
const rowStyles = {
|
||||||
style: {
|
style: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
minWidth: `${sumWidth}px`,
|
minWidth: `${sumWidth}px`
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
api.rowStyles = rowStyles
|
api.rowStyles = rowStyles
|
||||||
|
|
||||||
getRowProps.push(() => rowStyles)
|
getRowProps.push(() => rowStyles)
|
||||||
getHeaderGroupProps.push(() => rowStyles)
|
getHeaderRowProps.push(() => rowStyles)
|
||||||
|
|
||||||
getHeaderProps.push(column => ({
|
getHeaderProps.push(column => ({
|
||||||
style: {
|
style: {
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
...getStylesForColumn(column, columnMeasurements, defaultFlex, api),
|
...getStylesForColumn(column, columnMeasurements, defaultFlex, api)
|
||||||
},
|
}
|
||||||
|
// [refKey]: el => {
|
||||||
|
// renderedCellInfoRef.current[key] = {
|
||||||
|
// column,
|
||||||
|
// el
|
||||||
|
// };
|
||||||
|
// },
|
||||||
}))
|
}))
|
||||||
|
|
||||||
getCellProps.push(cell => {
|
getCellProps.push(cell => {
|
||||||
return {
|
return {
|
||||||
style: {
|
style: {
|
||||||
|
// display: 'block',
|
||||||
|
// boxSizing: 'border-box',
|
||||||
...getStylesForColumn(
|
...getStylesForColumn(
|
||||||
cell.column,
|
cell.column,
|
||||||
columnMeasurements,
|
columnMeasurements,
|
||||||
defaultFlex,
|
defaultFlex,
|
||||||
undefined,
|
undefined,
|
||||||
api
|
api
|
||||||
),
|
)
|
||||||
},
|
}
|
||||||
|
// [refKey]: el => {
|
||||||
|
// renderedCellInfoRef.current[columnPathStr] = {
|
||||||
|
// column,
|
||||||
|
// el
|
||||||
|
// };
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -96,7 +115,7 @@ function getStylesForColumn(column, columnMeasurements, defaultFlex, api) {
|
|||||||
return {
|
return {
|
||||||
flex: `${flex} 0 auto`,
|
flex: `${flex} 0 auto`,
|
||||||
width: `${width}px`,
|
width: `${width}px`,
|
||||||
maxWidth: `${maxWidth}px`,
|
maxWidth: `${maxWidth}px`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +127,6 @@ function getSizesForColumn(
|
|||||||
) {
|
) {
|
||||||
if (columns) {
|
if (columns) {
|
||||||
columns = columns
|
columns = columns
|
||||||
.filter(col => col.show || col.visible)
|
|
||||||
.map(column =>
|
.map(column =>
|
||||||
getSizesForColumn(column, columnMeasurements, defaultFlex, api)
|
getSizesForColumn(column, columnMeasurements, defaultFlex, api)
|
||||||
)
|
)
|
||||||
@@ -125,7 +143,7 @@ function getSizesForColumn(
|
|||||||
return {
|
return {
|
||||||
flex,
|
flex,
|
||||||
width,
|
width,
|
||||||
maxWidth,
|
maxWidth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +153,63 @@ function getSizesForColumn(
|
|||||||
width === 'auto'
|
width === 'auto'
|
||||||
? columnMeasurements[id] || defaultFlex
|
? columnMeasurements[id] || defaultFlex
|
||||||
: getFirstDefined(width, minWidth, defaultFlex),
|
: getFirstDefined(width, minWidth, defaultFlex),
|
||||||
maxWidth,
|
maxWidth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const resetRefs = () => {
|
||||||
|
// if (debug) console.info("resetRefs");
|
||||||
|
// renderedCellInfoRef.current = {};
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const calculateAutoWidths = () => {
|
||||||
|
// RAF(() => {
|
||||||
|
// const newColumnMeasurements = {};
|
||||||
|
// Object.values(renderedCellInfoRef.current).forEach(({ column, el }) => {
|
||||||
|
// if (!el) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let measurement = 0;
|
||||||
|
|
||||||
|
// const measureChildren = children => {
|
||||||
|
// if (children) {
|
||||||
|
// [].slice.call(children).forEach(child => {
|
||||||
|
// measurement = Math.max(
|
||||||
|
// measurement,
|
||||||
|
// Math.ceil(child.offsetWidth) || 0
|
||||||
|
// );
|
||||||
|
// measureChildren(child.children);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// return measurement;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const parentDims = getElementDimensions(el);
|
||||||
|
// measureChildren(el.children);
|
||||||
|
|
||||||
|
// newColumnMeasurements[column.id] = Math.max(
|
||||||
|
// newColumnMeasurements[column.id] || 0,
|
||||||
|
// measurement + parentDims.paddingLeft + parentDims.paddingRight
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const oldKeys = Object.keys(columnMeasurements);
|
||||||
|
// const newKeys = Object.keys(newColumnMeasurements);
|
||||||
|
|
||||||
|
// const needsUpdate =
|
||||||
|
// oldKeys.length !== newKeys.length ||
|
||||||
|
// oldKeys.some(key => {
|
||||||
|
// return columnMeasurements[key] !== newColumnMeasurements[key];
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (needsUpdate) {
|
||||||
|
// setState(old => {
|
||||||
|
// return {
|
||||||
|
// ...old,
|
||||||
|
// columnMeasurements: newColumnMeasurements
|
||||||
|
// };
|
||||||
|
// }, actions.updateAutoWidth);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// };
|
||||||
@@ -3,17 +3,19 @@ import PropTypes from 'prop-types'
|
|||||||
|
|
||||||
import * as aggregations from '../aggregations'
|
import * as aggregations from '../aggregations'
|
||||||
import { addActions, actions } from '../actions'
|
import { addActions, actions } from '../actions'
|
||||||
import { defaultState } from '../hooks/useTableState'
|
import { defaultState } from './useTableState'
|
||||||
import {
|
import {
|
||||||
mergeProps,
|
mergeProps,
|
||||||
applyPropHooks,
|
applyPropHooks,
|
||||||
defaultGroupByFn,
|
defaultGroupByFn,
|
||||||
getFirstDefined,
|
getFirstDefined
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
|
||||||
defaultState.groupBy = []
|
defaultState.groupBy = []
|
||||||
|
|
||||||
addActions('toggleGroupBy')
|
addActions({
|
||||||
|
toggleGroupBy: '__toggleGroupBy__'
|
||||||
|
})
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
// General
|
// General
|
||||||
@@ -21,12 +23,12 @@ const propTypes = {
|
|||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
aggregate: PropTypes.func,
|
aggregate: PropTypes.func,
|
||||||
canGroupBy: PropTypes.bool,
|
canGroupBy: PropTypes.bool,
|
||||||
Aggregated: PropTypes.any,
|
Aggregated: PropTypes.any
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
groupByFn: PropTypes.func,
|
groupByFn: PropTypes.func,
|
||||||
manualGrouping: PropTypes.bool,
|
manualGrouping: PropTypes.bool,
|
||||||
aggregations: PropTypes.object,
|
aggregations: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGroupBy = props => {
|
export const useGroupBy = props => {
|
||||||
@@ -41,7 +43,7 @@ export const useGroupBy = props => {
|
|||||||
disableGrouping,
|
disableGrouping,
|
||||||
aggregations: userAggregations = {},
|
aggregations: userAggregations = {},
|
||||||
hooks,
|
hooks,
|
||||||
state: [{ groupBy }, setState],
|
state: [{ groupBy }, setState]
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
columns.forEach(column => {
|
columns.forEach(column => {
|
||||||
@@ -50,10 +52,10 @@ export const useGroupBy = props => {
|
|||||||
|
|
||||||
column.canGroupBy = accessor
|
column.canGroupBy = accessor
|
||||||
? getFirstDefined(
|
? getFirstDefined(
|
||||||
canGroupBy,
|
canGroupBy,
|
||||||
disableGrouping === true ? false : undefined,
|
disableGrouping === true ? false : undefined,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
: false
|
: false
|
||||||
|
|
||||||
column.Aggregated = column.Aggregated || column.Cell
|
column.Aggregated = column.Aggregated || column.Cell
|
||||||
@@ -66,12 +68,12 @@ export const useGroupBy = props => {
|
|||||||
if (resolvedToggle) {
|
if (resolvedToggle) {
|
||||||
return {
|
return {
|
||||||
...old,
|
...old,
|
||||||
groupBy: [...groupBy, id],
|
groupBy: [...groupBy, id]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...old,
|
...old,
|
||||||
groupBy: groupBy.filter(d => d !== id),
|
groupBy: groupBy.filter(d => d !== id)
|
||||||
}
|
}
|
||||||
}, actions.toggleGroupBy)
|
}, actions.toggleGroupBy)
|
||||||
}
|
}
|
||||||
@@ -95,14 +97,14 @@ export const useGroupBy = props => {
|
|||||||
{
|
{
|
||||||
onClick: canGroupBy
|
onClick: canGroupBy
|
||||||
? e => {
|
? e => {
|
||||||
e.persist()
|
e.persist()
|
||||||
column.toggleGroupBy()
|
column.toggleGroupBy()
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
style: {
|
style: {
|
||||||
cursor: canGroupBy ? 'pointer' : undefined,
|
cursor: canGroupBy ? 'pointer' : undefined
|
||||||
},
|
},
|
||||||
title: 'Toggle GroupBy',
|
title: 'Toggle GroupBy'
|
||||||
},
|
},
|
||||||
applyPropHooks(api.hooks.getGroupByToggleProps, column, api),
|
applyPropHooks(api.hooks.getGroupByToggleProps, column, api),
|
||||||
props
|
props
|
||||||
@@ -167,7 +169,7 @@ export const useGroupBy = props => {
|
|||||||
values,
|
values,
|
||||||
subRows,
|
subRows,
|
||||||
depth,
|
depth,
|
||||||
index,
|
index
|
||||||
}
|
}
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
@@ -178,18 +180,10 @@ export const useGroupBy = props => {
|
|||||||
|
|
||||||
// Assign the new data
|
// Assign the new data
|
||||||
return groupRecursively(rows, groupBy)
|
return groupRecursively(rows, groupBy)
|
||||||
}, [
|
}, [rows, groupBy, columns, manualGroupBy])
|
||||||
manualGroupBy,
|
|
||||||
groupBy,
|
|
||||||
debug,
|
|
||||||
rows,
|
|
||||||
columns,
|
|
||||||
userAggregations,
|
|
||||||
groupByFn,
|
|
||||||
])
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
rows: groupedRows,
|
rows: groupedRows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,33 +1,28 @@
|
|||||||
import React from 'react'
|
import { useMemo, useLayoutEffect } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
//
|
//
|
||||||
import { addActions, actions } from '../actions'
|
import { addActions, actions } from '../actions'
|
||||||
import { defaultState } from '../hooks/useTableState'
|
import { defaultState } from './useTableState'
|
||||||
|
|
||||||
defaultState.pageSize = 10
|
defaultState.pageSize = 10
|
||||||
defaultState.pageIndex = 0
|
defaultState.pageIndex = 0
|
||||||
|
|
||||||
addActions('pageChange', 'pageSizeChange')
|
addActions({
|
||||||
|
pageChange: '__pageChange__'
|
||||||
|
})
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
// General
|
// General
|
||||||
manualPagination: PropTypes.bool,
|
manualPagination: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSR has issues with useLayoutEffect still, so pony-fill with useEffect during SSR
|
|
||||||
let useLayoutEffect =
|
|
||||||
typeof window !== 'undefined' && process.env.NODE_ENV === 'production'
|
|
||||||
? React.useLayoutEffect
|
|
||||||
: React.useEffect
|
|
||||||
|
|
||||||
export const usePagination = props => {
|
export const usePagination = props => {
|
||||||
PropTypes.checkPropTypes(propTypes, props, 'property', 'usePagination')
|
PropTypes.checkPropTypes(propTypes, props, 'property', 'usePagination')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
rows,
|
rows,
|
||||||
manualPagination,
|
manualPagination,
|
||||||
disablePageResetOnDataChange,
|
|
||||||
debug,
|
debug,
|
||||||
state: [
|
state: [
|
||||||
{
|
{
|
||||||
@@ -36,34 +31,27 @@ export const usePagination = props => {
|
|||||||
pageCount: userPageCount,
|
pageCount: userPageCount,
|
||||||
filters,
|
filters,
|
||||||
groupBy,
|
groupBy,
|
||||||
sortBy,
|
sortBy
|
||||||
},
|
},
|
||||||
setState,
|
setState
|
||||||
],
|
]
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const pageOptions = React.useMemo(
|
|
||||||
() => [...new Array(userPageCount)].map((d, i) => i),
|
|
||||||
[userPageCount]
|
|
||||||
)
|
|
||||||
|
|
||||||
const rowDep = disablePageResetOnDataChange ? null : rows
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setState(
|
setState(
|
||||||
old => ({
|
old => ({
|
||||||
...old,
|
...old,
|
||||||
pageIndex: 0,
|
pageIndex: 0
|
||||||
}),
|
}),
|
||||||
actions.pageChange
|
actions.pageChange
|
||||||
)
|
)
|
||||||
}, [setState, rowDep, filters, groupBy, sortBy])
|
}, [filters, groupBy, sortBy])
|
||||||
|
|
||||||
const { pages, pageCount } = React.useMemo(() => {
|
const { pages, pageCount } = useMemo(() => {
|
||||||
if (manualPagination) {
|
if (manualPagination) {
|
||||||
return {
|
return {
|
||||||
pages: [rows],
|
pages: [rows],
|
||||||
pageCount: userPageCount,
|
pageCount: userPageCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (debug) console.info('getPages')
|
if (debug) console.info('getPages')
|
||||||
@@ -84,10 +72,11 @@ export const usePagination = props => {
|
|||||||
return {
|
return {
|
||||||
pages,
|
pages,
|
||||||
pageCount,
|
pageCount,
|
||||||
pageOptions,
|
pageOptions
|
||||||
}
|
}
|
||||||
}, [manualPagination, debug, rows, pageOptions, userPageCount, pageSize])
|
}, [rows, pageSize, userPageCount])
|
||||||
|
|
||||||
|
const pageOptions = [...new Array(pageCount)].map((d, i) => i)
|
||||||
const page = manualPagination ? rows : pages[pageIndex] || []
|
const page = manualPagination ? rows : pages[pageIndex] || []
|
||||||
const canPreviousPage = pageIndex > 0
|
const canPreviousPage = pageIndex > 0
|
||||||
const canNextPage = pageIndex < pageCount - 1
|
const canNextPage = pageIndex < pageCount - 1
|
||||||
@@ -100,7 +89,7 @@ export const usePagination = props => {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...old,
|
...old,
|
||||||
pageIndex,
|
pageIndex
|
||||||
}
|
}
|
||||||
}, actions.pageChange)
|
}, actions.pageChange)
|
||||||
}
|
}
|
||||||
@@ -120,24 +109,21 @@ export const usePagination = props => {
|
|||||||
return {
|
return {
|
||||||
...old,
|
...old,
|
||||||
pageIndex,
|
pageIndex,
|
||||||
pageSize,
|
pageSize
|
||||||
}
|
}
|
||||||
}, actions.pageSizeChange)
|
}, actions.setPageSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
pages,
|
pages,
|
||||||
pageOptions,
|
pageOptions,
|
||||||
pageCount,
|
|
||||||
page,
|
page,
|
||||||
canPreviousPage,
|
canPreviousPage,
|
||||||
canNextPage,
|
canNextPage,
|
||||||
gotoPage,
|
gotoPage,
|
||||||
previousPage,
|
previousPage,
|
||||||
nextPage,
|
nextPage,
|
||||||
setPageSize,
|
setPageSize
|
||||||
pageIndex,
|
|
||||||
pageSize,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react'
|
import { useMemo } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
subRowsKey: PropTypes.string,
|
subRowsKey: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useRows = props => {
|
export const useRows = props => {
|
||||||
@@ -10,7 +10,7 @@ export const useRows = props => {
|
|||||||
|
|
||||||
const { debug, columns, subRowsKey = 'subRows', data } = props
|
const { debug, columns, subRowsKey = 'subRows', data } = props
|
||||||
|
|
||||||
const accessedRows = React.useMemo(() => {
|
const accessedRows = useMemo(() => {
|
||||||
if (debug) console.info('getAccessedRows')
|
if (debug) console.info('getAccessedRows')
|
||||||
|
|
||||||
// Access the row's data
|
// Access the row's data
|
||||||
@@ -29,7 +29,7 @@ export const useRows = props => {
|
|||||||
path: [i], // used to create a key for each row even if not nested
|
path: [i], // used to create a key for each row even if not nested
|
||||||
subRows,
|
subRows,
|
||||||
depth,
|
depth,
|
||||||
cells: [{}], // This is a dummy cell
|
cells: [{}] // This is a dummy cell
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override common array functions (and the dummy cell's getCellProps function)
|
// Override common array functions (and the dummy cell's getCellProps function)
|
||||||
@@ -57,10 +57,10 @@ export const useRows = props => {
|
|||||||
|
|
||||||
// Use the resolved data
|
// Use the resolved data
|
||||||
return data.map((d, i) => accessRow(d, i))
|
return data.map((d, i) => accessRow(d, i))
|
||||||
}, [debug, data, subRowsKey, columns])
|
}, [data, columns])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
rows: accessedRows,
|
rows: accessedRows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
export const useSimpleLayout = props => {
|
||||||
|
const {
|
||||||
|
hooks: {
|
||||||
|
columns: columnsHooks,
|
||||||
|
getHeaderProps,
|
||||||
|
getCellProps
|
||||||
|
}
|
||||||
|
} = props
|
||||||
|
|
||||||
|
columnsHooks.push((columns, api) => {
|
||||||
|
columns.forEach(column => {
|
||||||
|
column.visible =
|
||||||
|
typeof column.show === 'function' ? column.show(api) : !!column.show
|
||||||
|
})
|
||||||
|
|
||||||
|
getHeaderProps.push(column => ({
|
||||||
|
style: {
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
width: column.width !== undefined ? `${column.width}px` : 'auto'
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
getCellProps.push(cell => {
|
||||||
|
return {
|
||||||
|
style: {
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
width: cell.column.width !== undefined ? `${cell.column.width}px` : 'auto'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return columns
|
||||||
|
})
|
||||||
|
|
||||||
|
return props
|
||||||
|
}
|
||||||
@@ -2,37 +2,34 @@ import { useMemo } from 'react'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { addActions, actions } from '../actions'
|
import { addActions, actions } from '../actions'
|
||||||
import { defaultState } from '../hooks/useTableState'
|
import { defaultState } from './useTableState'
|
||||||
import * as sortTypes from '../sortTypes'
|
|
||||||
import {
|
import {
|
||||||
mergeProps,
|
mergeProps,
|
||||||
applyPropHooks,
|
applyPropHooks,
|
||||||
getFirstDefined,
|
getFirstDefined,
|
||||||
defaultOrderByFn,
|
defaultOrderByFn,
|
||||||
isFunction,
|
defaultSortByFn
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
|
||||||
defaultState.sortBy = []
|
defaultState.sortBy = []
|
||||||
|
|
||||||
addActions('sortByChange')
|
addActions({
|
||||||
|
sortByChange: '__sortByChange__'
|
||||||
|
})
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
// General
|
// General
|
||||||
columns: PropTypes.arrayOf(
|
columns: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
sortBy: PropTypes.func,
|
sortByFn: PropTypes.func,
|
||||||
defaultSortDesc: PropTypes.bool,
|
efaultSortDesc: PropTypes.bool
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
orderByFn: PropTypes.func,
|
sortByFn: PropTypes.func,
|
||||||
sortTypes: PropTypes.object,
|
|
||||||
defaultSortType: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
||||||
manualSorting: PropTypes.bool,
|
manualSorting: PropTypes.bool,
|
||||||
disableSorting: PropTypes.bool,
|
disableSorting: PropTypes.bool,
|
||||||
defaultSortDesc: PropTypes.bool,
|
defaultSortDesc: PropTypes.bool,
|
||||||
disableMultiSort: PropTypes.bool,
|
disableMultiSort: PropTypes.bool
|
||||||
disableSortRemove: PropTypes.bool,
|
|
||||||
disableMultiRemove: PropTypes.bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSortBy = props => {
|
export const useSortBy = props => {
|
||||||
@@ -43,41 +40,22 @@ export const useSortBy = props => {
|
|||||||
rows,
|
rows,
|
||||||
columns,
|
columns,
|
||||||
orderByFn = defaultOrderByFn,
|
orderByFn = defaultOrderByFn,
|
||||||
defaultSort = 'alphanumeric',
|
sortByFn = defaultSortByFn,
|
||||||
sortTypes: userSortTypes,
|
|
||||||
manualSorting,
|
manualSorting,
|
||||||
disableSorting,
|
disableSorting,
|
||||||
defaultSortDesc,
|
defaultSortDesc,
|
||||||
disableSortRemove,
|
|
||||||
disableMultiRemove,
|
|
||||||
disableMultiSort,
|
|
||||||
hooks,
|
hooks,
|
||||||
state: [{ sortBy }, setState],
|
state: [{ sortBy }, setState]
|
||||||
plugins,
|
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
// If useSortBy should probably come after useFilters for
|
|
||||||
// the best performance, so let's hint to the user about that...
|
|
||||||
const pluginIndex = plugins.indexOf(useSortBy)
|
|
||||||
|
|
||||||
const useFiltersIndex = plugins.findIndex(
|
|
||||||
plugin => plugin.name === 'useFilters'
|
|
||||||
)
|
|
||||||
|
|
||||||
if (useFiltersIndex > pluginIndex) {
|
|
||||||
console.warn(
|
|
||||||
'React Table: useSortBy should be placed before useFilters in your plugin list for better performance!'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
columns.forEach(column => {
|
columns.forEach(column => {
|
||||||
const { accessor, canSortBy } = column
|
const { accessor, canSortBy } = column
|
||||||
column.canSortBy = accessor
|
column.canSortBy = accessor
|
||||||
? getFirstDefined(
|
? getFirstDefined(
|
||||||
canSortBy,
|
canSortBy,
|
||||||
disableSorting === true ? false : undefined,
|
disableSorting === true ? false : undefined,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
: false
|
: false
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -95,77 +73,77 @@ export const useSortBy = props => {
|
|||||||
|
|
||||||
// Find any existing sortBy for this column
|
// Find any existing sortBy for this column
|
||||||
const existingSortBy = sortBy.find(d => d.id === columnID)
|
const existingSortBy = sortBy.find(d => d.id === columnID)
|
||||||
const existingIndex = sortBy.findIndex(d => d.id == columnID)
|
|
||||||
const hasDescDefined = typeof desc !== 'undefined' && desc !== null
|
const hasDescDefined = typeof desc !== 'undefined' && desc !== null
|
||||||
|
|
||||||
let newSortBy = []
|
let newSortBy = []
|
||||||
|
|
||||||
// What should we do with this sort action?
|
// What should we do with this filter?
|
||||||
let action
|
let action
|
||||||
|
|
||||||
if (!disableMultiSort && multi) {
|
if (!multi) {
|
||||||
if (existingSortBy) {
|
if (sortBy.length <= 1 && existingSortBy) {
|
||||||
action = 'toggle'
|
if (existingSortBy.desc) {
|
||||||
|
action = 'remove'
|
||||||
|
} else {
|
||||||
|
action = 'toggle'
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
action = 'add'
|
action = 'replace'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Normal mode
|
if (!existingSortBy) {
|
||||||
if (existingIndex !== sortBy.length - 1) {
|
action = 'add'
|
||||||
action = 'replace'
|
|
||||||
} else if (existingSortBy) {
|
|
||||||
action = 'toggle'
|
|
||||||
} else {
|
} else {
|
||||||
action = 'replace'
|
if (hasDescDefined) {
|
||||||
|
action = 'set'
|
||||||
|
} else {
|
||||||
|
action = 'toggle'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle toggle states that will remove the sortBy
|
|
||||||
if (
|
|
||||||
action === 'toggle' && // Must be toggling
|
|
||||||
!disableSortRemove && // If disableSortRemove, disable in general
|
|
||||||
!hasDescDefined && // Must not be setting desc
|
|
||||||
(multi ? !disableMultiRemove : true) && // If multi, don't allow if disableMultiRemove
|
|
||||||
((existingSortBy && // Finally, detect if it should indeed be removed
|
|
||||||
(existingSortBy.desc && !resolvedDefaultSortDesc)) ||
|
|
||||||
(!existingSortBy.desc && resolvedDefaultSortDesc))
|
|
||||||
) {
|
|
||||||
action = 'remove'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action === 'replace') {
|
if (action === 'replace') {
|
||||||
newSortBy = [
|
newSortBy = [
|
||||||
{
|
{
|
||||||
id: columnID,
|
id: columnID,
|
||||||
desc: hasDescDefined ? desc : resolvedDefaultSortDesc,
|
desc: hasDescDefined ? desc : resolvedDefaultSortDesc
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
} else if (action === 'add') {
|
} else if (action === 'add') {
|
||||||
newSortBy = [
|
newSortBy = [
|
||||||
...sortBy,
|
...sortBy,
|
||||||
{
|
{
|
||||||
id: columnID,
|
id: columnID,
|
||||||
desc: hasDescDefined ? desc : resolvedDefaultSortDesc,
|
desc: hasDescDefined ? desc : resolvedDefaultSortDesc
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
} else if (action === 'toggle') {
|
} else if (action === 'set') {
|
||||||
// This flips (or sets) the
|
|
||||||
newSortBy = sortBy.map(d => {
|
newSortBy = sortBy.map(d => {
|
||||||
if (d.id === columnID) {
|
if (d.id === columnID) {
|
||||||
return {
|
return {
|
||||||
...d,
|
...d,
|
||||||
desc: hasDescDefined ? desc : !existingSortBy.desc,
|
desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
})
|
||||||
|
} else if (action === 'toggle') {
|
||||||
|
newSortBy = sortBy.map(d => {
|
||||||
|
if (d.id === columnID) {
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
desc: !existingSortBy.desc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return d
|
return d
|
||||||
})
|
})
|
||||||
} else if (action === 'remove') {
|
} else if (action === 'remove') {
|
||||||
newSortBy = sortBy.filter(d => d.id !== columnID)
|
newSortBy = []
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...old,
|
...old,
|
||||||
sortBy: newSortBy,
|
sortBy: newSortBy
|
||||||
}
|
}
|
||||||
}, actions.sortByChange)
|
}, actions.sortByChange)
|
||||||
}
|
}
|
||||||
@@ -190,17 +168,17 @@ export const useSortBy = props => {
|
|||||||
{
|
{
|
||||||
onClick: canSortBy
|
onClick: canSortBy
|
||||||
? e => {
|
? e => {
|
||||||
e.persist()
|
e.persist()
|
||||||
column.toggleSortBy(
|
column.toggleSortBy(
|
||||||
undefined,
|
undefined,
|
||||||
!api.disableMultiSort && e.shiftKey
|
!api.disableMultiSort && e.shiftKey
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
style: {
|
style: {
|
||||||
cursor: canSortBy ? 'pointer' : undefined,
|
cursor: canSortBy ? 'pointer' : undefined
|
||||||
},
|
},
|
||||||
title: 'Toggle SortBy',
|
title: 'Toggle SortBy'
|
||||||
},
|
},
|
||||||
applyPropHooks(api.hooks.getSortByToggleProps, column, api),
|
applyPropHooks(api.hooks.getSortByToggleProps, column, api),
|
||||||
props
|
props
|
||||||
@@ -227,11 +205,13 @@ export const useSortBy = props => {
|
|||||||
}
|
}
|
||||||
if (debug) console.info('getSortedRows')
|
if (debug) console.info('getSortedRows')
|
||||||
|
|
||||||
const sortTypesByColumnID = {}
|
const sortMethodsByColumnID = {}
|
||||||
|
|
||||||
columns.forEach(col => {
|
columns
|
||||||
sortTypesByColumnID[col.id] = col.sortBy
|
.filter(col => col.sortMethod)
|
||||||
})
|
.forEach(col => {
|
||||||
|
sortMethodsByColumnID[col.id] = col.sortMethod
|
||||||
|
})
|
||||||
|
|
||||||
const sortData = rows => {
|
const sortData = rows => {
|
||||||
// Use the orderByFn to compose multiple sortBy's together.
|
// Use the orderByFn to compose multiple sortBy's together.
|
||||||
@@ -241,32 +221,21 @@ export const useSortBy = props => {
|
|||||||
rows,
|
rows,
|
||||||
sortBy.map(sort => {
|
sortBy.map(sort => {
|
||||||
// Support custom sorting methods for each column
|
// Support custom sorting methods for each column
|
||||||
const columnSort = sortTypesByColumnID[sort.id]
|
const columnSortBy = sortMethodsByColumnID[sort.id]
|
||||||
|
|
||||||
// Look up sortBy functions in this order:
|
|
||||||
// column function
|
|
||||||
// column string lookup on user sortType
|
|
||||||
// column string lookup on built-in sortType
|
|
||||||
// default function
|
|
||||||
// default string lookup on user sortType
|
|
||||||
// default string lookup on built-in sortType
|
|
||||||
const sortMethod =
|
|
||||||
isFunction(columnSort) ||
|
|
||||||
(userSortTypes || {})[columnSort] ||
|
|
||||||
sortTypes[columnSort] ||
|
|
||||||
isFunction(defaultSort) ||
|
|
||||||
(userSortTypes || {})[defaultSort] ||
|
|
||||||
sortTypes[defaultSort]
|
|
||||||
|
|
||||||
// Return the correct sortFn
|
// Return the correct sortFn
|
||||||
return (a, b) =>
|
return (a, b) =>
|
||||||
sortMethod(a.values[sort.id], b.values[sort.id], sort.desc)
|
(columnSortBy || sortByFn)(
|
||||||
|
a.values[sort.id],
|
||||||
|
b.values[sort.id],
|
||||||
|
sort.desc
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
// Map the directions
|
// Map the directions
|
||||||
sortBy.map(d => !d.desc)
|
sortBy.map(d => !d.desc)
|
||||||
)
|
)
|
||||||
|
|
||||||
// If there are sub-rows, sort them
|
// TODO: this should be optimized. Not good to loop again
|
||||||
sortedData.forEach(row => {
|
sortedData.forEach(row => {
|
||||||
if (!row.subRows) {
|
if (!row.subRows) {
|
||||||
return
|
return
|
||||||
@@ -278,19 +247,10 @@ export const useSortBy = props => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return sortData(rows)
|
return sortData(rows)
|
||||||
}, [
|
}, [rows, columns, sortBy, manualSorting])
|
||||||
manualSorting,
|
|
||||||
sortBy,
|
|
||||||
debug,
|
|
||||||
columns,
|
|
||||||
rows,
|
|
||||||
orderByFn,
|
|
||||||
userSortTypes,
|
|
||||||
defaultSort,
|
|
||||||
])
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
rows: sortedRows,
|
rows: sortedRows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+35
-53
@@ -1,32 +1,24 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
//
|
//
|
||||||
import { applyHooks, applyPropHooks, mergeProps, flexRender } from '../utils'
|
import { flexRender, applyHooks, applyPropHooks, mergeProps } from '../utils'
|
||||||
|
|
||||||
import { useTableState } from './useTableState'
|
import { useTableState } from './useTableState'
|
||||||
import { useColumns } from './useColumns'
|
|
||||||
import { useRows } from './useRows'
|
const renderErr =
|
||||||
|
'You must specify a render "type". This could be "Header", "Filter", or any other custom renderers you have set on your column.'
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
// General
|
// General
|
||||||
data: PropTypes.array.isRequired,
|
data: PropTypes.array.isRequired,
|
||||||
debug: PropTypes.bool,
|
debug: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderErr =
|
|
||||||
'You must specify a valid render component. This could be "column.Cell", "column.Header", "column.Filter", "column.Aggregated" or any other custom renderer component.'
|
|
||||||
|
|
||||||
export const useTable = (props, ...plugins) => {
|
export const useTable = (props, ...plugins) => {
|
||||||
// Validate props
|
// Validate props
|
||||||
PropTypes.checkPropTypes(propTypes, props, 'property', 'useTable')
|
PropTypes.checkPropTypes(propTypes, props, 'property', 'useTable')
|
||||||
|
|
||||||
// Destructure props
|
// Destructure props
|
||||||
let {
|
let { data = [], state: userState, debug } = props
|
||||||
data,
|
|
||||||
state: userState,
|
|
||||||
useColumns: userUseColumns = useColumns,
|
|
||||||
useRows: userUseRows = useRows,
|
|
||||||
debug,
|
|
||||||
} = props
|
|
||||||
|
|
||||||
debug = process.env.NODE_ENV === 'production' ? false : debug
|
debug = process.env.NODE_ENV === 'production' ? false : debug
|
||||||
|
|
||||||
@@ -44,11 +36,12 @@ export const useTable = (props, ...plugins) => {
|
|||||||
headerGroups: [],
|
headerGroups: [],
|
||||||
rows: [],
|
rows: [],
|
||||||
row: [],
|
row: [],
|
||||||
|
renderableRows: [],
|
||||||
getTableProps: [],
|
getTableProps: [],
|
||||||
getRowProps: [],
|
getRowProps: [],
|
||||||
getHeaderGroupProps: [],
|
getHeaderRowProps: [],
|
||||||
getHeaderProps: [],
|
getHeaderProps: [],
|
||||||
getCellProps: [],
|
getCellProps: []
|
||||||
}
|
}
|
||||||
|
|
||||||
// The initial api
|
// The initial api
|
||||||
@@ -56,61 +49,50 @@ export const useTable = (props, ...plugins) => {
|
|||||||
...props,
|
...props,
|
||||||
data,
|
data,
|
||||||
state,
|
state,
|
||||||
hooks,
|
hooks
|
||||||
plugins,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug) console.time('hooks')
|
if (debug) console.time('hooks')
|
||||||
// Loop through plugins to build the api out
|
// Loop through plugins to build the api out
|
||||||
api = [userUseColumns, userUseRows, ...plugins]
|
api = plugins.filter(Boolean).reduce((prev, next) => next(prev), api)
|
||||||
.filter(Boolean)
|
|
||||||
.reduce((prev, next) => next(prev), api)
|
|
||||||
if (debug) console.timeEnd('hooks')
|
if (debug) console.timeEnd('hooks')
|
||||||
|
|
||||||
// Determine column visibility
|
// Run the beforeRender hook
|
||||||
api.columns.forEach(column => {
|
if (debug) console.time('hooks.beforeRender')
|
||||||
column.visible =
|
applyHooks(api.hooks.beforeRender, undefined, api)
|
||||||
typeof column.show === 'function' ? column.show(api) : !!column.show
|
if (debug) console.timeEnd('hooks.beforeRender')
|
||||||
})
|
|
||||||
|
|
||||||
// Allow hooks to decorate columns
|
|
||||||
if (debug) console.time('hooks.columns')
|
if (debug) console.time('hooks.columns')
|
||||||
api.columns = applyHooks(api.hooks.columns, api.columns, api)
|
api.columns = applyHooks(api.hooks.columns, api.columns, api)
|
||||||
if (debug) console.timeEnd('hooks.columns')
|
if (debug) console.timeEnd('hooks.columns')
|
||||||
|
|
||||||
// Allow hooks to decorate headers
|
|
||||||
if (debug) console.time('hooks.headers')
|
if (debug) console.time('hooks.headers')
|
||||||
api.headers = applyHooks(api.hooks.headers, api.headers, api)
|
api.headers = applyHooks(api.hooks.headers, api.headers, api)
|
||||||
if (debug) console.timeEnd('hooks.headers')
|
if (debug) console.timeEnd('hooks.headers')
|
||||||
;[...api.columns, ...api.headers].forEach(column => {
|
;[...api.columns, ...api.headers].forEach(column => {
|
||||||
// Give columns/headers rendering power
|
// Give columns/headers rendering power
|
||||||
column.render = (type, userProps = {}) => {
|
column.render = (type, userProps = {}) => {
|
||||||
const Comp = typeof type === 'string' ? column[type] : type
|
if (!type) {
|
||||||
|
|
||||||
if (typeof Comp === 'undefined') {
|
|
||||||
throw new Error(renderErr)
|
throw new Error(renderErr)
|
||||||
}
|
}
|
||||||
|
return flexRender(column[type], {
|
||||||
return flexRender(Comp, {
|
|
||||||
...api,
|
...api,
|
||||||
...column,
|
...column,
|
||||||
...userProps,
|
...userProps
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give columns/headers a default getHeaderProps
|
// Give columns/headers getHeaderProps
|
||||||
column.getHeaderProps = props =>
|
column.getHeaderProps = props =>
|
||||||
mergeProps(
|
mergeProps(
|
||||||
{
|
{
|
||||||
key: ['header', column.id].join('_'),
|
key: ['header', column.id].join('_')
|
||||||
colSpan: column.columns ? column.columns.length : 1,
|
|
||||||
},
|
},
|
||||||
applyPropHooks(api.hooks.getHeaderProps, column, api),
|
applyPropHooks(api.hooks.getHeaderProps, column, api),
|
||||||
props
|
props
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Allow hooks to decorate headerGroups
|
|
||||||
if (debug) console.time('hooks.headerGroups')
|
if (debug) console.time('hooks.headerGroups')
|
||||||
api.headerGroups = applyHooks(
|
api.headerGroups = applyHooks(
|
||||||
api.hooks.headerGroups,
|
api.hooks.headerGroups,
|
||||||
@@ -134,12 +116,12 @@ export const useTable = (props, ...plugins) => {
|
|||||||
|
|
||||||
// Give headerGroups getRowProps
|
// Give headerGroups getRowProps
|
||||||
if (headerGroup.headers.length) {
|
if (headerGroup.headers.length) {
|
||||||
headerGroup.getHeaderGroupProps = (props = {}) =>
|
headerGroup.getRowProps = (props = {}) =>
|
||||||
mergeProps(
|
mergeProps(
|
||||||
{
|
{
|
||||||
key: [`header${i}`].join('_'),
|
key: [`header${i}`].join('_')
|
||||||
},
|
},
|
||||||
applyPropHooks(api.hooks.getHeaderGroupProps, headerGroup, api),
|
applyPropHooks(api.hooks.getHeaderRowProps, headerGroup, api),
|
||||||
props
|
props
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
@@ -162,7 +144,7 @@ export const useTable = (props, ...plugins) => {
|
|||||||
row.getRowProps = props =>
|
row.getRowProps = props =>
|
||||||
mergeProps(
|
mergeProps(
|
||||||
{ key: ['row', ...path].join('_') },
|
{ key: ['row', ...path].join('_') },
|
||||||
applyPropHooks(api.hooks.getRowProps, row, api),
|
applyHooks(api.hooks.getRowProps, row, api),
|
||||||
props
|
props
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -176,33 +158,30 @@ export const useTable = (props, ...plugins) => {
|
|||||||
const cell = {
|
const cell = {
|
||||||
column,
|
column,
|
||||||
row,
|
row,
|
||||||
value: row.values[column.id],
|
value: row.values[column.id]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give each cell a getCellProps base
|
|
||||||
cell.getCellProps = props => {
|
cell.getCellProps = props => {
|
||||||
const columnPathStr = [path, column.id].join('_')
|
const columnPathStr = [path, column.id].join('_')
|
||||||
return mergeProps(
|
return mergeProps(
|
||||||
{
|
{
|
||||||
key: ['cell', columnPathStr].join('_'),
|
key: ['cell', columnPathStr].join('_')
|
||||||
},
|
},
|
||||||
applyPropHooks(api.hooks.getCellProps, cell, api),
|
applyPropHooks(api.hooks.getCellProps, cell, api),
|
||||||
props
|
props
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give each cell a renderer function (supports multiple renderers)
|
|
||||||
cell.render = (type, userProps = {}) => {
|
cell.render = (type, userProps = {}) => {
|
||||||
const Comp = typeof type === 'string' ? column[type] : type
|
if (!type) {
|
||||||
|
throw new Error(
|
||||||
if (typeof Comp === 'undefined') {
|
'You must specify a render "type". This could be "Cell", "Header", "Filter", "Aggregated" or any other custom renderers you have set on your column.'
|
||||||
throw new Error(renderErr)
|
)
|
||||||
}
|
}
|
||||||
|
return flexRender(column[type], {
|
||||||
return flexRender(Comp, {
|
|
||||||
...api,
|
...api,
|
||||||
...cell,
|
...cell,
|
||||||
...userProps,
|
...userProps
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,5 +192,8 @@ export const useTable = (props, ...plugins) => {
|
|||||||
api.getTableProps = userProps =>
|
api.getTableProps = userProps =>
|
||||||
mergeProps(applyPropHooks(api.hooks.getTableProps, api), userProps)
|
mergeProps(applyPropHooks(api.hooks.getTableProps, api), userProps)
|
||||||
|
|
||||||
|
api.getRowProps = userProps =>
|
||||||
|
mergeProps(applyPropHooks(api.hooks.getRowProps, api), userProps)
|
||||||
|
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-38
@@ -1,6 +1,4 @@
|
|||||||
import React from 'react'
|
import { useState, useMemo } from 'react'
|
||||||
//
|
|
||||||
import { types } from '../actions'
|
|
||||||
|
|
||||||
export const defaultState = {}
|
export const defaultState = {}
|
||||||
|
|
||||||
@@ -8,49 +6,29 @@ const defaultReducer = (old, newState) => newState
|
|||||||
|
|
||||||
export const useTableState = (
|
export const useTableState = (
|
||||||
initialState = {},
|
initialState = {},
|
||||||
overrides,
|
overrides = {},
|
||||||
{ reducer = defaultReducer, useState: userUseState = React.useState } = {}
|
{ reducer = defaultReducer, useState: userUseState = useState } = {}
|
||||||
) => {
|
) => {
|
||||||
let [state, setState] = userUseState({
|
let [state, setState] = userUseState({
|
||||||
...defaultState,
|
...defaultState,
|
||||||
...initialState,
|
...initialState
|
||||||
})
|
})
|
||||||
|
|
||||||
const overriddenState = React.useMemo(() => {
|
const overriddenState = useMemo(() => {
|
||||||
const newState = {
|
const newState = {
|
||||||
...state,
|
...state
|
||||||
}
|
|
||||||
if (overrides) {
|
|
||||||
Object.keys(overrides).forEach(key => {
|
|
||||||
newState[key] = overrides[key]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
Object.keys(overrides).forEach(key => {
|
||||||
|
newState[key] = overrides[key]
|
||||||
|
})
|
||||||
return newState
|
return newState
|
||||||
}, [overrides, state])
|
}, [state, ...Object.values(overrides)])
|
||||||
|
|
||||||
const overriddenStateRef = React.useRef()
|
const reducedSetState = (updater, type) =>
|
||||||
overriddenStateRef.current = overriddenState
|
setState(old => {
|
||||||
|
const newState = updater(old)
|
||||||
|
return reducer(old, newState, type)
|
||||||
|
})
|
||||||
|
|
||||||
const reducedSetState = React.useCallback(
|
return [overriddenState, reducedSetState]
|
||||||
(updater, type) => {
|
|
||||||
if (!types[type]) {
|
|
||||||
console.info({
|
|
||||||
stateUpdaterFn: updater,
|
|
||||||
actionType: type,
|
|
||||||
currentState: overriddenStateRef.current,
|
|
||||||
})
|
|
||||||
throw new Error('Detected an unknown table action! (Details Above)')
|
|
||||||
}
|
|
||||||
return setState(old => {
|
|
||||||
const newState = updater(old)
|
|
||||||
return reducer(old, newState, type)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[reducer, setState]
|
|
||||||
)
|
|
||||||
|
|
||||||
return React.useMemo(() => [overriddenState, reducedSetState], [
|
|
||||||
overriddenState,
|
|
||||||
reducedSetState,
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import React from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
// Token pagination behaves a bit differently from
|
// Token pagination behaves a bit differently from
|
||||||
// index based pagination. This hook aids in that process.
|
// index based pagination. This hook aids in that process.
|
||||||
|
|
||||||
export const useTokenPagination = () => {
|
export const useTokenPagination = () => {
|
||||||
const [pageToken, setPageToken] = React.useState()
|
const [pageToken, setPageToken] = useState()
|
||||||
const [nextPageToken, setNextPageToken] = React.useState()
|
const [nextPageToken, setNextPageToken] = useState()
|
||||||
const [previousPageTokens, setPreviousPageTokens] = React.useState([])
|
const [previousPageTokens, setPreviousPageTokens] = useState([])
|
||||||
const [pageIndex, setPageIndex] = React.useState(0)
|
const [pageIndex, setPageIndex] = useState(0)
|
||||||
|
|
||||||
// Since we're using pagination tokens intead of index, we need
|
// Since we're using pagination tokens intead of index, we need
|
||||||
// to be a bit clever with page-like navigation here.
|
// to be a bit clever with page-like navigation here.
|
||||||
@@ -46,6 +46,6 @@ export const useTokenPagination = () => {
|
|||||||
nextPage,
|
nextPage,
|
||||||
canPreviousPage,
|
canPreviousPage,
|
||||||
canNextPage,
|
canNextPage,
|
||||||
resetPagination,
|
resetPagination
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+10
-11
@@ -1,14 +1,13 @@
|
|||||||
import * as utils from './utils'
|
|
||||||
export { utils }
|
|
||||||
export { useTable } from './hooks/useTable'
|
export { useTable } from './hooks/useTable'
|
||||||
export { useColumns } from './hooks/useColumns'
|
export { useColumns } from './hooks/useColumns'
|
||||||
export { useRows } from './hooks/useRows'
|
export { useRows } from './hooks/useRows'
|
||||||
export { useTableState, defaultState } from './hooks/useTableState'
|
export { useSimpleLayout } from './hooks/useSimpleLayout'
|
||||||
export { useExpanded } from './plugin-hooks/useExpanded'
|
export { useExpanded } from './hooks/useExpanded'
|
||||||
export { useFilters } from './plugin-hooks/useFilters'
|
export { useFilters } from './hooks/useFilters'
|
||||||
export { useGroupBy } from './plugin-hooks/useGroupBy'
|
export { useGroupBy } from './hooks/useGroupBy'
|
||||||
export { useSortBy } from './plugin-hooks/useSortBy'
|
export { useSortBy } from './hooks/useSortBy'
|
||||||
export { usePagination } from './plugin-hooks/usePagination'
|
export { usePagination } from './hooks/usePagination'
|
||||||
export { useFlexLayout } from './plugin-hooks/useFlexLayout'
|
export { useTableState } from './hooks/useTableState'
|
||||||
export { useTokenPagination } from './plugin-hooks/useTokenPagination'
|
export { useFlexLayout } from './hooks/useFlexLayout'
|
||||||
export { actions, addActions } from './actions'
|
export { useTokenPagination } from './hooks/useTokenPagination'
|
||||||
|
export { actions } from './actions'
|
||||||
|
|||||||
@@ -1,228 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
import { getFirstDefined, isFunction } from '../utils'
|
|
||||||
import * as filterTypes from '../filterTypes'
|
|
||||||
import { addActions, actions } from '../actions'
|
|
||||||
import { defaultState } from '../hooks/useTableState'
|
|
||||||
|
|
||||||
defaultState.filters = {}
|
|
||||||
|
|
||||||
addActions('setFilter', 'setAllFilters')
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
// General
|
|
||||||
columns: PropTypes.arrayOf(
|
|
||||||
PropTypes.shape({
|
|
||||||
filterFn: PropTypes.func,
|
|
||||||
filterAll: PropTypes.bool,
|
|
||||||
canFilter: PropTypes.bool,
|
|
||||||
Filter: PropTypes.any,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
|
|
||||||
filterFn: PropTypes.func,
|
|
||||||
manualFilters: PropTypes.bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useFilters = props => {
|
|
||||||
PropTypes.checkPropTypes(propTypes, props, 'property', 'useFilters')
|
|
||||||
|
|
||||||
const {
|
|
||||||
debug,
|
|
||||||
rows,
|
|
||||||
columns,
|
|
||||||
filterTypes: userFilterTypes,
|
|
||||||
manualFilters,
|
|
||||||
disableFilters,
|
|
||||||
hooks,
|
|
||||||
state: [{ filters }, setState],
|
|
||||||
} = props
|
|
||||||
|
|
||||||
const setFilter = (id, updater) => {
|
|
||||||
const column = columns.find(d => d.id === id)
|
|
||||||
|
|
||||||
const filterMethod = getFilterMethod(
|
|
||||||
column.filter,
|
|
||||||
userFilterTypes || {},
|
|
||||||
filterTypes
|
|
||||||
)
|
|
||||||
|
|
||||||
const { autoRemove } = filterMethod
|
|
||||||
|
|
||||||
return setState(old => {
|
|
||||||
const newFilter =
|
|
||||||
typeof updater === 'function' ? updater(old.filters[id]) : updater
|
|
||||||
|
|
||||||
//
|
|
||||||
if (shouldAutoRemove(autoRemove, newFilter)) {
|
|
||||||
const { [id]: remove, ...newFilters } = old.filters
|
|
||||||
return {
|
|
||||||
...old,
|
|
||||||
filters: newFilters,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...old,
|
|
||||||
filters: {
|
|
||||||
...old.filters,
|
|
||||||
[id]: newFilter,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}, actions.setFilter)
|
|
||||||
}
|
|
||||||
|
|
||||||
const setAllFilters = updater => {
|
|
||||||
return setState(old => {
|
|
||||||
const newFilters = typeof updater === 'function' ? updater(old) : updater
|
|
||||||
|
|
||||||
// Filter out undefined values
|
|
||||||
Object.keys(newFilters).forEach(id => {
|
|
||||||
const newFilter = newFilters[id]
|
|
||||||
const column = columns.find(d => d.id === id)
|
|
||||||
const autoRemove = getFilterMethod(
|
|
||||||
column.filter,
|
|
||||||
userFilterTypes || {},
|
|
||||||
filterTypes
|
|
||||||
)
|
|
||||||
|
|
||||||
if (shouldAutoRemove(autoRemove, newFilter)) {
|
|
||||||
delete newFilters[id]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
...old,
|
|
||||||
filters: newFilters,
|
|
||||||
}
|
|
||||||
}, actions.setAllFilters)
|
|
||||||
}
|
|
||||||
|
|
||||||
hooks.columns.push(columns => {
|
|
||||||
columns.forEach(column => {
|
|
||||||
const { id, accessor, canFilter } = column
|
|
||||||
|
|
||||||
// Determine if a column is filterable
|
|
||||||
column.canFilter = accessor
|
|
||||||
? getFirstDefined(
|
|
||||||
canFilter,
|
|
||||||
disableFilters === true ? false : undefined,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
: false
|
|
||||||
|
|
||||||
// Provide the column a way of updating the filter value
|
|
||||||
column.setFilter = val => setFilter(column.id, val)
|
|
||||||
|
|
||||||
// Provide the current filter value to the column for
|
|
||||||
// convenience
|
|
||||||
column.filterValue = filters[id]
|
|
||||||
})
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: Create a filter cache for incremental high speed multi-filtering
|
|
||||||
// This gets pretty complicated pretty fast, since you have to maintain a
|
|
||||||
// cache for each row group (top-level rows, and each row's recursive subrows)
|
|
||||||
// This would make multi-filtering a lot faster though. Too far?
|
|
||||||
|
|
||||||
const filteredRows = React.useMemo(() => {
|
|
||||||
if (manualFilters || !Object.keys(filters).length) {
|
|
||||||
return rows
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debug) console.info('getFilteredRows')
|
|
||||||
|
|
||||||
// Filters top level and nested rows
|
|
||||||
const filterRows = rows => {
|
|
||||||
let filteredRows = rows
|
|
||||||
|
|
||||||
filteredRows = Object.entries(filters).reduce(
|
|
||||||
(filteredSoFar, [columnID, filterValue]) => {
|
|
||||||
// Find the filters column
|
|
||||||
const column = columns.find(d => d.id === columnID)
|
|
||||||
|
|
||||||
column.preFilteredRows = filteredSoFar
|
|
||||||
|
|
||||||
// Don't filter hidden columns or columns that have had their filters disabled
|
|
||||||
if (!column || column.filterable === false) {
|
|
||||||
return filteredSoFar
|
|
||||||
}
|
|
||||||
|
|
||||||
const columnFilter = column.filter || filterTypes.text
|
|
||||||
|
|
||||||
// Look up filter functions in this order:
|
|
||||||
// column function
|
|
||||||
// column string lookup on user filters
|
|
||||||
// column string lookup on built-in filters
|
|
||||||
// default function
|
|
||||||
// default string lookup on user filters
|
|
||||||
// default string lookup on built-in filters
|
|
||||||
const filterMethod = getFilterMethod(
|
|
||||||
columnFilter,
|
|
||||||
userFilterTypes || {},
|
|
||||||
filterTypes
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!filterMethod) {
|
|
||||||
console.warn(
|
|
||||||
`Could not find a valid 'column.filter' for column with the ID: ${
|
|
||||||
column.id
|
|
||||||
}.`
|
|
||||||
)
|
|
||||||
return filteredSoFar
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass the rows, id, filterValue and column to the filterMethod
|
|
||||||
// to get the filtered rows back
|
|
||||||
return filterMethod(filteredSoFar, columnID, filterValue, column)
|
|
||||||
},
|
|
||||||
rows
|
|
||||||
)
|
|
||||||
|
|
||||||
// Apply the filter to any subRows
|
|
||||||
// We technically could do this recursively in the above loop,
|
|
||||||
// but that would severely hinder the API for the user, since they
|
|
||||||
// would be required to do that recursion in some scenarios
|
|
||||||
filteredRows = filteredRows.map(row => {
|
|
||||||
if (!row.subRows) {
|
|
||||||
return row
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...row,
|
|
||||||
subRows: filterRows(row.subRows),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// then filter any rows without subcolumns because it would be strange to show
|
|
||||||
filteredRows = filteredRows.filter(row => {
|
|
||||||
if (!row.subRows) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return row.subRows.length > 0
|
|
||||||
})
|
|
||||||
|
|
||||||
return filteredRows
|
|
||||||
}
|
|
||||||
|
|
||||||
return filterRows(rows)
|
|
||||||
}, [manualFilters, filters, debug, rows, columns, userFilterTypes])
|
|
||||||
|
|
||||||
return {
|
|
||||||
...props,
|
|
||||||
setFilter,
|
|
||||||
setAllFilters,
|
|
||||||
preFilteredRows: rows,
|
|
||||||
rows: filteredRows,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldAutoRemove(autoRemove, value) {
|
|
||||||
return autoRemove ? autoRemove(value) : typeof value === 'undefined'
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFilterMethod(filter, userFilterTypes, filterTypes) {
|
|
||||||
return isFunction(filter) || userFilterTypes[filter] || filterTypes[filter]
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
const reSplitAlphaNumeric = /([0-9]+)/gm
|
|
||||||
|
|
||||||
// Mixed sorting is slow, but very inclusive of many edge cases.
|
|
||||||
// It handles numbers, mixed alphanumeric combinations, and even
|
|
||||||
// null, undefined, and Infinity
|
|
||||||
export const alphanumeric = (a, b) => {
|
|
||||||
// Force to strings (or "" for unsupported types)
|
|
||||||
a = toString(a)
|
|
||||||
b = toString(b)
|
|
||||||
|
|
||||||
// Split on number groups, but keep the delimiter
|
|
||||||
// Then remove falsey split values
|
|
||||||
a = a.split(reSplitAlphaNumeric).filter(Boolean)
|
|
||||||
b = b.split(reSplitAlphaNumeric).filter(Boolean)
|
|
||||||
|
|
||||||
// While
|
|
||||||
while (a.length && b.length) {
|
|
||||||
let aa = a.shift()
|
|
||||||
let bb = b.shift()
|
|
||||||
|
|
||||||
const an = parseInt(aa, 10)
|
|
||||||
const bn = parseInt(bb, 10)
|
|
||||||
|
|
||||||
const combo = [an, bn].sort()
|
|
||||||
|
|
||||||
// Both are string
|
|
||||||
if (isNaN(combo[0])) {
|
|
||||||
if (aa > bb) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if (bb > aa) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// One is a string, one is a number
|
|
||||||
if (isNaN(combo[1])) {
|
|
||||||
return isNaN(an) ? -1 : 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both are numbers
|
|
||||||
if (an > bn) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if (bn > an) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.length - b.length
|
|
||||||
}
|
|
||||||
|
|
||||||
export function datetime(a, b) {
|
|
||||||
a = a.getTime()
|
|
||||||
b = b.getTime()
|
|
||||||
return numeric(a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function numeric(a, b) {
|
|
||||||
return a === b ? 0 : a > b ? 1 : -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utils
|
|
||||||
|
|
||||||
function toString(a) {
|
|
||||||
if (typeof a === 'number') {
|
|
||||||
if (isNaN(a) || a === Infinity || a === -Infinity) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
return String(a)
|
|
||||||
}
|
|
||||||
if (typeof a === 'string') {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
+40
-17
@@ -28,6 +28,25 @@ export function defaultOrderByFn(arr, funcs, dirs) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function defaultSortByFn(a, b, desc) {
|
||||||
|
// force null and undefined to the bottom
|
||||||
|
a = a === null || a === undefined ? '' : a
|
||||||
|
b = b === null || b === undefined ? '' : b
|
||||||
|
// force any string values to lowercase
|
||||||
|
a = typeof a === 'string' ? a.toLowerCase() : a
|
||||||
|
b = typeof b === 'string' ? b.toLowerCase() : b
|
||||||
|
// Return either 1 or -1 to indicate a sort priority
|
||||||
|
if (a > b) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if (a < b) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
// returning 0, undefined or any falsey value will defer to the next
|
||||||
|
// sorting mechanism or eventually the columns index via the orderByFn
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
export function getFirstDefined(...args) {
|
export function getFirstDefined(...args) {
|
||||||
for (let i = 0; i < args.length; i += 1) {
|
for (let i = 0; i < args.length; i += 1) {
|
||||||
if (typeof args[i] !== 'undefined') {
|
if (typeof args[i] !== 'undefined') {
|
||||||
@@ -48,6 +67,14 @@ export function defaultGroupByFn(rows, grouper) {
|
|||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function defaultFilterFn(row, id, value, column) {
|
||||||
|
return row.values[id] !== undefined
|
||||||
|
? String(row.values[id])
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(String(value).toLowerCase())
|
||||||
|
: true
|
||||||
|
}
|
||||||
|
|
||||||
export function setBy(obj = {}, path, value) {
|
export function setBy(obj = {}, path, value) {
|
||||||
const recurse = (obj, depth = 0) => {
|
const recurse = (obj, depth = 0) => {
|
||||||
const key = path[depth]
|
const key = path[depth]
|
||||||
@@ -56,7 +83,7 @@ export function setBy(obj = {}, path, value) {
|
|||||||
depth === path.length - 1 ? value : recurse(target, depth + 1)
|
depth === path.length - 1 ? value : recurse(target, depth + 1)
|
||||||
return {
|
return {
|
||||||
...obj,
|
...obj,
|
||||||
[key]: subValue,
|
[key]: subValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,11 +95,11 @@ export function getElementDimensions(element) {
|
|||||||
const style = window.getComputedStyle(element)
|
const style = window.getComputedStyle(element)
|
||||||
const margins = {
|
const margins = {
|
||||||
left: parseInt(style.marginLeft),
|
left: parseInt(style.marginLeft),
|
||||||
right: parseInt(style.marginRight),
|
right: parseInt(style.marginRight)
|
||||||
}
|
}
|
||||||
const padding = {
|
const padding = {
|
||||||
left: parseInt(style.paddingLeft),
|
left: parseInt(style.paddingLeft),
|
||||||
right: parseInt(style.paddingRight),
|
right: parseInt(style.paddingRight)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
left: Math.ceil(rect.left),
|
left: Math.ceil(rect.left),
|
||||||
@@ -84,13 +111,17 @@ export function getElementDimensions(element) {
|
|||||||
marginRight: margins.right,
|
marginRight: margins.right,
|
||||||
paddingLeft: padding.left,
|
paddingLeft: padding.left,
|
||||||
paddingRight: padding.right,
|
paddingRight: padding.right,
|
||||||
scrollWidth: element.scrollWidth,
|
scrollWidth: element.scrollWidth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flexRender(Comp, props) {
|
export function flexRender(Comp, props) {
|
||||||
if (typeof Comp === 'function' || typeof Comp === 'object') {
|
if (typeof Comp === 'function') {
|
||||||
return <Comp {...props} />
|
return Object.getPrototypeOf(Comp).isReactComponent ? (
|
||||||
|
<Comp {...props} />
|
||||||
|
) : (
|
||||||
|
Comp(props)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return Comp
|
return Comp
|
||||||
}
|
}
|
||||||
@@ -103,9 +134,9 @@ export const mergeProps = (...groups) => {
|
|||||||
...rest,
|
...rest,
|
||||||
style: {
|
style: {
|
||||||
...(props.style || {}),
|
...(props.style || {}),
|
||||||
...style,
|
...style
|
||||||
},
|
},
|
||||||
className: [props.className, className].filter(Boolean).join(' '),
|
className: [props.className, className].filter(Boolean).join(' ')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return props
|
return props
|
||||||
@@ -121,7 +152,7 @@ export const warnUnknownProps = props => {
|
|||||||
if (Object.keys(props).length) {
|
if (Object.keys(props).length) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unknown options passed to useReactTable:
|
`Unknown options passed to useReactTable:
|
||||||
|
|
||||||
${JSON.stringify(props, null, 2)}`
|
${JSON.stringify(props, null, 2)}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -131,14 +162,6 @@ export function sum(arr) {
|
|||||||
return arr.reduce((prev, curr) => prev + curr, 0)
|
return arr.reduce((prev, curr) => prev + curr, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isFunction(a) {
|
|
||||||
if (typeof a === 'function') {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
function makePathArray(obj) {
|
function makePathArray(obj) {
|
||||||
return flattenDeep(obj)
|
return flattenDeep(obj)
|
||||||
.join('.')
|
.join('.')
|
||||||
|
|||||||
Reference in New Issue
Block a user