planning
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s

This commit is contained in:
2024-10-14 09:15:30 +02:00
parent bcba00a730
commit 6e64e138e2
21059 changed files with 2317811 additions and 1 deletions

21
node_modules/react-window/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Brian Vaughn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

171
node_modules/react-window/README.md generated vendored Normal file
View File

@@ -0,0 +1,171 @@
# react-window
> React components for efficiently rendering large lists and tabular data
### If you like this project, 🎉 [become a sponsor](https://github.com/sponsors/bvaughn/) or ☕ [buy me a coffee](http://givebrian.coffee/)
---
React window works by only rendering *part* of a large data set (just enough to fill the viewport). This helps address some common performance bottlenecks:
1. It reduces the amount of work (and time) required to render the initial view and to process updates.
2. It reduces the memory footprint by avoiding over-allocation of DOM nodes.
### Sponsors
The following wonderful companies have sponsored react-window:
<a href="https://opencollective.com/react-window/sponsor/0/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/1/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/2/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/3/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/4/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/5/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/6/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/7/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/8/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/9/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/10/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/11/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/12/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/13/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/14/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/15/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/16/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/17/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/18/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/19/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/20/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/21/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/22/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/23/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/24/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/25/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/26/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/27/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/28/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/react-window/sponsor/29/website" target="_blank"><img src="https://opencollective.com/react-window/sponsor/29/avatar.svg"></a>
[Learn more about becoming a sponsor!](https://opencollective.com/react-window#sponsor)
<a href="https://opencollective.com/react-window/backer/0/website" target="_blank"><img src="https://opencollective.com/react-window/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/1/website" target="_blank"><img src="https://opencollective.com/react-window/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/2/website" target="_blank"><img src="https://opencollective.com/react-window/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/3/website" target="_blank"><img src="https://opencollective.com/react-window/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/4/website" target="_blank"><img src="https://opencollective.com/react-window/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/5/website" target="_blank"><img src="https://opencollective.com/react-window/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/6/website" target="_blank"><img src="https://opencollective.com/react-window/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/7/website" target="_blank"><img src="https://opencollective.com/react-window/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/8/website" target="_blank"><img src="https://opencollective.com/react-window/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/9/website" target="_blank"><img src="https://opencollective.com/react-window/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/10/website" target="_blank"><img src="https://opencollective.com/react-window/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/11/website" target="_blank"><img src="https://opencollective.com/react-window/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/12/website" target="_blank"><img src="https://opencollective.com/react-window/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/13/website" target="_blank"><img src="https://opencollective.com/react-window/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/14/website" target="_blank"><img src="https://opencollective.com/react-window/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/15/website" target="_blank"><img src="https://opencollective.com/react-window/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/16/website" target="_blank"><img src="https://opencollective.com/react-window/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/17/website" target="_blank"><img src="https://opencollective.com/react-window/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/18/website" target="_blank"><img src="https://opencollective.com/react-window/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/19/website" target="_blank"><img src="https://opencollective.com/react-window/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/20/website" target="_blank"><img src="https://opencollective.com/react-window/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/21/website" target="_blank"><img src="https://opencollective.com/react-window/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/22/website" target="_blank"><img src="https://opencollective.com/react-window/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/23/website" target="_blank"><img src="https://opencollective.com/react-window/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/24/website" target="_blank"><img src="https://opencollective.com/react-window/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/25/website" target="_blank"><img src="https://opencollective.com/react-window/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/26/website" target="_blank"><img src="https://opencollective.com/react-window/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/27/website" target="_blank"><img src="https://opencollective.com/react-window/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/28/website" target="_blank"><img src="https://opencollective.com/react-window/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/react-window/backer/29/website" target="_blank"><img src="https://opencollective.com/react-window/backer/29/avatar.svg"></a>
## Install
```bash
# Yarn
yarn add react-window
# NPM
npm install --save react-window
```
## Usage
Learn more at [react-window.now.sh](https://react-window.now.sh/):
## Related libraries
* [`react-virtualized-auto-sizer`](https://npmjs.com/package/react-virtualized-auto-sizer): HOC that grows to fit all of the available space and passes the width and height values to its child.
* [`react-window-infinite-loader`](https://npmjs.com/package/react-window-infinite-loader): Helps break large data sets down into chunks that can be just-in-time loaded as they are scrolled into view. It can also be used to create infinite loading lists (e.g. Facebook or Twitter).
* [`react-vtree`](https://www.npmjs.com/package/react-vtree): Lightweight and flexible solution to render large tree structures (e.g., file system).
## Frequently asked questions
### How is `react-window` different from `react-virtualized`?
I wrote `react-virtualized` several years ago. At the time, I was new to both React and the concept of windowing. Because of this, I made a few API decisions that I later came to regret. One of these was adding too many non-essential features and components. Once you add something to an open source project, removing it is pretty painful for users.
`react-window` is a complete rewrite of `react-virtualized`. I didn't try to solve as many problems or support as many use cases. Instead I focused on making the package **smaller**<sup>1</sup> and **faster**. I also put a lot of thought into making the API (and documentation) as beginner-friendly as possible (with the caveat that windowing is still kind of an advanced use case).
If `react-window` provides the functionality your project needs, I would strongly recommend using it instead of `react-virtualized`. However if you need features that only `react-virtualized` provides, you have two options:
1. Use `react-virtualized`. (It's still widely used by a lot of successful projects!)
2. Create a component that decorates one of the `react-window` primitives and adds the functionality you need. You may even want to release this component to NPM (as its own, standalone package)! 🙂
<sup>1 - Adding a `react-virtualized` list to a CRA project increases the (gzipped) build size by ~33.5 KB. Adding a `react-window` list to a CRA project increases the (gzipped) build size by &lt;2 KB.</sup>
### Can a list or a grid fill 100% the width or height of a page?
Yes. I recommend using the [`react-virtualized-auto-sizer` package](https://npmjs.com/package/react-virtualized-auto-sizer):
<img width="336" alt="screen shot 2019-03-07 at 7 29 08 pm" src="https://user-images.githubusercontent.com/29597/54005716-50f41880-410f-11e9-864f-a65bbdf49e07.png">
Here's a [Code Sandbox demo](https://codesandbox.io/s/3vnx878jk5).
### Why is my list blank when I scroll?
If your list looks something like this...
<img src="https://user-images.githubusercontent.com/29597/54005352-eb535c80-410d-11e9-80b2-d3d02db1f599.gif" width="302" height="152">
...then you probably forgot to use the `style` parameter! Libraries like react-window work by absolutely positioning the list items (via an inline style), so don't forget to attach it to the DOM element you render!
<img width="257" alt="screen shot 2019-03-07 at 7 21 48 pm" src="https://user-images.githubusercontent.com/29597/54005433-45ecb880-410e-11e9-8721-420ff1a153da.png">
### Can I lazy load data for my list?
Yes. I recommend using the [`react-window-infinite-loader` package](https://npmjs.com/package/react-window-infinite-loader):
<img width="368" alt="screen shot 2019-03-07 at 7 32 32 pm" src="https://user-images.githubusercontent.com/29597/54006733-653a1480-4113-11e9-907b-08ca5e27b3f9.png">
Here's a [Code Sandbox demo](https://codesandbox.io/s/5wqo7z2np4).
### Can I attach custom properties or event handlers?
Yes, using the `outerElementType` prop.
<img width="412" alt="Screen Shot 2019-03-12 at 8 58 09 AM" src="https://user-images.githubusercontent.com/29597/54215333-f9ee9a80-44a4-11e9-9142-34c67026d950.png">
Here's a [Code Sandbox demo](https://codesandbox.io/s/4zqx79nww0).
### Can I add padding to the top and bottom of a list?
Yes, although it requires a bit of inline styling.
<img width="418" alt="Screen Shot 2019-06-02 at 8 38 18 PM" src="https://user-images.githubusercontent.com/29597/58774454-65ad4480-8576-11e9-8889-07044fd41393.png">
Here's a [Code Sandbox demo](https://codesandbox.io/s/react-window-list-padding-dg0pq).
### Can I add gutter or padding between items?
Yes, although it requires a bit of inline styling.
<img width="416" alt="Screen Shot 2019-03-26 at 6 33 56 PM" src="https://user-images.githubusercontent.com/29597/55043972-c14ad700-4ff5-11e9-9caa-2e9f4d85f96c.png">
Here's a [Code Sandbox demo](https://codesandbox.io/s/2w8wmlm89p).
### Does this library support "sticky" items?
Yes, although it requires a small amount of user code. Here's a [Code Sandbox demo](https://codesandbox.io/s/0mk3qwpl4l).
## License
MIT © [bvaughn](https://github.com/bvaughn)

2
node_modules/react-window/dist/index-dev.umd.js generated vendored Normal file

File diff suppressed because one or more lines are too long

1
node_modules/react-window/dist/index-dev.umd.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

2
node_modules/react-window/dist/index-prod.umd.js generated vendored Normal file

File diff suppressed because one or more lines are too long

1
node_modules/react-window/dist/index-prod.umd.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

2087
node_modules/react-window/dist/index.cjs.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

3
node_modules/react-window/dist/index.cjs.js.flow generated vendored Normal file
View File

@@ -0,0 +1,3 @@
// @flow
export * from '../src';

1
node_modules/react-window/dist/index.cjs.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

2076
node_modules/react-window/dist/index.esm.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

3
node_modules/react-window/dist/index.esm.js.flow generated vendored Normal file
View File

@@ -0,0 +1,3 @@
// @flow
export * from '../src';

1
node_modules/react-window/dist/index.esm.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

114
node_modules/react-window/package.json generated vendored Normal file
View File

@@ -0,0 +1,114 @@
{
"name": "react-window",
"version": "1.8.10",
"description": "React components for efficiently rendering large, scrollable lists and tabular data",
"author": "Brian Vaughn <brian.david.vaughn@gmail.com> (https://github.com/bvaughn/)",
"contributors": [
"Brian Vaughn <brian.david.vaughn@gmail.com> (https://github.com/bvaughn/)"
],
"license": "MIT",
"homepage": "http://react-window.now.sh/",
"repository": {
"type": "git",
"url": "https://github.com/bvaughn/react-window.git"
},
"bugs": {
"url": "https://github.com/bvaughn/react-window/issues"
},
"engines": {
"node": ">8.0.0"
},
"keywords": [
"react",
"reactjs",
"virtual",
"window",
"windowed",
"list",
"scrolling",
"infinite",
"virtualized",
"table",
"grid",
"spreadsheet"
],
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"files": [
"dist",
"src/*.js"
],
"sideEffects": false,
"scripts": {
"flow": "flow check --max-warnings=0 src && flow check website",
"precommit": "lint-staged",
"prettier": "prettier --write '**/*.{js,json,css}'",
"linc": "lint-staged",
"lint": "eslint '**/*.js'",
"test": "cross-env CI=1 react-scripts test --env=jsdom",
"test:watch": "react-scripts test --env=jsdom",
"build:flow": "cp flow-template dist/index.cjs.js.flow && cp flow-template dist/index.esm.js.flow",
"build:source": "rollup -c",
"build": "del dist && mkdir dist && yarn build:flow && yarn build:source",
"start": "rollup -c -w",
"prepare": "yarn run build",
"website:build": "cd website && yarn build",
"website:deploy": "cd website && yarn deploy",
"website:run": "cd website && yarn start"
},
"lint-staged": {
"{website,src}/**/*.{js,json,css}": [
"prettier --write",
"git add"
],
"**/*.js": "eslint --max-warnings 0"
},
"dependencies": {
"@babel/runtime": "^7.0.0",
"memoize-one": ">=3.1.1 <6"
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^9.0.0",
"babel-plugin-annotate-pure-calls": "^0.3.0",
"cross-env": "^5.1.4",
"del-cli": "^1.1.0",
"eslint": "^4.19.1",
"eslint-config-prettier": "^2.9.0",
"eslint-config-react-app": "^2.1.0",
"eslint-config-standard": "^11.0.0",
"eslint-config-standard-react": "^6.0.0",
"eslint-plugin-flowtype": "^2.47.1",
"eslint-plugin-import": "^2.11.0",
"eslint-plugin-jsx-a11y": "^5",
"eslint-plugin-node": "^6.0.1",
"eslint-plugin-prettier": "^2.6.0",
"eslint-plugin-promise": "^3.7.0",
"eslint-plugin-react": "^7.7.0",
"eslint-plugin-standard": "^3.0.1",
"flow-bin": "^0.111.0",
"gh-pages": "^1.1.0",
"lint-staged": "^7.0.5",
"prettier": "^1.12.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-is": "^17.0.1",
"react-scripts": "^1.1.1",
"react-test-renderer": "^17.0.1",
"rollup": "^1.4.1",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^9.2.1",
"rollup-plugin-node-resolve": "^4.0.1",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-terser": "^5.1.0"
}
}

244
node_modules/react-window/src/FixedSizeGrid.js generated vendored Normal file
View File

@@ -0,0 +1,244 @@
// @flow
import createGridComponent from './createGridComponent';
import type { Props, ScrollToAlign } from './createGridComponent';
const FixedSizeGrid = createGridComponent({
getColumnOffset: ({ columnWidth }: Props<any>, index: number): number =>
index * ((columnWidth: any): number),
getColumnWidth: ({ columnWidth }: Props<any>, index: number): number =>
((columnWidth: any): number),
getRowOffset: ({ rowHeight }: Props<any>, index: number): number =>
index * ((rowHeight: any): number),
getRowHeight: ({ rowHeight }: Props<any>, index: number): number =>
((rowHeight: any): number),
getEstimatedTotalHeight: ({ rowCount, rowHeight }: Props<any>) =>
((rowHeight: any): number) * rowCount,
getEstimatedTotalWidth: ({ columnCount, columnWidth }: Props<any>) =>
((columnWidth: any): number) * columnCount,
getOffsetForColumnAndAlignment: (
{ columnCount, columnWidth, width }: Props<any>,
columnIndex: number,
align: ScrollToAlign,
scrollLeft: number,
instanceProps: typeof undefined,
scrollbarSize: number
): number => {
const lastColumnOffset = Math.max(
0,
columnCount * ((columnWidth: any): number) - width
);
const maxOffset = Math.min(
lastColumnOffset,
columnIndex * ((columnWidth: any): number)
);
const minOffset = Math.max(
0,
columnIndex * ((columnWidth: any): number) -
width +
scrollbarSize +
((columnWidth: any): number)
);
if (align === 'smart') {
if (scrollLeft >= minOffset - width && scrollLeft <= maxOffset + width) {
align = 'auto';
} else {
align = 'center';
}
}
switch (align) {
case 'start':
return maxOffset;
case 'end':
return minOffset;
case 'center':
// "Centered" offset is usually the average of the min and max.
// But near the edges of the list, this doesn't hold true.
const middleOffset = Math.round(
minOffset + (maxOffset - minOffset) / 2
);
if (middleOffset < Math.ceil(width / 2)) {
return 0; // near the beginning
} else if (middleOffset > lastColumnOffset + Math.floor(width / 2)) {
return lastColumnOffset; // near the end
} else {
return middleOffset;
}
case 'auto':
default:
if (scrollLeft >= minOffset && scrollLeft <= maxOffset) {
return scrollLeft;
} else if (minOffset > maxOffset) {
// Because we only take into account the scrollbar size when calculating minOffset
// this value can be larger than maxOffset when at the end of the list
return minOffset;
} else if (scrollLeft < minOffset) {
return minOffset;
} else {
return maxOffset;
}
}
},
getOffsetForRowAndAlignment: (
{ rowHeight, height, rowCount }: Props<any>,
rowIndex: number,
align: ScrollToAlign,
scrollTop: number,
instanceProps: typeof undefined,
scrollbarSize: number
): number => {
const lastRowOffset = Math.max(
0,
rowCount * ((rowHeight: any): number) - height
);
const maxOffset = Math.min(
lastRowOffset,
rowIndex * ((rowHeight: any): number)
);
const minOffset = Math.max(
0,
rowIndex * ((rowHeight: any): number) -
height +
scrollbarSize +
((rowHeight: any): number)
);
if (align === 'smart') {
if (scrollTop >= minOffset - height && scrollTop <= maxOffset + height) {
align = 'auto';
} else {
align = 'center';
}
}
switch (align) {
case 'start':
return maxOffset;
case 'end':
return minOffset;
case 'center':
// "Centered" offset is usually the average of the min and max.
// But near the edges of the list, this doesn't hold true.
const middleOffset = Math.round(
minOffset + (maxOffset - minOffset) / 2
);
if (middleOffset < Math.ceil(height / 2)) {
return 0; // near the beginning
} else if (middleOffset > lastRowOffset + Math.floor(height / 2)) {
return lastRowOffset; // near the end
} else {
return middleOffset;
}
case 'auto':
default:
if (scrollTop >= minOffset && scrollTop <= maxOffset) {
return scrollTop;
} else if (minOffset > maxOffset) {
// Because we only take into account the scrollbar size when calculating minOffset
// this value can be larger than maxOffset when at the end of the list
return minOffset;
} else if (scrollTop < minOffset) {
return minOffset;
} else {
return maxOffset;
}
}
},
getColumnStartIndexForOffset: (
{ columnWidth, columnCount }: Props<any>,
scrollLeft: number
): number =>
Math.max(
0,
Math.min(
columnCount - 1,
Math.floor(scrollLeft / ((columnWidth: any): number))
)
),
getColumnStopIndexForStartIndex: (
{ columnWidth, columnCount, width }: Props<any>,
startIndex: number,
scrollLeft: number
): number => {
const left = startIndex * ((columnWidth: any): number);
const numVisibleColumns = Math.ceil(
(width + scrollLeft - left) / ((columnWidth: any): number)
);
return Math.max(
0,
Math.min(
columnCount - 1,
startIndex + numVisibleColumns - 1 // -1 is because stop index is inclusive
)
);
},
getRowStartIndexForOffset: (
{ rowHeight, rowCount }: Props<any>,
scrollTop: number
): number =>
Math.max(
0,
Math.min(rowCount - 1, Math.floor(scrollTop / ((rowHeight: any): number)))
),
getRowStopIndexForStartIndex: (
{ rowHeight, rowCount, height }: Props<any>,
startIndex: number,
scrollTop: number
): number => {
const top = startIndex * ((rowHeight: any): number);
const numVisibleRows = Math.ceil(
(height + scrollTop - top) / ((rowHeight: any): number)
);
return Math.max(
0,
Math.min(
rowCount - 1,
startIndex + numVisibleRows - 1 // -1 is because stop index is inclusive
)
);
},
initInstanceProps(props: Props<any>): any {
// Noop
},
shouldResetStyleCacheOnItemSizeChange: true,
validateProps: ({ columnWidth, rowHeight }: Props<any>): void => {
if (process.env.NODE_ENV !== 'production') {
if (typeof columnWidth !== 'number') {
throw Error(
'An invalid "columnWidth" prop has been specified. ' +
'Value should be a number. ' +
`"${
columnWidth === null ? 'null' : typeof columnWidth
}" was specified.`
);
}
if (typeof rowHeight !== 'number') {
throw Error(
'An invalid "rowHeight" prop has been specified. ' +
'Value should be a number. ' +
`"${rowHeight === null ? 'null' : typeof rowHeight}" was specified.`
);
}
}
},
});
export default FixedSizeGrid;

137
node_modules/react-window/src/FixedSizeList.js generated vendored Normal file
View File

@@ -0,0 +1,137 @@
// @flow
import createListComponent from './createListComponent';
import type { Props, ScrollToAlign } from './createListComponent';
type InstanceProps = any;
const FixedSizeList = createListComponent({
getItemOffset: ({ itemSize }: Props<any>, index: number): number =>
index * ((itemSize: any): number),
getItemSize: ({ itemSize }: Props<any>, index: number): number =>
((itemSize: any): number),
getEstimatedTotalSize: ({ itemCount, itemSize }: Props<any>) =>
((itemSize: any): number) * itemCount,
getOffsetForIndexAndAlignment: (
{ direction, height, itemCount, itemSize, layout, width }: Props<any>,
index: number,
align: ScrollToAlign,
scrollOffset: number,
instanceProps: InstanceProps,
scrollbarSize: number
): number => {
// TODO Deprecate direction "horizontal"
const isHorizontal = direction === 'horizontal' || layout === 'horizontal';
const size = (((isHorizontal ? width : height): any): number);
const lastItemOffset = Math.max(
0,
itemCount * ((itemSize: any): number) - size
);
const maxOffset = Math.min(
lastItemOffset,
index * ((itemSize: any): number)
);
const minOffset = Math.max(
0,
index * ((itemSize: any): number) -
size +
((itemSize: any): number) +
scrollbarSize
);
if (align === 'smart') {
if (
scrollOffset >= minOffset - size &&
scrollOffset <= maxOffset + size
) {
align = 'auto';
} else {
align = 'center';
}
}
switch (align) {
case 'start':
return maxOffset;
case 'end':
return minOffset;
case 'center': {
// "Centered" offset is usually the average of the min and max.
// But near the edges of the list, this doesn't hold true.
const middleOffset = Math.round(
minOffset + (maxOffset - minOffset) / 2
);
if (middleOffset < Math.ceil(size / 2)) {
return 0; // near the beginning
} else if (middleOffset > lastItemOffset + Math.floor(size / 2)) {
return lastItemOffset; // near the end
} else {
return middleOffset;
}
}
case 'auto':
default:
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
return scrollOffset;
} else if (scrollOffset < minOffset) {
return minOffset;
} else {
return maxOffset;
}
}
},
getStartIndexForOffset: (
{ itemCount, itemSize }: Props<any>,
offset: number
): number =>
Math.max(
0,
Math.min(itemCount - 1, Math.floor(offset / ((itemSize: any): number)))
),
getStopIndexForStartIndex: (
{ direction, height, itemCount, itemSize, layout, width }: Props<any>,
startIndex: number,
scrollOffset: number
): number => {
// TODO Deprecate direction "horizontal"
const isHorizontal = direction === 'horizontal' || layout === 'horizontal';
const offset = startIndex * ((itemSize: any): number);
const size = (((isHorizontal ? width : height): any): number);
const numVisibleItems = Math.ceil(
(size + scrollOffset - offset) / ((itemSize: any): number)
);
return Math.max(
0,
Math.min(
itemCount - 1,
startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive
)
);
},
initInstanceProps(props: Props<any>): any {
// Noop
},
shouldResetStyleCacheOnItemSizeChange: true,
validateProps: ({ itemSize }: Props<any>): void => {
if (process.env.NODE_ENV !== 'production') {
if (typeof itemSize !== 'number') {
throw Error(
'An invalid "itemSize" prop has been specified. ' +
'Value should be a number. ' +
`"${itemSize === null ? 'null' : typeof itemSize}" was specified.`
);
}
}
},
});
export default FixedSizeList;

507
node_modules/react-window/src/VariableSizeGrid.js generated vendored Normal file
View File

@@ -0,0 +1,507 @@
// @flow
import createGridComponent from './createGridComponent';
import type { Props, ScrollToAlign } from './createGridComponent';
const DEFAULT_ESTIMATED_ITEM_SIZE = 50;
type VariableSizeProps = {|
estimatedColumnWidth: number,
estimatedRowHeight: number,
...Props<any>,
|};
type itemSizeGetter = (index: number) => number;
type ItemType = 'column' | 'row';
type ItemMetadata = {|
offset: number,
size: number,
|};
type ItemMetadataMap = { [index: number]: ItemMetadata };
type InstanceProps = {|
columnMetadataMap: ItemMetadataMap,
estimatedColumnWidth: number,
estimatedRowHeight: number,
lastMeasuredColumnIndex: number,
lastMeasuredRowIndex: number,
rowMetadataMap: ItemMetadataMap,
|};
const getEstimatedTotalHeight = (
{ rowCount }: Props<any>,
{ rowMetadataMap, estimatedRowHeight, lastMeasuredRowIndex }: InstanceProps
) => {
let totalSizeOfMeasuredRows = 0;
// Edge case check for when the number of items decreases while a scroll is in progress.
// https://github.com/bvaughn/react-window/pull/138
if (lastMeasuredRowIndex >= rowCount) {
lastMeasuredRowIndex = rowCount - 1;
}
if (lastMeasuredRowIndex >= 0) {
const itemMetadata = rowMetadataMap[lastMeasuredRowIndex];
totalSizeOfMeasuredRows = itemMetadata.offset + itemMetadata.size;
}
const numUnmeasuredItems = rowCount - lastMeasuredRowIndex - 1;
const totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedRowHeight;
return totalSizeOfMeasuredRows + totalSizeOfUnmeasuredItems;
};
const getEstimatedTotalWidth = (
{ columnCount }: Props<any>,
{
columnMetadataMap,
estimatedColumnWidth,
lastMeasuredColumnIndex,
}: InstanceProps
) => {
let totalSizeOfMeasuredRows = 0;
// Edge case check for when the number of items decreases while a scroll is in progress.
// https://github.com/bvaughn/react-window/pull/138
if (lastMeasuredColumnIndex >= columnCount) {
lastMeasuredColumnIndex = columnCount - 1;
}
if (lastMeasuredColumnIndex >= 0) {
const itemMetadata = columnMetadataMap[lastMeasuredColumnIndex];
totalSizeOfMeasuredRows = itemMetadata.offset + itemMetadata.size;
}
const numUnmeasuredItems = columnCount - lastMeasuredColumnIndex - 1;
const totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedColumnWidth;
return totalSizeOfMeasuredRows + totalSizeOfUnmeasuredItems;
};
const getItemMetadata = (
itemType: ItemType,
props: Props<any>,
index: number,
instanceProps: InstanceProps
): ItemMetadata => {
let itemMetadataMap, itemSize, lastMeasuredIndex;
if (itemType === 'column') {
itemMetadataMap = instanceProps.columnMetadataMap;
itemSize = ((props.columnWidth: any): itemSizeGetter);
lastMeasuredIndex = instanceProps.lastMeasuredColumnIndex;
} else {
itemMetadataMap = instanceProps.rowMetadataMap;
itemSize = ((props.rowHeight: any): itemSizeGetter);
lastMeasuredIndex = instanceProps.lastMeasuredRowIndex;
}
if (index > lastMeasuredIndex) {
let offset = 0;
if (lastMeasuredIndex >= 0) {
const itemMetadata = itemMetadataMap[lastMeasuredIndex];
offset = itemMetadata.offset + itemMetadata.size;
}
for (let i = lastMeasuredIndex + 1; i <= index; i++) {
let size = itemSize(i);
itemMetadataMap[i] = {
offset,
size,
};
offset += size;
}
if (itemType === 'column') {
instanceProps.lastMeasuredColumnIndex = index;
} else {
instanceProps.lastMeasuredRowIndex = index;
}
}
return itemMetadataMap[index];
};
const findNearestItem = (
itemType: ItemType,
props: Props<any>,
instanceProps: InstanceProps,
offset: number
) => {
let itemMetadataMap, lastMeasuredIndex;
if (itemType === 'column') {
itemMetadataMap = instanceProps.columnMetadataMap;
lastMeasuredIndex = instanceProps.lastMeasuredColumnIndex;
} else {
itemMetadataMap = instanceProps.rowMetadataMap;
lastMeasuredIndex = instanceProps.lastMeasuredRowIndex;
}
const lastMeasuredItemOffset =
lastMeasuredIndex > 0 ? itemMetadataMap[lastMeasuredIndex].offset : 0;
if (lastMeasuredItemOffset >= offset) {
// If we've already measured items within this range just use a binary search as it's faster.
return findNearestItemBinarySearch(
itemType,
props,
instanceProps,
lastMeasuredIndex,
0,
offset
);
} else {
// If we haven't yet measured this high, fallback to an exponential search with an inner binary search.
// The exponential search avoids pre-computing sizes for the full set of items as a binary search would.
// The overall complexity for this approach is O(log n).
return findNearestItemExponentialSearch(
itemType,
props,
instanceProps,
Math.max(0, lastMeasuredIndex),
offset
);
}
};
const findNearestItemBinarySearch = (
itemType: ItemType,
props: Props<any>,
instanceProps: InstanceProps,
high: number,
low: number,
offset: number
): number => {
while (low <= high) {
const middle = low + Math.floor((high - low) / 2);
const currentOffset = getItemMetadata(
itemType,
props,
middle,
instanceProps
).offset;
if (currentOffset === offset) {
return middle;
} else if (currentOffset < offset) {
low = middle + 1;
} else if (currentOffset > offset) {
high = middle - 1;
}
}
if (low > 0) {
return low - 1;
} else {
return 0;
}
};
const findNearestItemExponentialSearch = (
itemType: ItemType,
props: Props<any>,
instanceProps: InstanceProps,
index: number,
offset: number
): number => {
const itemCount = itemType === 'column' ? props.columnCount : props.rowCount;
let interval = 1;
while (
index < itemCount &&
getItemMetadata(itemType, props, index, instanceProps).offset < offset
) {
index += interval;
interval *= 2;
}
return findNearestItemBinarySearch(
itemType,
props,
instanceProps,
Math.min(index, itemCount - 1),
Math.floor(index / 2),
offset
);
};
const getOffsetForIndexAndAlignment = (
itemType: ItemType,
props: Props<any>,
index: number,
align: ScrollToAlign,
scrollOffset: number,
instanceProps: InstanceProps,
scrollbarSize: number
): number => {
const size = itemType === 'column' ? props.width : props.height;
const itemMetadata = getItemMetadata(itemType, props, index, instanceProps);
// Get estimated total size after ItemMetadata is computed,
// To ensure it reflects actual measurements instead of just estimates.
const estimatedTotalSize =
itemType === 'column'
? getEstimatedTotalWidth(props, instanceProps)
: getEstimatedTotalHeight(props, instanceProps);
const maxOffset = Math.max(
0,
Math.min(estimatedTotalSize - size, itemMetadata.offset)
);
const minOffset = Math.max(
0,
itemMetadata.offset - size + scrollbarSize + itemMetadata.size
);
if (align === 'smart') {
if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) {
align = 'auto';
} else {
align = 'center';
}
}
switch (align) {
case 'start':
return maxOffset;
case 'end':
return minOffset;
case 'center':
return Math.round(minOffset + (maxOffset - minOffset) / 2);
case 'auto':
default:
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
return scrollOffset;
} else if (minOffset > maxOffset) {
// Because we only take into account the scrollbar size when calculating minOffset
// this value can be larger than maxOffset when at the end of the list
return minOffset;
} else if (scrollOffset < minOffset) {
return minOffset;
} else {
return maxOffset;
}
}
};
const VariableSizeGrid = createGridComponent({
getColumnOffset: (
props: Props<any>,
index: number,
instanceProps: InstanceProps
): number => getItemMetadata('column', props, index, instanceProps).offset,
getColumnStartIndexForOffset: (
props: Props<any>,
scrollLeft: number,
instanceProps: InstanceProps
): number => findNearestItem('column', props, instanceProps, scrollLeft),
getColumnStopIndexForStartIndex: (
props: Props<any>,
startIndex: number,
scrollLeft: number,
instanceProps: InstanceProps
): number => {
const { columnCount, width } = props;
const itemMetadata = getItemMetadata(
'column',
props,
startIndex,
instanceProps
);
const maxOffset = scrollLeft + width;
let offset = itemMetadata.offset + itemMetadata.size;
let stopIndex = startIndex;
while (stopIndex < columnCount - 1 && offset < maxOffset) {
stopIndex++;
offset += getItemMetadata('column', props, stopIndex, instanceProps).size;
}
return stopIndex;
},
getColumnWidth: (
props: Props<any>,
index: number,
instanceProps: InstanceProps
): number => instanceProps.columnMetadataMap[index].size,
getEstimatedTotalHeight,
getEstimatedTotalWidth,
getOffsetForColumnAndAlignment: (
props: Props<any>,
index: number,
align: ScrollToAlign,
scrollOffset: number,
instanceProps: InstanceProps,
scrollbarSize: number
): number =>
getOffsetForIndexAndAlignment(
'column',
props,
index,
align,
scrollOffset,
instanceProps,
scrollbarSize
),
getOffsetForRowAndAlignment: (
props: Props<any>,
index: number,
align: ScrollToAlign,
scrollOffset: number,
instanceProps: InstanceProps,
scrollbarSize: number
): number =>
getOffsetForIndexAndAlignment(
'row',
props,
index,
align,
scrollOffset,
instanceProps,
scrollbarSize
),
getRowOffset: (
props: Props<any>,
index: number,
instanceProps: InstanceProps
): number => getItemMetadata('row', props, index, instanceProps).offset,
getRowHeight: (
props: Props<any>,
index: number,
instanceProps: InstanceProps
): number => instanceProps.rowMetadataMap[index].size,
getRowStartIndexForOffset: (
props: Props<any>,
scrollTop: number,
instanceProps: InstanceProps
): number => findNearestItem('row', props, instanceProps, scrollTop),
getRowStopIndexForStartIndex: (
props: Props<any>,
startIndex: number,
scrollTop: number,
instanceProps: InstanceProps
): number => {
const { rowCount, height } = props;
const itemMetadata = getItemMetadata(
'row',
props,
startIndex,
instanceProps
);
const maxOffset = scrollTop + height;
let offset = itemMetadata.offset + itemMetadata.size;
let stopIndex = startIndex;
while (stopIndex < rowCount - 1 && offset < maxOffset) {
stopIndex++;
offset += getItemMetadata('row', props, stopIndex, instanceProps).size;
}
return stopIndex;
},
initInstanceProps(props: Props<any>, instance: any): InstanceProps {
const {
estimatedColumnWidth,
estimatedRowHeight,
} = ((props: any): VariableSizeProps);
const instanceProps = {
columnMetadataMap: {},
estimatedColumnWidth: estimatedColumnWidth || DEFAULT_ESTIMATED_ITEM_SIZE,
estimatedRowHeight: estimatedRowHeight || DEFAULT_ESTIMATED_ITEM_SIZE,
lastMeasuredColumnIndex: -1,
lastMeasuredRowIndex: -1,
rowMetadataMap: {},
};
instance.resetAfterColumnIndex = (
columnIndex: number,
shouldForceUpdate?: boolean = true
) => {
instance.resetAfterIndices({ columnIndex, shouldForceUpdate });
};
instance.resetAfterRowIndex = (
rowIndex: number,
shouldForceUpdate?: boolean = true
) => {
instance.resetAfterIndices({ rowIndex, shouldForceUpdate });
};
instance.resetAfterIndices = ({
columnIndex,
rowIndex,
shouldForceUpdate = true,
}: {
columnIndex?: number,
rowIndex?: number,
shouldForceUpdate: boolean,
}) => {
if (typeof columnIndex === 'number') {
instanceProps.lastMeasuredColumnIndex = Math.min(
instanceProps.lastMeasuredColumnIndex,
columnIndex - 1
);
}
if (typeof rowIndex === 'number') {
instanceProps.lastMeasuredRowIndex = Math.min(
instanceProps.lastMeasuredRowIndex,
rowIndex - 1
);
}
// We could potentially optimize further by only evicting styles after this index,
// But since styles are only cached while scrolling is in progress-
// It seems an unnecessary optimization.
// It's unlikely that resetAfterIndex() will be called while a user is scrolling.
instance._getItemStyleCache(-1);
if (shouldForceUpdate) {
instance.forceUpdate();
}
};
return instanceProps;
},
shouldResetStyleCacheOnItemSizeChange: false,
validateProps: ({ columnWidth, rowHeight }: Props<any>): void => {
if (process.env.NODE_ENV !== 'production') {
if (typeof columnWidth !== 'function') {
throw Error(
'An invalid "columnWidth" prop has been specified. ' +
'Value should be a function. ' +
`"${
columnWidth === null ? 'null' : typeof columnWidth
}" was specified.`
);
} else if (typeof rowHeight !== 'function') {
throw Error(
'An invalid "rowHeight" prop has been specified. ' +
'Value should be a function. ' +
`"${rowHeight === null ? 'null' : typeof rowHeight}" was specified.`
);
}
}
},
});
export default VariableSizeGrid;

317
node_modules/react-window/src/VariableSizeList.js generated vendored Normal file
View File

@@ -0,0 +1,317 @@
// @flow
import createListComponent from './createListComponent';
import type { Props, ScrollToAlign } from './createListComponent';
const DEFAULT_ESTIMATED_ITEM_SIZE = 50;
type VariableSizeProps = {|
estimatedItemSize: number,
...Props<any>,
|};
type itemSizeGetter = (index: number) => number;
type ItemMetadata = {|
offset: number,
size: number,
|};
type InstanceProps = {|
itemMetadataMap: { [index: number]: ItemMetadata },
estimatedItemSize: number,
lastMeasuredIndex: number,
|};
const getItemMetadata = (
props: Props<any>,
index: number,
instanceProps: InstanceProps
): ItemMetadata => {
const { itemSize } = ((props: any): VariableSizeProps);
const { itemMetadataMap, lastMeasuredIndex } = instanceProps;
if (index > lastMeasuredIndex) {
let offset = 0;
if (lastMeasuredIndex >= 0) {
const itemMetadata = itemMetadataMap[lastMeasuredIndex];
offset = itemMetadata.offset + itemMetadata.size;
}
for (let i = lastMeasuredIndex + 1; i <= index; i++) {
let size = ((itemSize: any): itemSizeGetter)(i);
itemMetadataMap[i] = {
offset,
size,
};
offset += size;
}
instanceProps.lastMeasuredIndex = index;
}
return itemMetadataMap[index];
};
const findNearestItem = (
props: Props<any>,
instanceProps: InstanceProps,
offset: number
) => {
const { itemMetadataMap, lastMeasuredIndex } = instanceProps;
const lastMeasuredItemOffset =
lastMeasuredIndex > 0 ? itemMetadataMap[lastMeasuredIndex].offset : 0;
if (lastMeasuredItemOffset >= offset) {
// If we've already measured items within this range just use a binary search as it's faster.
return findNearestItemBinarySearch(
props,
instanceProps,
lastMeasuredIndex,
0,
offset
);
} else {
// If we haven't yet measured this high, fallback to an exponential search with an inner binary search.
// The exponential search avoids pre-computing sizes for the full set of items as a binary search would.
// The overall complexity for this approach is O(log n).
return findNearestItemExponentialSearch(
props,
instanceProps,
Math.max(0, lastMeasuredIndex),
offset
);
}
};
const findNearestItemBinarySearch = (
props: Props<any>,
instanceProps: InstanceProps,
high: number,
low: number,
offset: number
): number => {
while (low <= high) {
const middle = low + Math.floor((high - low) / 2);
const currentOffset = getItemMetadata(props, middle, instanceProps).offset;
if (currentOffset === offset) {
return middle;
} else if (currentOffset < offset) {
low = middle + 1;
} else if (currentOffset > offset) {
high = middle - 1;
}
}
if (low > 0) {
return low - 1;
} else {
return 0;
}
};
const findNearestItemExponentialSearch = (
props: Props<any>,
instanceProps: InstanceProps,
index: number,
offset: number
): number => {
const { itemCount } = props;
let interval = 1;
while (
index < itemCount &&
getItemMetadata(props, index, instanceProps).offset < offset
) {
index += interval;
interval *= 2;
}
return findNearestItemBinarySearch(
props,
instanceProps,
Math.min(index, itemCount - 1),
Math.floor(index / 2),
offset
);
};
const getEstimatedTotalSize = (
{ itemCount }: Props<any>,
{ itemMetadataMap, estimatedItemSize, lastMeasuredIndex }: InstanceProps
) => {
let totalSizeOfMeasuredItems = 0;
// Edge case check for when the number of items decreases while a scroll is in progress.
// https://github.com/bvaughn/react-window/pull/138
if (lastMeasuredIndex >= itemCount) {
lastMeasuredIndex = itemCount - 1;
}
if (lastMeasuredIndex >= 0) {
const itemMetadata = itemMetadataMap[lastMeasuredIndex];
totalSizeOfMeasuredItems = itemMetadata.offset + itemMetadata.size;
}
const numUnmeasuredItems = itemCount - lastMeasuredIndex - 1;
const totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedItemSize;
return totalSizeOfMeasuredItems + totalSizeOfUnmeasuredItems;
};
const VariableSizeList = createListComponent({
getItemOffset: (
props: Props<any>,
index: number,
instanceProps: InstanceProps
): number => getItemMetadata(props, index, instanceProps).offset,
getItemSize: (
props: Props<any>,
index: number,
instanceProps: InstanceProps
): number => instanceProps.itemMetadataMap[index].size,
getEstimatedTotalSize,
getOffsetForIndexAndAlignment: (
props: Props<any>,
index: number,
align: ScrollToAlign,
scrollOffset: number,
instanceProps: InstanceProps,
scrollbarSize: number
): number => {
const { direction, height, layout, width } = props;
// TODO Deprecate direction "horizontal"
const isHorizontal = direction === 'horizontal' || layout === 'horizontal';
const size = (((isHorizontal ? width : height): any): number);
const itemMetadata = getItemMetadata(props, index, instanceProps);
// Get estimated total size after ItemMetadata is computed,
// To ensure it reflects actual measurements instead of just estimates.
const estimatedTotalSize = getEstimatedTotalSize(props, instanceProps);
const maxOffset = Math.max(
0,
Math.min(estimatedTotalSize - size, itemMetadata.offset)
);
const minOffset = Math.max(
0,
itemMetadata.offset - size + itemMetadata.size + scrollbarSize
);
if (align === 'smart') {
if (
scrollOffset >= minOffset - size &&
scrollOffset <= maxOffset + size
) {
align = 'auto';
} else {
align = 'center';
}
}
switch (align) {
case 'start':
return maxOffset;
case 'end':
return minOffset;
case 'center':
return Math.round(minOffset + (maxOffset - minOffset) / 2);
case 'auto':
default:
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
return scrollOffset;
} else if (scrollOffset < minOffset) {
return minOffset;
} else {
return maxOffset;
}
}
},
getStartIndexForOffset: (
props: Props<any>,
offset: number,
instanceProps: InstanceProps
): number => findNearestItem(props, instanceProps, offset),
getStopIndexForStartIndex: (
props: Props<any>,
startIndex: number,
scrollOffset: number,
instanceProps: InstanceProps
): number => {
const { direction, height, itemCount, layout, width } = props;
// TODO Deprecate direction "horizontal"
const isHorizontal = direction === 'horizontal' || layout === 'horizontal';
const size = (((isHorizontal ? width : height): any): number);
const itemMetadata = getItemMetadata(props, startIndex, instanceProps);
const maxOffset = scrollOffset + size;
let offset = itemMetadata.offset + itemMetadata.size;
let stopIndex = startIndex;
while (stopIndex < itemCount - 1 && offset < maxOffset) {
stopIndex++;
offset += getItemMetadata(props, stopIndex, instanceProps).size;
}
return stopIndex;
},
initInstanceProps(props: Props<any>, instance: any): InstanceProps {
const { estimatedItemSize } = ((props: any): VariableSizeProps);
const instanceProps = {
itemMetadataMap: {},
estimatedItemSize: estimatedItemSize || DEFAULT_ESTIMATED_ITEM_SIZE,
lastMeasuredIndex: -1,
};
instance.resetAfterIndex = (
index: number,
shouldForceUpdate?: boolean = true
) => {
instanceProps.lastMeasuredIndex = Math.min(
instanceProps.lastMeasuredIndex,
index - 1
);
// We could potentially optimize further by only evicting styles after this index,
// But since styles are only cached while scrolling is in progress-
// It seems an unnecessary optimization.
// It's unlikely that resetAfterIndex() will be called while a user is scrolling.
instance._getItemStyleCache(-1);
if (shouldForceUpdate) {
instance.forceUpdate();
}
};
return instanceProps;
},
shouldResetStyleCacheOnItemSizeChange: false,
validateProps: ({ itemSize }: Props<any>): void => {
if (process.env.NODE_ENV !== 'production') {
if (typeof itemSize !== 'function') {
throw Error(
'An invalid "itemSize" prop has been specified. ' +
'Value should be a function. ' +
`"${itemSize === null ? 'null' : typeof itemSize}" was specified.`
);
}
}
},
});
export default VariableSizeList;

18
node_modules/react-window/src/areEqual.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
// @flow
import shallowDiffers from './shallowDiffers';
// Custom comparison function for React.memo().
// It knows to compare individual style props and ignore the wrapper object.
// See https://reactjs.org/docs/react-api.html#reactmemo
export default function areEqual(
prevProps: Object,
nextProps: Object
): boolean {
const { style: prevStyle, ...prevRest } = prevProps;
const { style: nextStyle, ...nextRest } = nextProps;
return (
!shallowDiffers(prevStyle, nextStyle) && !shallowDiffers(prevRest, nextRest)
);
}

919
node_modules/react-window/src/createGridComponent.js generated vendored Normal file
View File

@@ -0,0 +1,919 @@
// @flow
import memoizeOne from 'memoize-one';
import { createElement, PureComponent } from 'react';
import { cancelTimeout, requestTimeout } from './timer';
import { getScrollbarSize, getRTLOffsetType } from './domHelpers';
import type { TimeoutID } from './timer';
type Direction = 'ltr' | 'rtl';
export type ScrollToAlign = 'auto' | 'smart' | 'center' | 'start' | 'end';
type itemSize = number | ((index: number) => number);
type RenderComponentProps<T> = {|
columnIndex: number,
data: T,
isScrolling?: boolean,
rowIndex: number,
style: Object,
|};
export type RenderComponent<T> = React$ComponentType<
$Shape<RenderComponentProps<T>>
>;
type ScrollDirection = 'forward' | 'backward';
type OnItemsRenderedCallback = ({
overscanColumnStartIndex: number,
overscanColumnStopIndex: number,
overscanRowStartIndex: number,
overscanRowStopIndex: number,
visibleColumnStartIndex: number,
visibleColumnStopIndex: number,
visibleRowStartIndex: number,
visibleRowStopIndex: number,
}) => void;
type OnScrollCallback = ({
horizontalScrollDirection: ScrollDirection,
scrollLeft: number,
scrollTop: number,
scrollUpdateWasRequested: boolean,
verticalScrollDirection: ScrollDirection,
}) => void;
type ScrollEvent = SyntheticEvent<HTMLDivElement>;
type ItemStyleCache = { [key: string]: Object };
type OuterProps = {|
children: React$Node,
className: string | void,
onScroll: ScrollEvent => void,
style: {
[string]: mixed,
},
|};
type InnerProps = {|
children: React$Node,
style: {
[string]: mixed,
},
|};
export type Props<T> = {|
children: RenderComponent<T>,
className?: string,
columnCount: number,
columnWidth: itemSize,
direction: Direction,
height: number,
initialScrollLeft?: number,
initialScrollTop?: number,
innerRef?: any,
innerElementType?: string | React$AbstractComponent<InnerProps, any>,
innerTagName?: string, // deprecated
itemData: T,
itemKey?: (params: {|
columnIndex: number,
data: T,
rowIndex: number,
|}) => any,
onItemsRendered?: OnItemsRenderedCallback,
onScroll?: OnScrollCallback,
outerRef?: any,
outerElementType?: string | React$AbstractComponent<OuterProps, any>,
outerTagName?: string, // deprecated
overscanColumnCount?: number,
overscanColumnsCount?: number, // deprecated
overscanCount?: number, // deprecated
overscanRowCount?: number,
overscanRowsCount?: number, // deprecated
rowCount: number,
rowHeight: itemSize,
style?: Object,
useIsScrolling: boolean,
width: number,
|};
type State = {|
instance: any,
isScrolling: boolean,
horizontalScrollDirection: ScrollDirection,
scrollLeft: number,
scrollTop: number,
scrollUpdateWasRequested: boolean,
verticalScrollDirection: ScrollDirection,
|};
type getItemOffset = (
props: Props<any>,
index: number,
instanceProps: any
) => number;
type getItemSize = (
props: Props<any>,
index: number,
instanceProps: any
) => number;
type getEstimatedTotalSize = (props: Props<any>, instanceProps: any) => number;
type GetOffsetForItemAndAlignment = (
props: Props<any>,
index: number,
align: ScrollToAlign,
scrollOffset: number,
instanceProps: any,
scrollbarSize: number
) => number;
type GetStartIndexForOffset = (
props: Props<any>,
offset: number,
instanceProps: any
) => number;
type GetStopIndexForStartIndex = (
props: Props<any>,
startIndex: number,
scrollOffset: number,
instanceProps: any
) => number;
type InitInstanceProps = (props: Props<any>, instance: any) => any;
type ValidateProps = (props: Props<any>) => void;
const IS_SCROLLING_DEBOUNCE_INTERVAL = 150;
const defaultItemKey = ({ columnIndex, data, rowIndex }) =>
`${rowIndex}:${columnIndex}`;
// In DEV mode, this Set helps us only log a warning once per component instance.
// This avoids spamming the console every time a render happens.
let devWarningsOverscanCount = null;
let devWarningsOverscanRowsColumnsCount = null;
let devWarningsTagName = null;
if (process.env.NODE_ENV !== 'production') {
if (typeof window !== 'undefined' && typeof window.WeakSet !== 'undefined') {
devWarningsOverscanCount = new WeakSet();
devWarningsOverscanRowsColumnsCount = new WeakSet();
devWarningsTagName = new WeakSet();
}
}
export default function createGridComponent({
getColumnOffset,
getColumnStartIndexForOffset,
getColumnStopIndexForStartIndex,
getColumnWidth,
getEstimatedTotalHeight,
getEstimatedTotalWidth,
getOffsetForColumnAndAlignment,
getOffsetForRowAndAlignment,
getRowHeight,
getRowOffset,
getRowStartIndexForOffset,
getRowStopIndexForStartIndex,
initInstanceProps,
shouldResetStyleCacheOnItemSizeChange,
validateProps,
}: {|
getColumnOffset: getItemOffset,
getColumnStartIndexForOffset: GetStartIndexForOffset,
getColumnStopIndexForStartIndex: GetStopIndexForStartIndex,
getColumnWidth: getItemSize,
getEstimatedTotalHeight: getEstimatedTotalSize,
getEstimatedTotalWidth: getEstimatedTotalSize,
getOffsetForColumnAndAlignment: GetOffsetForItemAndAlignment,
getOffsetForRowAndAlignment: GetOffsetForItemAndAlignment,
getRowOffset: getItemOffset,
getRowHeight: getItemSize,
getRowStartIndexForOffset: GetStartIndexForOffset,
getRowStopIndexForStartIndex: GetStopIndexForStartIndex,
initInstanceProps: InitInstanceProps,
shouldResetStyleCacheOnItemSizeChange: boolean,
validateProps: ValidateProps,
|}) {
return class Grid<T> extends PureComponent<Props<T>, State> {
_instanceProps: any = initInstanceProps(this.props, this);
_resetIsScrollingTimeoutId: TimeoutID | null = null;
_outerRef: ?HTMLDivElement;
static defaultProps = {
direction: 'ltr',
itemData: undefined,
useIsScrolling: false,
};
state: State = {
instance: this,
isScrolling: false,
horizontalScrollDirection: 'forward',
scrollLeft:
typeof this.props.initialScrollLeft === 'number'
? this.props.initialScrollLeft
: 0,
scrollTop:
typeof this.props.initialScrollTop === 'number'
? this.props.initialScrollTop
: 0,
scrollUpdateWasRequested: false,
verticalScrollDirection: 'forward',
};
// Always use explicit constructor for React components.
// It produces less code after transpilation. (#26)
// eslint-disable-next-line no-useless-constructor
constructor(props: Props<T>) {
super(props);
}
static getDerivedStateFromProps(
nextProps: Props<T>,
prevState: State
): $Shape<State> | null {
validateSharedProps(nextProps, prevState);
validateProps(nextProps);
return null;
}
scrollTo({
scrollLeft,
scrollTop,
}: {
scrollLeft: number,
scrollTop: number,
}): void {
if (scrollLeft !== undefined) {
scrollLeft = Math.max(0, scrollLeft);
}
if (scrollTop !== undefined) {
scrollTop = Math.max(0, scrollTop);
}
this.setState(prevState => {
if (scrollLeft === undefined) {
scrollLeft = prevState.scrollLeft;
}
if (scrollTop === undefined) {
scrollTop = prevState.scrollTop;
}
if (
prevState.scrollLeft === scrollLeft &&
prevState.scrollTop === scrollTop
) {
return null;
}
return {
horizontalScrollDirection:
prevState.scrollLeft < scrollLeft ? 'forward' : 'backward',
scrollLeft: scrollLeft,
scrollTop: scrollTop,
scrollUpdateWasRequested: true,
verticalScrollDirection:
prevState.scrollTop < scrollTop ? 'forward' : 'backward',
};
}, this._resetIsScrollingDebounced);
}
scrollToItem({
align = 'auto',
columnIndex,
rowIndex,
}: {
align: ScrollToAlign,
columnIndex?: number,
rowIndex?: number,
}): void {
const { columnCount, height, rowCount, width } = this.props;
const { scrollLeft, scrollTop } = this.state;
const scrollbarSize = getScrollbarSize();
if (columnIndex !== undefined) {
columnIndex = Math.max(0, Math.min(columnIndex, columnCount - 1));
}
if (rowIndex !== undefined) {
rowIndex = Math.max(0, Math.min(rowIndex, rowCount - 1));
}
const estimatedTotalHeight = getEstimatedTotalHeight(
this.props,
this._instanceProps
);
const estimatedTotalWidth = getEstimatedTotalWidth(
this.props,
this._instanceProps
);
// The scrollbar size should be considered when scrolling an item into view,
// to ensure it's fully visible.
// But we only need to account for its size when it's actually visible.
const horizontalScrollbarSize =
estimatedTotalWidth > width ? scrollbarSize : 0;
const verticalScrollbarSize =
estimatedTotalHeight > height ? scrollbarSize : 0;
this.scrollTo({
scrollLeft:
columnIndex !== undefined
? getOffsetForColumnAndAlignment(
this.props,
columnIndex,
align,
scrollLeft,
this._instanceProps,
verticalScrollbarSize
)
: scrollLeft,
scrollTop:
rowIndex !== undefined
? getOffsetForRowAndAlignment(
this.props,
rowIndex,
align,
scrollTop,
this._instanceProps,
horizontalScrollbarSize
)
: scrollTop,
});
}
componentDidMount() {
const { initialScrollLeft, initialScrollTop } = this.props;
if (this._outerRef != null) {
const outerRef = ((this._outerRef: any): HTMLElement);
if (typeof initialScrollLeft === 'number') {
outerRef.scrollLeft = initialScrollLeft;
}
if (typeof initialScrollTop === 'number') {
outerRef.scrollTop = initialScrollTop;
}
}
this._callPropsCallbacks();
}
componentDidUpdate() {
const { direction } = this.props;
const { scrollLeft, scrollTop, scrollUpdateWasRequested } = this.state;
if (scrollUpdateWasRequested && this._outerRef != null) {
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
// So we need to determine which browser behavior we're dealing with, and mimic it.
const outerRef = ((this._outerRef: any): HTMLElement);
if (direction === 'rtl') {
switch (getRTLOffsetType()) {
case 'negative':
outerRef.scrollLeft = -scrollLeft;
break;
case 'positive-ascending':
outerRef.scrollLeft = scrollLeft;
break;
default:
const { clientWidth, scrollWidth } = outerRef;
outerRef.scrollLeft = scrollWidth - clientWidth - scrollLeft;
break;
}
} else {
outerRef.scrollLeft = Math.max(0, scrollLeft);
}
outerRef.scrollTop = Math.max(0, scrollTop);
}
this._callPropsCallbacks();
}
componentWillUnmount() {
if (this._resetIsScrollingTimeoutId !== null) {
cancelTimeout(this._resetIsScrollingTimeoutId);
}
}
render() {
const {
children,
className,
columnCount,
direction,
height,
innerRef,
innerElementType,
innerTagName,
itemData,
itemKey = defaultItemKey,
outerElementType,
outerTagName,
rowCount,
style,
useIsScrolling,
width,
} = this.props;
const { isScrolling } = this.state;
const [
columnStartIndex,
columnStopIndex,
] = this._getHorizontalRangeToRender();
const [rowStartIndex, rowStopIndex] = this._getVerticalRangeToRender();
const items = [];
if (columnCount > 0 && rowCount) {
for (
let rowIndex = rowStartIndex;
rowIndex <= rowStopIndex;
rowIndex++
) {
for (
let columnIndex = columnStartIndex;
columnIndex <= columnStopIndex;
columnIndex++
) {
items.push(
createElement(children, {
columnIndex,
data: itemData,
isScrolling: useIsScrolling ? isScrolling : undefined,
key: itemKey({ columnIndex, data: itemData, rowIndex }),
rowIndex,
style: this._getItemStyle(rowIndex, columnIndex),
})
);
}
}
}
// Read this value AFTER items have been created,
// So their actual sizes (if variable) are taken into consideration.
const estimatedTotalHeight = getEstimatedTotalHeight(
this.props,
this._instanceProps
);
const estimatedTotalWidth = getEstimatedTotalWidth(
this.props,
this._instanceProps
);
return createElement(
outerElementType || outerTagName || 'div',
{
className,
onScroll: this._onScroll,
ref: this._outerRefSetter,
style: {
position: 'relative',
height,
width,
overflow: 'auto',
WebkitOverflowScrolling: 'touch',
willChange: 'transform',
direction,
...style,
},
},
createElement(innerElementType || innerTagName || 'div', {
children: items,
ref: innerRef,
style: {
height: estimatedTotalHeight,
pointerEvents: isScrolling ? 'none' : undefined,
width: estimatedTotalWidth,
},
})
);
}
_callOnItemsRendered: (
overscanColumnStartIndex: number,
overscanColumnStopIndex: number,
overscanRowStartIndex: number,
overscanRowStopIndex: number,
visibleColumnStartIndex: number,
visibleColumnStopIndex: number,
visibleRowStartIndex: number,
visibleRowStopIndex: number
) => void;
_callOnItemsRendered = memoizeOne(
(
overscanColumnStartIndex: number,
overscanColumnStopIndex: number,
overscanRowStartIndex: number,
overscanRowStopIndex: number,
visibleColumnStartIndex: number,
visibleColumnStopIndex: number,
visibleRowStartIndex: number,
visibleRowStopIndex: number
) =>
((this.props.onItemsRendered: any): OnItemsRenderedCallback)({
overscanColumnStartIndex,
overscanColumnStopIndex,
overscanRowStartIndex,
overscanRowStopIndex,
visibleColumnStartIndex,
visibleColumnStopIndex,
visibleRowStartIndex,
visibleRowStopIndex,
})
);
_callOnScroll: (
scrollLeft: number,
scrollTop: number,
horizontalScrollDirection: ScrollDirection,
verticalScrollDirection: ScrollDirection,
scrollUpdateWasRequested: boolean
) => void;
_callOnScroll = memoizeOne(
(
scrollLeft: number,
scrollTop: number,
horizontalScrollDirection: ScrollDirection,
verticalScrollDirection: ScrollDirection,
scrollUpdateWasRequested: boolean
) =>
((this.props.onScroll: any): OnScrollCallback)({
horizontalScrollDirection,
scrollLeft,
scrollTop,
verticalScrollDirection,
scrollUpdateWasRequested,
})
);
_callPropsCallbacks() {
const { columnCount, onItemsRendered, onScroll, rowCount } = this.props;
if (typeof onItemsRendered === 'function') {
if (columnCount > 0 && rowCount > 0) {
const [
overscanColumnStartIndex,
overscanColumnStopIndex,
visibleColumnStartIndex,
visibleColumnStopIndex,
] = this._getHorizontalRangeToRender();
const [
overscanRowStartIndex,
overscanRowStopIndex,
visibleRowStartIndex,
visibleRowStopIndex,
] = this._getVerticalRangeToRender();
this._callOnItemsRendered(
overscanColumnStartIndex,
overscanColumnStopIndex,
overscanRowStartIndex,
overscanRowStopIndex,
visibleColumnStartIndex,
visibleColumnStopIndex,
visibleRowStartIndex,
visibleRowStopIndex
);
}
}
if (typeof onScroll === 'function') {
const {
horizontalScrollDirection,
scrollLeft,
scrollTop,
scrollUpdateWasRequested,
verticalScrollDirection,
} = this.state;
this._callOnScroll(
scrollLeft,
scrollTop,
horizontalScrollDirection,
verticalScrollDirection,
scrollUpdateWasRequested
);
}
}
// Lazily create and cache item styles while scrolling,
// So that pure component sCU will prevent re-renders.
// We maintain this cache, and pass a style prop rather than index,
// So that List can clear cached styles and force item re-render if necessary.
_getItemStyle: (rowIndex: number, columnIndex: number) => Object;
_getItemStyle = (rowIndex: number, columnIndex: number): Object => {
const { columnWidth, direction, rowHeight } = this.props;
const itemStyleCache = this._getItemStyleCache(
shouldResetStyleCacheOnItemSizeChange && columnWidth,
shouldResetStyleCacheOnItemSizeChange && direction,
shouldResetStyleCacheOnItemSizeChange && rowHeight
);
const key = `${rowIndex}:${columnIndex}`;
let style;
if (itemStyleCache.hasOwnProperty(key)) {
style = itemStyleCache[key];
} else {
const offset = getColumnOffset(
this.props,
columnIndex,
this._instanceProps
);
const isRtl = direction === 'rtl';
itemStyleCache[key] = style = {
position: 'absolute',
left: isRtl ? undefined : offset,
right: isRtl ? offset : undefined,
top: getRowOffset(this.props, rowIndex, this._instanceProps),
height: getRowHeight(this.props, rowIndex, this._instanceProps),
width: getColumnWidth(this.props, columnIndex, this._instanceProps),
};
}
return style;
};
_getItemStyleCache: (_: any, __: any, ___: any) => ItemStyleCache;
_getItemStyleCache = memoizeOne((_: any, __: any, ___: any) => ({}));
_getHorizontalRangeToRender(): [number, number, number, number] {
const {
columnCount,
overscanColumnCount,
overscanColumnsCount,
overscanCount,
rowCount,
} = this.props;
const { horizontalScrollDirection, isScrolling, scrollLeft } = this.state;
const overscanCountResolved: number =
overscanColumnCount || overscanColumnsCount || overscanCount || 1;
if (columnCount === 0 || rowCount === 0) {
return [0, 0, 0, 0];
}
const startIndex = getColumnStartIndexForOffset(
this.props,
scrollLeft,
this._instanceProps
);
const stopIndex = getColumnStopIndexForStartIndex(
this.props,
startIndex,
scrollLeft,
this._instanceProps
);
// Overscan by one item in each direction so that tab/focus works.
// If there isn't at least one extra item, tab loops back around.
const overscanBackward =
!isScrolling || horizontalScrollDirection === 'backward'
? Math.max(1, overscanCountResolved)
: 1;
const overscanForward =
!isScrolling || horizontalScrollDirection === 'forward'
? Math.max(1, overscanCountResolved)
: 1;
return [
Math.max(0, startIndex - overscanBackward),
Math.max(0, Math.min(columnCount - 1, stopIndex + overscanForward)),
startIndex,
stopIndex,
];
}
_getVerticalRangeToRender(): [number, number, number, number] {
const {
columnCount,
overscanCount,
overscanRowCount,
overscanRowsCount,
rowCount,
} = this.props;
const { isScrolling, verticalScrollDirection, scrollTop } = this.state;
const overscanCountResolved: number =
overscanRowCount || overscanRowsCount || overscanCount || 1;
if (columnCount === 0 || rowCount === 0) {
return [0, 0, 0, 0];
}
const startIndex = getRowStartIndexForOffset(
this.props,
scrollTop,
this._instanceProps
);
const stopIndex = getRowStopIndexForStartIndex(
this.props,
startIndex,
scrollTop,
this._instanceProps
);
// Overscan by one item in each direction so that tab/focus works.
// If there isn't at least one extra item, tab loops back around.
const overscanBackward =
!isScrolling || verticalScrollDirection === 'backward'
? Math.max(1, overscanCountResolved)
: 1;
const overscanForward =
!isScrolling || verticalScrollDirection === 'forward'
? Math.max(1, overscanCountResolved)
: 1;
return [
Math.max(0, startIndex - overscanBackward),
Math.max(0, Math.min(rowCount - 1, stopIndex + overscanForward)),
startIndex,
stopIndex,
];
}
_onScroll = (event: ScrollEvent): void => {
const {
clientHeight,
clientWidth,
scrollLeft,
scrollTop,
scrollHeight,
scrollWidth,
} = event.currentTarget;
this.setState(prevState => {
if (
prevState.scrollLeft === scrollLeft &&
prevState.scrollTop === scrollTop
) {
// Scroll position may have been updated by cDM/cDU,
// In which case we don't need to trigger another render,
// And we don't want to update state.isScrolling.
return null;
}
const { direction } = this.props;
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
let calculatedScrollLeft = scrollLeft;
if (direction === 'rtl') {
switch (getRTLOffsetType()) {
case 'negative':
calculatedScrollLeft = -scrollLeft;
break;
case 'positive-descending':
calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft;
break;
}
}
// Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
calculatedScrollLeft = Math.max(
0,
Math.min(calculatedScrollLeft, scrollWidth - clientWidth)
);
const calculatedScrollTop = Math.max(
0,
Math.min(scrollTop, scrollHeight - clientHeight)
);
return {
isScrolling: true,
horizontalScrollDirection:
prevState.scrollLeft < scrollLeft ? 'forward' : 'backward',
scrollLeft: calculatedScrollLeft,
scrollTop: calculatedScrollTop,
verticalScrollDirection:
prevState.scrollTop < scrollTop ? 'forward' : 'backward',
scrollUpdateWasRequested: false,
};
}, this._resetIsScrollingDebounced);
};
_outerRefSetter = (ref: any): void => {
const { outerRef } = this.props;
this._outerRef = ((ref: any): HTMLDivElement);
if (typeof outerRef === 'function') {
outerRef(ref);
} else if (
outerRef != null &&
typeof outerRef === 'object' &&
outerRef.hasOwnProperty('current')
) {
outerRef.current = ref;
}
};
_resetIsScrollingDebounced = () => {
if (this._resetIsScrollingTimeoutId !== null) {
cancelTimeout(this._resetIsScrollingTimeoutId);
}
this._resetIsScrollingTimeoutId = requestTimeout(
this._resetIsScrolling,
IS_SCROLLING_DEBOUNCE_INTERVAL
);
};
_resetIsScrolling = () => {
this._resetIsScrollingTimeoutId = null;
this.setState({ isScrolling: false }, () => {
// Clear style cache after state update has been committed.
// This way we don't break pure sCU for items that don't use isScrolling param.
this._getItemStyleCache(-1);
});
};
};
}
const validateSharedProps = (
{
children,
direction,
height,
innerTagName,
outerTagName,
overscanColumnsCount,
overscanCount,
overscanRowsCount,
width,
}: Props<any>,
{ instance }: State
): void => {
if (process.env.NODE_ENV !== 'production') {
if (typeof overscanCount === 'number') {
if (devWarningsOverscanCount && !devWarningsOverscanCount.has(instance)) {
devWarningsOverscanCount.add(instance);
console.warn(
'The overscanCount prop has been deprecated. ' +
'Please use the overscanColumnCount and overscanRowCount props instead.'
);
}
}
if (
typeof overscanColumnsCount === 'number' ||
typeof overscanRowsCount === 'number'
) {
if (
devWarningsOverscanRowsColumnsCount &&
!devWarningsOverscanRowsColumnsCount.has(instance)
) {
devWarningsOverscanRowsColumnsCount.add(instance);
console.warn(
'The overscanColumnsCount and overscanRowsCount props have been deprecated. ' +
'Please use the overscanColumnCount and overscanRowCount props instead.'
);
}
}
if (innerTagName != null || outerTagName != null) {
if (devWarningsTagName && !devWarningsTagName.has(instance)) {
devWarningsTagName.add(instance);
console.warn(
'The innerTagName and outerTagName props have been deprecated. ' +
'Please use the innerElementType and outerElementType props instead.'
);
}
}
if (children == null) {
throw Error(
'An invalid "children" prop has been specified. ' +
'Value should be a React component. ' +
`"${children === null ? 'null' : typeof children}" was specified.`
);
}
switch (direction) {
case 'ltr':
case 'rtl':
// Valid values
break;
default:
throw Error(
'An invalid "direction" prop has been specified. ' +
'Value should be either "ltr" or "rtl". ' +
`"${direction}" was specified.`
);
}
if (typeof width !== 'number') {
throw Error(
'An invalid "width" prop has been specified. ' +
'Grids must specify a number for width. ' +
`"${width === null ? 'null' : typeof width}" was specified.`
);
}
if (typeof height !== 'number') {
throw Error(
'An invalid "height" prop has been specified. ' +
'Grids must specify a number for height. ' +
`"${height === null ? 'null' : typeof height}" was specified.`
);
}
}
};

745
node_modules/react-window/src/createListComponent.js generated vendored Normal file
View File

@@ -0,0 +1,745 @@
// @flow
import memoizeOne from 'memoize-one';
import { createElement, PureComponent } from 'react';
import { cancelTimeout, requestTimeout } from './timer';
import { getScrollbarSize, getRTLOffsetType } from './domHelpers';
import type { TimeoutID } from './timer';
export type ScrollToAlign = 'auto' | 'smart' | 'center' | 'start' | 'end';
type itemSize = number | ((index: number) => number);
// TODO Deprecate directions "horizontal" and "vertical"
type Direction = 'ltr' | 'rtl' | 'horizontal' | 'vertical';
type Layout = 'horizontal' | 'vertical';
type RenderComponentProps<T> = {|
data: T,
index: number,
isScrolling?: boolean,
style: Object,
|};
type RenderComponent<T> = React$ComponentType<$Shape<RenderComponentProps<T>>>;
type ScrollDirection = 'forward' | 'backward';
type onItemsRenderedCallback = ({
overscanStartIndex: number,
overscanStopIndex: number,
visibleStartIndex: number,
visibleStopIndex: number,
}) => void;
type onScrollCallback = ({
scrollDirection: ScrollDirection,
scrollOffset: number,
scrollUpdateWasRequested: boolean,
}) => void;
type ScrollEvent = SyntheticEvent<HTMLDivElement>;
type ItemStyleCache = { [index: number]: Object };
type OuterProps = {|
children: React$Node,
className: string | void,
onScroll: ScrollEvent => void,
style: {
[string]: mixed,
},
|};
type InnerProps = {|
children: React$Node,
style: {
[string]: mixed,
},
|};
export type Props<T> = {|
children: RenderComponent<T>,
className?: string,
direction: Direction,
height: number | string,
initialScrollOffset?: number,
innerRef?: any,
innerElementType?: string | React$AbstractComponent<InnerProps, any>,
innerTagName?: string, // deprecated
itemCount: number,
itemData: T,
itemKey?: (index: number, data: T) => any,
itemSize: itemSize,
layout: Layout,
onItemsRendered?: onItemsRenderedCallback,
onScroll?: onScrollCallback,
outerRef?: any,
outerElementType?: string | React$AbstractComponent<OuterProps, any>,
outerTagName?: string, // deprecated
overscanCount: number,
style?: Object,
useIsScrolling: boolean,
width: number | string,
|};
type State = {|
instance: any,
isScrolling: boolean,
scrollDirection: ScrollDirection,
scrollOffset: number,
scrollUpdateWasRequested: boolean,
|};
type GetItemOffset = (
props: Props<any>,
index: number,
instanceProps: any
) => number;
type GetItemSize = (
props: Props<any>,
index: number,
instanceProps: any
) => number;
type GetEstimatedTotalSize = (props: Props<any>, instanceProps: any) => number;
type GetOffsetForIndexAndAlignment = (
props: Props<any>,
index: number,
align: ScrollToAlign,
scrollOffset: number,
instanceProps: any
) => number;
type GetStartIndexForOffset = (
props: Props<any>,
offset: number,
instanceProps: any
) => number;
type GetStopIndexForStartIndex = (
props: Props<any>,
startIndex: number,
scrollOffset: number,
instanceProps: any
) => number;
type InitInstanceProps = (props: Props<any>, instance: any) => any;
type ValidateProps = (props: Props<any>) => void;
const IS_SCROLLING_DEBOUNCE_INTERVAL = 150;
const defaultItemKey = (index: number, data: any) => index;
// In DEV mode, this Set helps us only log a warning once per component instance.
// This avoids spamming the console every time a render happens.
let devWarningsDirection = null;
let devWarningsTagName = null;
if (process.env.NODE_ENV !== 'production') {
if (typeof window !== 'undefined' && typeof window.WeakSet !== 'undefined') {
devWarningsDirection = new WeakSet();
devWarningsTagName = new WeakSet();
}
}
export default function createListComponent({
getItemOffset,
getEstimatedTotalSize,
getItemSize,
getOffsetForIndexAndAlignment,
getStartIndexForOffset,
getStopIndexForStartIndex,
initInstanceProps,
shouldResetStyleCacheOnItemSizeChange,
validateProps,
}: {|
getItemOffset: GetItemOffset,
getEstimatedTotalSize: GetEstimatedTotalSize,
getItemSize: GetItemSize,
getOffsetForIndexAndAlignment: GetOffsetForIndexAndAlignment,
getStartIndexForOffset: GetStartIndexForOffset,
getStopIndexForStartIndex: GetStopIndexForStartIndex,
initInstanceProps: InitInstanceProps,
shouldResetStyleCacheOnItemSizeChange: boolean,
validateProps: ValidateProps,
|}) {
return class List<T> extends PureComponent<Props<T>, State> {
_instanceProps: any = initInstanceProps(this.props, this);
_outerRef: ?HTMLDivElement;
_resetIsScrollingTimeoutId: TimeoutID | null = null;
static defaultProps = {
direction: 'ltr',
itemData: undefined,
layout: 'vertical',
overscanCount: 2,
useIsScrolling: false,
};
state: State = {
instance: this,
isScrolling: false,
scrollDirection: 'forward',
scrollOffset:
typeof this.props.initialScrollOffset === 'number'
? this.props.initialScrollOffset
: 0,
scrollUpdateWasRequested: false,
};
// Always use explicit constructor for React components.
// It produces less code after transpilation. (#26)
// eslint-disable-next-line no-useless-constructor
constructor(props: Props<T>) {
super(props);
}
static getDerivedStateFromProps(
nextProps: Props<T>,
prevState: State
): $Shape<State> | null {
validateSharedProps(nextProps, prevState);
validateProps(nextProps);
return null;
}
scrollTo(scrollOffset: number): void {
scrollOffset = Math.max(0, scrollOffset);
this.setState(prevState => {
if (prevState.scrollOffset === scrollOffset) {
return null;
}
return {
scrollDirection:
prevState.scrollOffset < scrollOffset ? 'forward' : 'backward',
scrollOffset: scrollOffset,
scrollUpdateWasRequested: true,
};
}, this._resetIsScrollingDebounced);
}
scrollToItem(index: number, align: ScrollToAlign = 'auto'): void {
const { itemCount, layout } = this.props;
const { scrollOffset } = this.state;
index = Math.max(0, Math.min(index, itemCount - 1));
// The scrollbar size should be considered when scrolling an item into view, to ensure it's fully visible.
// But we only need to account for its size when it's actually visible.
// This is an edge case for lists; normally they only scroll in the dominant direction.
let scrollbarSize = 0;
if (this._outerRef) {
const outerRef = ((this._outerRef: any): HTMLElement);
if (layout === 'vertical') {
scrollbarSize =
outerRef.scrollWidth > outerRef.clientWidth
? getScrollbarSize()
: 0;
} else {
scrollbarSize =
outerRef.scrollHeight > outerRef.clientHeight
? getScrollbarSize()
: 0;
}
}
this.scrollTo(
getOffsetForIndexAndAlignment(
this.props,
index,
align,
scrollOffset,
this._instanceProps,
scrollbarSize
)
);
}
componentDidMount() {
const { direction, initialScrollOffset, layout } = this.props;
if (typeof initialScrollOffset === 'number' && this._outerRef != null) {
const outerRef = ((this._outerRef: any): HTMLElement);
// TODO Deprecate direction "horizontal"
if (direction === 'horizontal' || layout === 'horizontal') {
outerRef.scrollLeft = initialScrollOffset;
} else {
outerRef.scrollTop = initialScrollOffset;
}
}
this._callPropsCallbacks();
}
componentDidUpdate() {
const { direction, layout } = this.props;
const { scrollOffset, scrollUpdateWasRequested } = this.state;
if (scrollUpdateWasRequested && this._outerRef != null) {
const outerRef = ((this._outerRef: any): HTMLElement);
// TODO Deprecate direction "horizontal"
if (direction === 'horizontal' || layout === 'horizontal') {
if (direction === 'rtl') {
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
// So we need to determine which browser behavior we're dealing with, and mimic it.
switch (getRTLOffsetType()) {
case 'negative':
outerRef.scrollLeft = -scrollOffset;
break;
case 'positive-ascending':
outerRef.scrollLeft = scrollOffset;
break;
default:
const { clientWidth, scrollWidth } = outerRef;
outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset;
break;
}
} else {
outerRef.scrollLeft = scrollOffset;
}
} else {
outerRef.scrollTop = scrollOffset;
}
}
this._callPropsCallbacks();
}
componentWillUnmount() {
if (this._resetIsScrollingTimeoutId !== null) {
cancelTimeout(this._resetIsScrollingTimeoutId);
}
}
render() {
const {
children,
className,
direction,
height,
innerRef,
innerElementType,
innerTagName,
itemCount,
itemData,
itemKey = defaultItemKey,
layout,
outerElementType,
outerTagName,
style,
useIsScrolling,
width,
} = this.props;
const { isScrolling } = this.state;
// TODO Deprecate direction "horizontal"
const isHorizontal =
direction === 'horizontal' || layout === 'horizontal';
const onScroll = isHorizontal
? this._onScrollHorizontal
: this._onScrollVertical;
const [startIndex, stopIndex] = this._getRangeToRender();
const items = [];
if (itemCount > 0) {
for (let index = startIndex; index <= stopIndex; index++) {
items.push(
createElement(children, {
data: itemData,
key: itemKey(index, itemData),
index,
isScrolling: useIsScrolling ? isScrolling : undefined,
style: this._getItemStyle(index),
})
);
}
}
// Read this value AFTER items have been created,
// So their actual sizes (if variable) are taken into consideration.
const estimatedTotalSize = getEstimatedTotalSize(
this.props,
this._instanceProps
);
return createElement(
outerElementType || outerTagName || 'div',
{
className,
onScroll,
ref: this._outerRefSetter,
style: {
position: 'relative',
height,
width,
overflow: 'auto',
WebkitOverflowScrolling: 'touch',
willChange: 'transform',
direction,
...style,
},
},
createElement(innerElementType || innerTagName || 'div', {
children: items,
ref: innerRef,
style: {
height: isHorizontal ? '100%' : estimatedTotalSize,
pointerEvents: isScrolling ? 'none' : undefined,
width: isHorizontal ? estimatedTotalSize : '100%',
},
})
);
}
_callOnItemsRendered: (
overscanStartIndex: number,
overscanStopIndex: number,
visibleStartIndex: number,
visibleStopIndex: number
) => void;
_callOnItemsRendered = memoizeOne(
(
overscanStartIndex: number,
overscanStopIndex: number,
visibleStartIndex: number,
visibleStopIndex: number
) =>
((this.props.onItemsRendered: any): onItemsRenderedCallback)({
overscanStartIndex,
overscanStopIndex,
visibleStartIndex,
visibleStopIndex,
})
);
_callOnScroll: (
scrollDirection: ScrollDirection,
scrollOffset: number,
scrollUpdateWasRequested: boolean
) => void;
_callOnScroll = memoizeOne(
(
scrollDirection: ScrollDirection,
scrollOffset: number,
scrollUpdateWasRequested: boolean
) =>
((this.props.onScroll: any): onScrollCallback)({
scrollDirection,
scrollOffset,
scrollUpdateWasRequested,
})
);
_callPropsCallbacks() {
if (typeof this.props.onItemsRendered === 'function') {
const { itemCount } = this.props;
if (itemCount > 0) {
const [
overscanStartIndex,
overscanStopIndex,
visibleStartIndex,
visibleStopIndex,
] = this._getRangeToRender();
this._callOnItemsRendered(
overscanStartIndex,
overscanStopIndex,
visibleStartIndex,
visibleStopIndex
);
}
}
if (typeof this.props.onScroll === 'function') {
const {
scrollDirection,
scrollOffset,
scrollUpdateWasRequested,
} = this.state;
this._callOnScroll(
scrollDirection,
scrollOffset,
scrollUpdateWasRequested
);
}
}
// Lazily create and cache item styles while scrolling,
// So that pure component sCU will prevent re-renders.
// We maintain this cache, and pass a style prop rather than index,
// So that List can clear cached styles and force item re-render if necessary.
_getItemStyle: (index: number) => Object;
_getItemStyle = (index: number): Object => {
const { direction, itemSize, layout } = this.props;
const itemStyleCache = this._getItemStyleCache(
shouldResetStyleCacheOnItemSizeChange && itemSize,
shouldResetStyleCacheOnItemSizeChange && layout,
shouldResetStyleCacheOnItemSizeChange && direction
);
let style;
if (itemStyleCache.hasOwnProperty(index)) {
style = itemStyleCache[index];
} else {
const offset = getItemOffset(this.props, index, this._instanceProps);
const size = getItemSize(this.props, index, this._instanceProps);
// TODO Deprecate direction "horizontal"
const isHorizontal =
direction === 'horizontal' || layout === 'horizontal';
const isRtl = direction === 'rtl';
const offsetHorizontal = isHorizontal ? offset : 0;
itemStyleCache[index] = style = {
position: 'absolute',
left: isRtl ? undefined : offsetHorizontal,
right: isRtl ? offsetHorizontal : undefined,
top: !isHorizontal ? offset : 0,
height: !isHorizontal ? size : '100%',
width: isHorizontal ? size : '100%',
};
}
return style;
};
_getItemStyleCache: (_: any, __: any, ___: any) => ItemStyleCache;
_getItemStyleCache = memoizeOne((_: any, __: any, ___: any) => ({}));
_getRangeToRender(): [number, number, number, number] {
const { itemCount, overscanCount } = this.props;
const { isScrolling, scrollDirection, scrollOffset } = this.state;
if (itemCount === 0) {
return [0, 0, 0, 0];
}
const startIndex = getStartIndexForOffset(
this.props,
scrollOffset,
this._instanceProps
);
const stopIndex = getStopIndexForStartIndex(
this.props,
startIndex,
scrollOffset,
this._instanceProps
);
// Overscan by one item in each direction so that tab/focus works.
// If there isn't at least one extra item, tab loops back around.
const overscanBackward =
!isScrolling || scrollDirection === 'backward'
? Math.max(1, overscanCount)
: 1;
const overscanForward =
!isScrolling || scrollDirection === 'forward'
? Math.max(1, overscanCount)
: 1;
return [
Math.max(0, startIndex - overscanBackward),
Math.max(0, Math.min(itemCount - 1, stopIndex + overscanForward)),
startIndex,
stopIndex,
];
}
_onScrollHorizontal = (event: ScrollEvent): void => {
const { clientWidth, scrollLeft, scrollWidth } = event.currentTarget;
this.setState(prevState => {
if (prevState.scrollOffset === scrollLeft) {
// Scroll position may have been updated by cDM/cDU,
// In which case we don't need to trigger another render,
// And we don't want to update state.isScrolling.
return null;
}
const { direction } = this.props;
let scrollOffset = scrollLeft;
if (direction === 'rtl') {
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
switch (getRTLOffsetType()) {
case 'negative':
scrollOffset = -scrollLeft;
break;
case 'positive-descending':
scrollOffset = scrollWidth - clientWidth - scrollLeft;
break;
}
}
// Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
scrollOffset = Math.max(
0,
Math.min(scrollOffset, scrollWidth - clientWidth)
);
return {
isScrolling: true,
scrollDirection:
prevState.scrollOffset < scrollOffset ? 'forward' : 'backward',
scrollOffset,
scrollUpdateWasRequested: false,
};
}, this._resetIsScrollingDebounced);
};
_onScrollVertical = (event: ScrollEvent): void => {
const { clientHeight, scrollHeight, scrollTop } = event.currentTarget;
this.setState(prevState => {
if (prevState.scrollOffset === scrollTop) {
// Scroll position may have been updated by cDM/cDU,
// In which case we don't need to trigger another render,
// And we don't want to update state.isScrolling.
return null;
}
// Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
const scrollOffset = Math.max(
0,
Math.min(scrollTop, scrollHeight - clientHeight)
);
return {
isScrolling: true,
scrollDirection:
prevState.scrollOffset < scrollOffset ? 'forward' : 'backward',
scrollOffset,
scrollUpdateWasRequested: false,
};
}, this._resetIsScrollingDebounced);
};
_outerRefSetter = (ref: any): void => {
const { outerRef } = this.props;
this._outerRef = ((ref: any): HTMLDivElement);
if (typeof outerRef === 'function') {
outerRef(ref);
} else if (
outerRef != null &&
typeof outerRef === 'object' &&
outerRef.hasOwnProperty('current')
) {
outerRef.current = ref;
}
};
_resetIsScrollingDebounced = () => {
if (this._resetIsScrollingTimeoutId !== null) {
cancelTimeout(this._resetIsScrollingTimeoutId);
}
this._resetIsScrollingTimeoutId = requestTimeout(
this._resetIsScrolling,
IS_SCROLLING_DEBOUNCE_INTERVAL
);
};
_resetIsScrolling = () => {
this._resetIsScrollingTimeoutId = null;
this.setState({ isScrolling: false }, () => {
// Clear style cache after state update has been committed.
// This way we don't break pure sCU for items that don't use isScrolling param.
this._getItemStyleCache(-1, null);
});
};
};
}
// NOTE: I considered further wrapping individual items with a pure ListItem component.
// This would avoid ever calling the render function for the same index more than once,
// But it would also add the overhead of a lot of components/fibers.
// I assume people already do this (render function returning a class component),
// So my doing it would just unnecessarily double the wrappers.
const validateSharedProps = (
{
children,
direction,
height,
layout,
innerTagName,
outerTagName,
width,
}: Props<any>,
{ instance }: State
): void => {
if (process.env.NODE_ENV !== 'production') {
if (innerTagName != null || outerTagName != null) {
if (devWarningsTagName && !devWarningsTagName.has(instance)) {
devWarningsTagName.add(instance);
console.warn(
'The innerTagName and outerTagName props have been deprecated. ' +
'Please use the innerElementType and outerElementType props instead.'
);
}
}
// TODO Deprecate direction "horizontal"
const isHorizontal = direction === 'horizontal' || layout === 'horizontal';
switch (direction) {
case 'horizontal':
case 'vertical':
if (devWarningsDirection && !devWarningsDirection.has(instance)) {
devWarningsDirection.add(instance);
console.warn(
'The direction prop should be either "ltr" (default) or "rtl". ' +
'Please use the layout prop to specify "vertical" (default) or "horizontal" orientation.'
);
}
break;
case 'ltr':
case 'rtl':
// Valid values
break;
default:
throw Error(
'An invalid "direction" prop has been specified. ' +
'Value should be either "ltr" or "rtl". ' +
`"${direction}" was specified.`
);
}
switch (layout) {
case 'horizontal':
case 'vertical':
// Valid values
break;
default:
throw Error(
'An invalid "layout" prop has been specified. ' +
'Value should be either "horizontal" or "vertical". ' +
`"${layout}" was specified.`
);
}
if (children == null) {
throw Error(
'An invalid "children" prop has been specified. ' +
'Value should be a React component. ' +
`"${children === null ? 'null' : typeof children}" was specified.`
);
}
if (isHorizontal && typeof width !== 'number') {
throw Error(
'An invalid "width" prop has been specified. ' +
'Horizontal lists must specify a number for width. ' +
`"${width === null ? 'null' : typeof width}" was specified.`
);
} else if (!isHorizontal && typeof height !== 'number') {
throw Error(
'An invalid "height" prop has been specified. ' +
'Vertical lists must specify a number for height. ' +
`"${height === null ? 'null' : typeof height}" was specified.`
);
}
}
};

72
node_modules/react-window/src/domHelpers.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
// @flow
let size: number = -1;
// This utility copied from "dom-helpers" package.
export function getScrollbarSize(recalculate?: boolean = false): number {
if (size === -1 || recalculate) {
const div = document.createElement('div');
const style = div.style;
style.width = '50px';
style.height = '50px';
style.overflow = 'scroll';
((document.body: any): HTMLBodyElement).appendChild(div);
size = div.offsetWidth - div.clientWidth;
((document.body: any): HTMLBodyElement).removeChild(div);
}
return size;
}
export type RTLOffsetType =
| 'negative'
| 'positive-descending'
| 'positive-ascending';
let cachedRTLResult: RTLOffsetType | null = null;
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
// Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
// Safari's elastic bounce makes detecting this even more complicated wrt potential false positives.
// The safest way to check this is to intentionally set a negative offset,
// and then verify that the subsequent "scroll" event matches the negative offset.
// If it does not match, then we can assume a non-standard RTL scroll implementation.
export function getRTLOffsetType(recalculate?: boolean = false): RTLOffsetType {
if (cachedRTLResult === null || recalculate) {
const outerDiv = document.createElement('div');
const outerStyle = outerDiv.style;
outerStyle.width = '50px';
outerStyle.height = '50px';
outerStyle.overflow = 'scroll';
outerStyle.direction = 'rtl';
const innerDiv = document.createElement('div');
const innerStyle = innerDiv.style;
innerStyle.width = '100px';
innerStyle.height = '100px';
outerDiv.appendChild(innerDiv);
((document.body: any): HTMLBodyElement).appendChild(outerDiv);
if (outerDiv.scrollLeft > 0) {
cachedRTLResult = 'positive-descending';
} else {
outerDiv.scrollLeft = 1;
if (outerDiv.scrollLeft === 0) {
cachedRTLResult = 'negative';
} else {
cachedRTLResult = 'positive-ascending';
}
}
((document.body: any): HTMLBodyElement).removeChild(outerDiv);
return cachedRTLResult;
}
return cachedRTLResult;
}

9
node_modules/react-window/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// @flow
export { default as VariableSizeGrid } from './VariableSizeGrid';
export { default as VariableSizeList } from './VariableSizeList';
export { default as FixedSizeGrid } from './FixedSizeGrid';
export { default as FixedSizeList } from './FixedSizeList';
export { default as areEqual } from './areEqual';
export { default as shouldComponentUpdate } from './shouldComponentUpdate';

17
node_modules/react-window/src/shallowDiffers.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
// @flow
// Pulled from react-compat
// https://github.com/developit/preact-compat/blob/7c5de00e7c85e2ffd011bf3af02899b63f699d3a/src/index.js#L349
export default function shallowDiffers(prev: Object, next: Object): boolean {
for (let attribute in prev) {
if (!(attribute in next)) {
return true;
}
}
for (let attribute in next) {
if (prev[attribute] !== next[attribute]) {
return true;
}
}
return false;
}

16
node_modules/react-window/src/shouldComponentUpdate.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
// @flow
import areEqual from './areEqual';
import shallowDiffers from './shallowDiffers';
// Custom shouldComponentUpdate for class components.
// It knows to compare individual style props and ignore the wrapper object.
// See https://reactjs.org/docs/react-component.html#shouldcomponentupdate
export default function shouldComponentUpdate(
nextProps: Object,
nextState: Object
): boolean {
return (
!areEqual(this.props, nextProps) || shallowDiffers(this.state, nextState)
);
}

37
node_modules/react-window/src/timer.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
// @flow
// Animation frame based implementation of setTimeout.
// Inspired by Joe Lambert, https://gist.github.com/joelambert/1002116#file-requesttimeout-js
const hasNativePerformanceNow =
typeof performance === 'object' && typeof performance.now === 'function';
const now = hasNativePerformanceNow
? () => performance.now()
: () => Date.now();
export type TimeoutID = {|
id: AnimationFrameID,
|};
export function cancelTimeout(timeoutID: TimeoutID) {
cancelAnimationFrame(timeoutID.id);
}
export function requestTimeout(callback: Function, delay: number): TimeoutID {
const start = now();
function tick() {
if (now() - start >= delay) {
callback.call(null);
} else {
timeoutID.id = requestAnimationFrame(tick);
}
}
const timeoutID: TimeoutID = {
id: requestAnimationFrame(tick),
};
return timeoutID;
}