├── .babelrc ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── README.v1.md ├── demo ├── dist │ ├── app.js │ ├── index.html │ ├── react.js │ └── virtualList.js ├── src │ ├── ConfigurableExample.js │ ├── app.js │ └── index.html └── webpack.config.babel.js ├── deploy-to-gh-pages.sh ├── jsconfig.json ├── lib ├── VirtualList.js └── utils │ ├── defaultMapVirtualToProps.js │ ├── getElementTop.js │ ├── getVisibleItemBounds.js │ ├── throttleWithRAF.js │ └── topFromWindow.js ├── package.json └── src ├── VirtualList.js ├── __tests__ └── VirtualList.js └── utils ├── __tests__ ├── defaultMapVirtualToProps.js ├── getElementTop.js ├── getVisibleItemBounds.js └── topFromWindow.js ├── defaultMapVirtualToProps.js ├── getElementTop.js ├── getVisibleItemBounds.js ├── throttleWithRAF.js └── topFromWindow.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-0", 5 | "react" 6 | ] 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: stable 3 | after_success: 4 | bash ./deploy-to-gh-pages.sh 5 | env: 6 | global: 7 | secure: eel7PQWWJmM2gyxTCiUzv/4GYfDfKqrPTcEZOFYGgF0JkdgAqwowvnbw9B8Oexwhp8z/4hPwZNaeZnOAUlUekTJhpMItRcw6egyPyRxzgw2Uk1JC5JU6WVZdjjqQ4GJ+N6Spt56HGiEnTBtVAo2z1deah6s2Z9ZFnc/wqfz/Y/tE5NWE1ePD3cfqnf35yZ2jQkaOMPUS36inYRm+Fjc3IdCEH0OE/IsKYW00MLWlCyN/OkhElMW9kcjpzfpfdSjsyWNnAZF9BZykD053bh+R8gkKkFdyHzkA/w5wQoXAkRkz7ONkDOYXHNQK4VFwT0sl8+WU2tccmKFM9YGxnwLcBCF4ZUxoUNGaWsZsF9xlDZ0ZaV7/ySYyqpFZtfJvv42R1bx7wbIz8PVWqL2/Tl+HMU47X+L6ukDz3yZPFI/3Dcr0ksMU6/YmQR3yoBlTRyX7eJKjO3hiDHys1lFEsA6gCh9IU43a00msAHXOBJ171OL0SDn5qbeUhSS2LRnwXTBh5HZ9b5o7Zrj9nFtaL450kAXBoE5aulI+hAixce7tPNL7fLs+/WRKnYwxLyrjQ7aiv3NVJbZc3RkdheGVB8Wo15mAZZ6cJKCjChfJTPpIM92ZrFFi45dOa8KhVwll1+vqMv6P0fWXHXvBIItPMrGbMrGz0vzUvpePiEMhX2MjtO8= -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Dizzle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [react-virtual-list](http://developerdizzle.github.io/react-virtual-list/) [![Build Status](https://travis-ci.org/developerdizzle/react-virtual-list.svg?branch=master)](https://travis-ci.org/developerdizzle/react-virtual-list) [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/react.svg)](https://bundlephobia.com/result?p=react-virtual-list) 2 | 3 | 4 | Super simple virtualized list [higher-order component](https://facebook.github.io/react/docs/higher-order-components.html) for [React](https://github.com/facebook/react) version `^15.0.0 || ^16.0.0`. 5 | 6 | [Check out the demo here](http://developerdizzle.github.io/react-virtual-list) 7 | 8 | `react-virtual-list` allows you to display a large list of fixed-height items, while only rendering the items visible on the screen. This allows a large list to be rendered with much fewer DOM elements. 9 | 10 | ### Some other benefits: 11 | * One dependency (and it's `prop-types`) 12 | * [Small!](https://bundlephobia.com/result?p=react-virtual-list) 13 | * Performant - demo page almost always stays over 60fps [http://i.imgur.com/CHVCK9x.png](http://i.imgur.com/CHVCK9x.png) 14 | * Keeps your components separate - as a higher-order component 15 | * Gives you control - doesn't force any particular markup, but gives you the necessary styles and data to use. 16 | 17 | ## Legacy 18 | 19 | If you're looking for documentation on version 1, supporting React `~0.13.x`, it's [here](README.v1.md). 20 | 21 | ## Installation 22 | 23 | You can use [npm](https://npmjs.org) to install [react-virtual-list](https://www.npmjs.com/package/react-virtual-list). 24 | 25 | ```console 26 | > npm install react-virtual-list --save 27 | ``` 28 | 29 | ## Usage 30 | 31 | The `./lib/VirtualList.js` module exports a single, ES5-compatible, CommonJS-accessible, component factory. 32 | 33 | ```js 34 | import VirtualList from 'react-virtual-list'; 35 | ``` 36 | 37 | Your inner list component uses the `virtual` property to render the visible items, and set a style to set the overall list height and padding. 38 | 39 | ```js 40 | const MyList = ({ 41 | virtual, 42 | itemHeight, 43 | }) => ( 44 | 51 | ); 52 | ``` 53 | 54 | **Note:** You should set [keys](https://facebook.github.io/react/docs/lists-and-keys.html) on your list items. 55 | 56 | Create the virtualized component. 57 | 58 | ```js 59 | const MyVirtualList = VirtualList()(MyList); 60 | ``` 61 | 62 | Write the JSX for the virtualized component with the necessary [properties](#properties). 63 | 64 | ```js 65 | 69 | ``` 70 | 71 | #### Options 72 | 73 | Options are used before the virtualized component can be created. This means that if you need to change an option after the initial render, you will need to recreate the virtualized component. 74 | 75 | ```js 76 | const options = { 77 | container: this.refs.container, // use this scrollable element as a container 78 | initialState: { 79 | firstItemIndex: 0, // show first ten items 80 | lastItemIndex: 9, // during initial render 81 | }, 82 | }; 83 | 84 | const MyVirtualList = VirtualList(options)(MyList); 85 | ``` 86 | 87 | Name | Type | Default | Description 88 | --- | --- | --- | --- 89 | `container` | DOM Element | window | Scrollable element that contains the list. Use this if you have a list inside an element with `overflow: scroll`. 90 | `initialState` | object | - | An object with `firstItemIndex` and `lastItemIndex` properties, which represent array indexes of `items` (see below). These are used to calculate the visible items before the component is mounted. Useful for server-side rendering. 91 | 92 | #### Properties 93 | 94 | These properties and any others set on your virtual component, such as `className`, will be passed down to the inner component. 95 | 96 | Name | Type | Default | Description 97 | --- | --- | --- | --- 98 | `items` | Array | - | Full array of list items. Only the visible subset of these will be rendered. 99 | `itemHeight` | Number | - | Height in pixels of a single item. **You must have a CSS rule that sets the height of each list item to this value.** 100 | `itemBuffer` | Number | 0 | Number of items that should be rendered before and after the visible viewport. Try using this if you have a complex list that suffers from a bit of lag when scrolling. 101 | 102 | #### Mapping 103 | 104 | `VirtualList` allows a second, optional, parameter, named `mapVirtualToProps`, which functions similarly to [Redux's `mapStateToProps`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options). This function can be provided to change the properties passed to `MyList`. Its arguments are: 105 | 106 | Name | Description 107 | --- | --- 108 | `props` | Includes all properties passed to `MyVirtualList` 109 | `state` | Includes `firstItemIndex` and `lastItemIndex`; array indexes of the visible bounds of `items` 110 | 111 | The default `mapVirtualToProps` can be found [here](/src/utils/defaultMapVirtualToProps.js). 112 | 113 | #### Example Usage 114 | 115 | Check out [demo/src/app.js](demo/src/app.js) and [demo/src/ConfigurableExample.js](demo/src/ConfigurableExample.js) for the example used in the [demo](http://developerdizzle.github.io/react-virtual-list). 116 | 117 | ## Tests 118 | 119 | Use `npm test` to run the tests using [Jest](https://github.com/facebook/jest). Not everything is currently tested yet! 120 | -------------------------------------------------------------------------------- /README.v1.md: -------------------------------------------------------------------------------- 1 | # [react-virtual-list](http://developerdizzle.github.io/react-virtual-list/) [![Build Status](https://travis-ci.org/developerdizzle/react-virtual-list.svg?branch=master)](https://travis-ci.org/developerdizzle/react-virtual-list) 2 | # Legacy (version 1) 3 | 4 | Super simple virtualized list [React](https://github.com/facebook/react) component 5 | 6 | [Check out the demo here](http://developerdizzle.github.io/react-virtual-list) 7 | 8 | This React component allows you to display a large list of fixed-height items in your document, while only rendering the items visible in the viewport. This allows a large list to be rendered with much fewer DOM elements. 9 | 10 | ## Installation 11 | 12 | You can use NPM to install react-virtual-list: 13 | 14 | ```console 15 | $ npm install react-virtual-list --save 16 | ``` 17 | 18 | ## Usage 19 | 20 | The `./dist/VirtualList.js` module exports a single React component. 21 | 22 | ``` 23 | var VirtualList = require('react-virtual-list'); 24 | ``` 25 | 26 | #### JSX 27 | 28 | ``` 29 | 30 | ``` 31 | 32 | #### Properties 33 | 34 | * `items` the full array of list items. Only the visible subset of these will be rendered. 35 | * `renderItem` a function to render a single item, passed as argument `item`. Must return a single React element (`React.createElement(...)`) 36 | * `itemHeight` the height in pixels of a single item. **You must have a CSS rule that sets the height of each list item to this value.** 37 | * `container` the scrollable element that contains the list. Defaults to `window`. Use this if you have a list inside an element with `overflow: scroll`. 38 | * `tagName` the tagName for the root element that surrounds the items rendered by renderItem. Defaults to `div`. Use this if you want to render a list with `ul` and `li`, or any other elements. 39 | * `scrollDelay` the delay in milliseconds after scroll to recalculate. Defaults to `0`. Can be used to throttle recalculation. 40 | * `itemBuffer` the number of items that should be rendered before and after the visible viewport. Defaults to `0`. 41 | 42 | Any other properties set on `VirtualList`, such as `className`, will be reflected on the component's root element. 43 | 44 | #### Functions 45 | 46 | * `visibleItems` the currently visible array of items. Can be used to figure out which items are in the viewport. Eg: `var items = this.refs.list.visibleItems()` 47 | 48 | #### Example Usage 49 | 50 | Check out [https://github.com/developerdizzle/react-virtual-list/blob/gh-pages/App.jsx](https://github.com/developerdizzle/react-virtual-list/blob/gh-pages/App.jsx) for the example used in the demo. 51 | 52 | ## Tests 53 | 54 | Use `npm test` to run the tests using [jasmine-node](https://github.com/mhevery/jasmine-node). Currently only the math calculations are tested. Hoping to add some DOM tests as well. 55 | 56 | ## To Do 57 | 58 | * ES6/2015 59 | * [Known issue with mobile scroll event](https://github.com/developerdizzle/react-virtual-list/issues/1) -------------------------------------------------------------------------------- /demo/dist/app.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([2],[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}var a=n(1),r=i(a),o=n(2),l=n(7),s=i(l);n(31);var u=function(e){var t=e.virtual,n=e.itemHeight;return r.default.createElement("ul",{className:"media-list list-group",style:t.style},t.items.map(function(e){return r.default.createElement("li",{key:"item"+e.id,className:"list-group-item",style:{height:n}},r.default.createElement("div",{className:"media-left"},r.default.createElement("img",{className:"media-object",src:"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHZpZXdCb3g9IjAgMCA2NCA2NCIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+PGRlZnMvPjxyZWN0IHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIgZmlsbD0iI0VFRUVFRSIvPjxnPjx0ZXh0IHg9IjEzLjQ2ODc1IiB5PSIzMiIgc3R5bGU9ImZpbGw6I0FBQUFBQTtmb250LXdlaWdodDpib2xkO2ZvbnQtZmFtaWx5OkFyaWFsLCBIZWx2ZXRpY2EsIE9wZW4gU2Fucywgc2Fucy1zZXJpZiwgbW9ub3NwYWNlO2ZvbnQtc2l6ZToxMHB0O2RvbWluYW50LWJhc2VsaW5lOmNlbnRyYWwiPjY0eDY0PC90ZXh0PjwvZz48L3N2Zz4="})),r.default.createElement("div",{className:"media-body"},r.default.createElement("h4",{className:"media-heading"},e.title),r.default.createElement("p",null,e.text)))}))},c=(0,s.default)(u);(0,o.render)(r.default.createElement(c,null),document.getElementById("app"))},,,,,,,function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var l=function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:I.default;return function(n){var i,f;return f=i=function(i){function u(t){a(this,u);var n=r(this,(u.__proto__||Object.getPrototypeOf(u)).call(this,t));return n._isMounted=!1,n.options=l({container:"undefined"!=typeof window?window:void 0},e),n.state={firstItemIndex:0,lastItemIndex:-1},e&&e.initialState&&(n.state=l({},n.state,e.initialState)),n.refreshState=n.refreshState.bind(n),"undefined"!=typeof window&&"requestAnimationFrame"in window&&(n.refreshState=(0,g.default)(n.refreshState)),n}return o(u,i),s(u,[{key:"setStateIfNeeded",value:function(e,t,n,i,a){var r=(0,v.default)(e,t,n,i,a);void 0!==r&&(r.firstItemIndex>r.lastItemIndex||r.firstItemIndex===this.state.firstItemIndex&&r.lastItemIndex===this.state.lastItemIndex||this.setState(r))}},{key:"refreshState",value:function(){if(this._isMounted){var e=this.props,t=e.itemHeight,n=e.items,i=e.itemBuffer;this.setStateIfNeeded(this.domNode,this.options.container,n,t,i)}}},{key:"componentWillMount",value:function(){this._isMounted=!0}},{key:"componentDidMount",value:function(){this.domNode=m.default.findDOMNode(this),this.refreshState(),this.options.container.addEventListener("scroll",this.refreshState),this.options.container.addEventListener("resize",this.refreshState)}},{key:"componentWillUnmount",value:function(){this._isMounted=!1,this.options.container.removeEventListener("scroll",this.refreshState),this.options.container.removeEventListener("resize",this.refreshState)}},{key:"componentWillReceiveProps",value:function(e){var t=e.itemHeight,n=e.items,i=e.itemBuffer;this.setStateIfNeeded(this.domNode,this.options.container,n,t,i)}},{key:"render",value:function(){return c.default.createElement(n,l({},this.props,t(this.props,this.state)))}}]),u}(u.PureComponent),i.propTypes={items:h.default.array.isRequired,itemHeight:h.default.number.isRequired,itemBuffer:h.default.number},i.defaultProps={itemBuffer:0},f}};t.default=E},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e,t){var n=e.items,i=e.itemHeight,a=t.firstItemIndex,r=t.lastItemIndex,o=r>-1?n.slice(a,r+1):[],l=n.length*i,s=a*i;return{virtual:{items:o,style:{height:l,paddingTop:s,boxSizing:"border-box"}}}};t.default=n},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){return e.pageYOffset?e.pageYOffset:e.document?e.document.documentElement&&e.document.documentElement.scrollTop?e.document.documentElement.scrollTop:e.document.body&&e.document.body.scrollTop?e.document.body.scrollTop:0:e.scrollY||e.scrollTop||0};t.default=n},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(30),r=i(a),o=n(27),l=i(o),s=function(e,t,n,i,a){if(t&&i&&n&&0!==n.length){var o=t.innerHeight,s=t.clientHeight,u=o||s;if(u){var c=(0,l.default)(t),f=c+u,m=(0,r.default)(e)-(0,r.default)(t),d=i*n.length,h=Math.max(0,c-m),p=Math.max(0,Math.min(d,f-m)),v=Math.max(0,Math.floor(h/i)-a),b=Math.min(n.length,Math.ceil(p/i)+a)-1;return{firstItemIndex:v,lastItemIndex:b}}}};t.default=s},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=arguments;t.default=function(e){var t=!1;return function(){t||(t=!0,window.requestAnimationFrame(function(){e.apply(void 0,n),t=!1}))}}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function e(t){return"undefined"!=typeof t&&t?(t.offsetTop||0)+e(t.offsetParent):0};t.default=n},function(e,t,n){e.exports=n.p+"index.html"}]); -------------------------------------------------------------------------------- /demo/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 |
18 |

React Virtual List

19 |
20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | Fork me on GitHub 30 | 31 | -------------------------------------------------------------------------------- /demo/dist/react.js: -------------------------------------------------------------------------------- 1 | !function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n=window.webpackJsonp;window.webpackJsonp=function(a,i){for(var l,u,c=0,s=[];cthis.eventPool.length&&this.eventPool.push(e)}function L(e){e.eventPool=[],e.getPooled=M,e.release=U}function z(e,t){switch(e){case"topKeyUp":return-1!==Bn.indexOf(t.keyCode);case"topKeyDown":return 229!==t.keyCode;case"topKeyPress":case"topMouseDown":case"topBlur":return!0;default:return!1}}function A(e){return e=e.detail,"object"===("undefined"==typeof e?"undefined":cn(e))&&"data"in e?e.data:null}function H(e,t){switch(e){case"topCompositionEnd":return A(t);case"topKeyPress":return 32!==t.which?null:(Yn=!0,qn);case"topTextInput":return e=t.data,e===qn&&Yn?null:e;default:return null}}function j(e,t){if(Xn)return"topCompositionEnd"===e||!Wn&&z(e,t)?(e=F(),zn._root=null,zn._startText=null,zn._fallbackText=null,Xn=!1,e):null;switch(e){case"topPaste":return null;case"topKeyPress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1t}return!1}function ce(e,t,n,r,o){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=o,this.mustUseProperty=n,this.propertyName=e,this.type=t}function se(e){return e[1].toUpperCase()}function fe(e,t,n,r){var o=xr.hasOwnProperty(t)?xr[t]:null,a=null!==o?0===o.type:!r&&(2qr.length&&qr.push(e)}}}function Ke(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n["ms"+e]="MS"+t,n["O"+e]="o"+t.toLowerCase(),n}function Qe(e){if(Jr[e])return Jr[e];if(!Xr[e])return e;var t,n=Xr[e];for(t in n)if(n.hasOwnProperty(t)&&t in Zr)return Jr[e]=n[t];return e}function $e(e){return Object.prototype.hasOwnProperty.call(e,oo)||(e[oo]=ro++,no[e[oo]]={}),no[e[oo]]}function qe(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function Ge(e,t){var n=qe(e);e=0;for(var r;n;){if(3===n.nodeType){if(r=e+n.textContent.length,e<=t&&r>=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=qe(n)}}function Ye(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&"text"===e.type||"textarea"===t||"true"===e.contentEditable)}function Xe(e,t){if(so||null==lo||lo!==hn())return null;var n=lo;return"selectionStart"in n&&Ye(n)?n={start:n.selectionStart,end:n.selectionEnd}:window.getSelection?(n=window.getSelection(),n={anchorNode:n.anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}):n=void 0,co&&mn(co,n)?null:(co=n,e=R.getPooled(io.select,uo,e,t),e.type="select",e.target=lo,N(e),e)}function Je(e,t,n,r){this.tag=e,this.key=n,this.stateNode=this.type=null,this.sibling=this.child=this.return=null,this.index=0,this.ref=null,this.pendingProps=t,this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.effectTag=0,this.lastEffect=this.firstEffect=this.nextEffect=null,this.expirationTime=0,this.alternate=null}function Ze(e,t,n){var r=e.alternate;return null===r?(r=new Je(e.tag,t,e.key,e.mode),r.type=e.type,r.stateNode=e.stateNode,r.alternate=e,e.alternate=r):(r.pendingProps=t,r.effectTag=0,r.nextEffect=null,r.firstEffect=null,r.lastEffect=null),r.expirationTime=n,r.child=e.child,r.memoizedProps=e.memoizedProps,r.memoizedState=e.memoizedState,r.updateQueue=e.updateQueue,r.sibling=e.sibling,r.index=e.index,r.ref=e.ref,r}function et(e,t,n){var o=e.type,a=e.key;e=e.props;var i=void 0;if("function"==typeof o)i=o.prototype&&o.prototype.isReactComponent?2:0;else if("string"==typeof o)i=5;else switch(o){case dr:return tt(e.children,t,n,a);case vr:i=11,t|=3;break;case pr:i=11,t|=2;break;case cr:i=7;break;case sr:i=9;break;default:if("object"===("undefined"==typeof o?"undefined":cn(o))&&null!==o)switch(o.$$typeof){case hr:i=13;break;case mr:i=12;break;case yr:i=14;break;default:if("number"==typeof o.tag)return t=o,t.pendingProps=e,t.expirationTime=n,t;r("130",null==o?o:"undefined"==typeof o?"undefined":cn(o),"")}else r("130",null==o?o:"undefined"==typeof o?"undefined":cn(o),"")}return t=new Je(i,e,a,t),t.type=o,t.expirationTime=n,t}function tt(e,t,n,r){return e=new Je(10,e,r,t),e.expirationTime=n,e}function nt(e,t,n){return e=new Je(6,e,null,t),e.expirationTime=n,e}function rt(e,t,n){return t=new Je(4,null!==e.children?e.children:[],e.key,t),t.expirationTime=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function ot(e){return function(t){try{return e(t)}catch(e){}}}function at(e){if("undefined"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var t=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(t.isDisabled||!t.supportsFiber)return!0;try{var n=t.inject(e);po=ot(function(e){return t.onCommitFiberRoot(n,e)}),ho=ot(function(e){return t.onCommitFiberUnmount(n,e)})}catch(e){}return!0}function it(e){"function"==typeof po&&po(e)}function lt(e){"function"==typeof ho&&ho(e)}function ut(e){return{baseState:e,expirationTime:0,first:null,last:null,callbackList:null,hasForceUpdate:!1,isInitialized:!1,capturedValues:null}}function ct(e,t){null===e.last?e.first=e.last=t:(e.last.next=t,e.last=t),(0===e.expirationTime||e.expirationTime>t.expirationTime)&&(e.expirationTime=t.expirationTime)}function st(e){mo=vo=null;var t=e.alternate,n=e.updateQueue;null===n&&(n=e.updateQueue=ut(null)),null!==t?(e=t.updateQueue,null===e&&(e=t.updateQueue=ut(null))):e=null,mo=n,vo=e!==n?e:null}function ft(e,t){st(e),e=mo;var n=vo;null===n?ct(e,t):null===e.last||null===n.last?(ct(e,t),ct(n,t)):(ct(e,t),n.last=t)}function dt(e,t,n,r){return e=e.partialState,"function"==typeof e?e.call(t,n,r):e}function pt(e,t,n,r,o,a){null!==e&&e.updateQueue===n&&(n=t.updateQueue={baseState:n.baseState,expirationTime:n.expirationTime,first:n.first,last:n.last,isInitialized:n.isInitialized,capturedValues:n.capturedValues,callbackList:null,hasForceUpdate:!1}),n.expirationTime=0,n.isInitialized?e=n.baseState:(e=n.baseState=t.memoizedState,n.isInitialized=!0);for(var i=!0,l=n.first,u=!1;null!==l;){var c=l.expirationTime;if(c>a){var s=n.expirationTime;(0===s||s>c)&&(n.expirationTime=c),u||(u=!0,n.baseState=e)}else u||(n.first=l.next,null===n.first&&(n.last=null)),l.isReplace?(e=dt(l,r,e,o),i=!0):(c=dt(l,r,e,o))&&(e=i?dn({},e,c):dn(e,c),i=!1),l.isForced&&(n.hasForceUpdate=!0),null!==l.callback&&(c=n.callbackList,null===c&&(c=n.callbackList=[]),c.push(l)),null!==l.capturedValue&&(c=n.capturedValues,null===c?n.capturedValues=[l.capturedValue]:c.push(l.capturedValue));l=l.next}return null!==n.callbackList?t.effectTag|=32:null!==n.first||n.hasForceUpdate||null!==n.capturedValues||(t.updateQueue=null),u||(n.baseState=e),e}function ht(e,t){var n=e.callbackList;if(null!==n)for(e.callbackList=null,e=0;em?(v=f,f=null):v=f.sibling;var y=p(r,f,l[m],u);if(null===y){null===f&&(f=v);break}e&&f&&null===y.alternate&&t(r,f),a=i(y,a,m),null===s?c=y:s.sibling=y,s=y,f=v}if(m===l.length)return n(r,f),c;if(null===f){for(;mv?(y=m,m=null):y=m.sibling;var b=p(a,m,g.value,c);if(null===b){m||(m=y);break}e&&m&&null===b.alternate&&t(a,m),l=i(b,l,v),null===f?s=b:f.sibling=b,f=b,m=y}if(g.done)return n(a,m),s;if(null===m){for(;!g.done;v++,g=u.next())g=d(a,g.value,c),null!==g&&(l=i(g,l,v),null===f?s=g:f.sibling=g,f=g);return s}for(m=o(a,m);!g.done;v++,g=u.next())g=h(m,a,v,g.value,c),null!==g&&(e&&null!==g.alternate&&m.delete(null===g.key?v:g.key),l=i(g,l,v),null===f?s=g:f.sibling=g,f=g);return e&&m.forEach(function(e){return t(a,e)}),s}return function(e,o,i,u){"object"===("undefined"==typeof i?"undefined":cn(i))&&null!==i&&i.type===dr&&null===i.key&&(i=i.props.children);var c="object"===("undefined"==typeof i?"undefined":cn(i))&&null!==i;if(c)switch(i.$$typeof){case ur:e:{var s=i.key;for(c=o;null!==c;){if(c.key===s){if(10===c.tag?i.type===dr:c.type===i.type){n(e,c.sibling),o=a(c,i.type===dr?i.props.children:i.props,u),o.ref=vt(e,c,i),o.return=e,e=o;break e}n(e,c);break}t(e,c),c=c.sibling}i.type===dr?(o=tt(i.props.children,e.mode,u,i.key),o.return=e,e=o):(u=et(i,e.mode,u),u.ref=vt(e,o,i),u.return=e,e=u)}return l(e);case fr:e:{for(c=i.key;null!==o;){if(o.key===c){if(4===o.tag&&o.stateNode.containerInfo===i.containerInfo&&o.stateNode.implementation===i.implementation){n(e,o.sibling),o=a(o,i.children||[],u),o.return=e,e=o;break e}n(e,o);break}t(e,o),o=o.sibling}o=rt(i,e.mode,u),o.return=e,e=o}return l(e)}if("string"==typeof i||"number"==typeof i)return i=""+i,null!==o&&6===o.tag?(n(e,o.sibling),o=a(o,i,u)):(n(e,o),o=nt(i,e.mode,u)),o.return=e,e=o,l(e);if(yo(i))return m(e,o,i,u);if(re(i))return v(e,o,i,u);if(c&&yt(e,i),"undefined"==typeof i)switch(e.tag){case 2:case 1:u=e.type,r("152",u.displayName||u.name||"Component")}return n(e,o)}}function bt(e,t,n,o,a,i,l){function u(e,t,n){c(e,t,n,t.expirationTime)}function c(e,t,n,r){t.child=null===e?bo(t,null,n,r):go(t,e.child,n,r)}function s(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.effectTag|=128)}function f(e,t,n,r,o,a){if(s(e,t),!n&&!o)return r&&E(t,!1),m(e,t);n=t.stateNode,ir.current=t;var i=o?null:n.render();return t.effectTag|=1,o&&(c(e,t,null,a),t.child=null),c(e,t,i,a),t.memoizedState=n.state,t.memoizedProps=n.props,r&&E(t,!0),t.child}function d(e){var t=e.stateNode;t.pendingContext?S(e,t.pendingContext,t.pendingContext!==t.context):t.context&&S(e,t.context,!1),b(e,t.containerInfo)}function p(e,t,n,r){var o=e.child;for(null!==o&&(o.return=e);null!==o;){switch(o.tag){case 12:var a=0|o.stateNode;if(o.type===t&&0!==(a&n)){for(a=o;null!==a;){var i=a.alternate; 15 | if(0===a.expirationTime||a.expirationTime>r)a.expirationTime=r,null!==i&&(0===i.expirationTime||i.expirationTime>r)&&(i.expirationTime=r);else{if(null===i||!(0===i.expirationTime||i.expirationTime>r))break;i.expirationTime=r}a=a.return}a=null}else a=o.child;break;case 13:a=o.type===e.type?null:o.child;break;default:a=o.child}if(null!==a)a.return=o;else for(a=o;null!==a;){if(a===e){a=null;break}if(o=a.sibling,null!==o){a=o;break}a=a.return}o=a}}function h(e,t,n){var r=t.type._context,o=t.pendingProps,a=t.memoizedProps;if(!w()&&a===o)return t.stateNode=0,C(t),m(e,t);var i=o.value;if(t.memoizedProps=o,null===a)i=1073741823;else if(a.value===o.value){if(a.children===o.children)return t.stateNode=0,C(t),m(e,t);i=0}else{var l=a.value;if(l===i&&(0!==l||1/l===1/i)||l!==l&&i!==i){if(a.children===o.children)return t.stateNode=0,C(t),m(e,t);i=0}else if(i="function"==typeof r._calculateChangedBits?r._calculateChangedBits(l,i):1073741823,i|=0,0===i){if(a.children===o.children)return t.stateNode=0,C(t),m(e,t)}else p(t,r,i,n)}return t.stateNode=i,C(t),u(e,t,o.children),t.child}function m(e,t){if(null!==e&&t.child!==e.child?r("153"):void 0,null!==t.child){e=t.child;var n=Ze(e,e.pendingProps,e.expirationTime);for(t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,n=n.sibling=Ze(e,e.pendingProps,e.expirationTime),n.return=t;n.sibling=null}return t.child}var v=e.shouldSetTextContent,y=e.shouldDeprioritizeSubtree,g=t.pushHostContext,b=t.pushHostContainer,C=o.pushProvider,k=n.getMaskedContext,x=n.getUnmaskedContext,w=n.hasContextChanged,T=n.pushContextProvider,S=n.pushTopLevelContextObject,E=n.invalidateContextProvider,_=a.enterHydrationState,P=a.resetHydrationState,N=a.tryToClaimNextHydratableInstance;e=mt(n,i,l,function(e,t){e.memoizedProps=t},function(e,t){e.memoizedState=t});var I=e.adoptClassInstance,O=e.callGetDerivedStateFromProps,F=e.constructClassInstance,D=e.mountClassInstance,R=e.resumeMountClassInstance,M=e.updateClassInstance;return{beginWork:function(e,t,n){if(0===t.expirationTime||t.expirationTime>n){switch(t.tag){case 3:d(t);break;case 2:T(t);break;case 4:b(t,t.stateNode.containerInfo);break;case 13:C(t)}return null}switch(t.tag){case 0:null!==e?r("155"):void 0;var o=t.type,a=t.pendingProps,i=x(t);return i=k(t,i),o=o(a,i),t.effectTag|=1,"object"===("undefined"==typeof o?"undefined":cn(o))&&null!==o&&"function"==typeof o.render&&void 0===o.$$typeof?(i=t.type,t.tag=2,t.memoizedState=null!==o.state&&void 0!==o.state?o.state:null,"function"==typeof i.getDerivedStateFromProps&&(a=O(t,o,a,t.memoizedState),null!==a&&void 0!==a&&(t.memoizedState=dn({},t.memoizedState,a))),a=T(t),I(t,o),D(t,n),e=f(e,t,!0,a,!1,n)):(t.tag=1,u(e,t,o),t.memoizedProps=a,e=t.child),e;case 1:return a=t.type,n=t.pendingProps,w()||t.memoizedProps!==n?(o=x(t),o=k(t,o),a=a(n,o),t.effectTag|=1,u(e,t,a),t.memoizedProps=n,e=t.child):e=m(e,t),e;case 2:a=T(t),null===e?null===t.stateNode?(F(t,t.pendingProps),D(t,n),o=!0):o=R(t,n):o=M(e,t,n),i=!1;var l=t.updateQueue;return null!==l&&null!==l.capturedValues&&(i=o=!0),f(e,t,o,a,i,n);case 3:e:if(d(t),o=t.updateQueue,null!==o){if(i=t.memoizedState,a=pt(e,t,o,null,null,n),t.memoizedState=a,o=t.updateQueue,null!==o&&null!==o.capturedValues)o=null;else{if(i===a){P(),e=m(e,t);break e}o=a.element}i=t.stateNode,(null===e||null===e.child)&&i.hydrate&&_(t)?(t.effectTag|=2,t.child=bo(t,null,o,n)):(P(),u(e,t,o)),t.memoizedState=a,e=t.child}else P(),e=m(e,t);return e;case 5:return g(t),null===e&&N(t),a=t.type,l=t.memoizedProps,o=t.pendingProps,i=null!==e?e.memoizedProps:null,w()||l!==o||((l=1&t.mode&&y(a,o))&&(t.expirationTime=1073741823),l&&1073741823===n)?(l=o.children,v(a,o)?l=null:i&&v(a,i)&&(t.effectTag|=16),s(e,t),1073741823!==n&&1&t.mode&&y(a,o)?(t.expirationTime=1073741823,t.memoizedProps=o,e=null):(u(e,t,l),t.memoizedProps=o,e=t.child)):e=m(e,t),e;case 6:return null===e&&N(t),t.memoizedProps=t.pendingProps,null;case 8:t.tag=7;case 7:return a=t.pendingProps,w()||t.memoizedProps!==a||(a=t.memoizedProps),o=a.children,t.stateNode=null===e?bo(t,t.stateNode,o,n):go(t,e.stateNode,o,n),t.memoizedProps=a,t.stateNode;case 9:return null;case 4:return b(t,t.stateNode.containerInfo),a=t.pendingProps,w()||t.memoizedProps!==a?(null===e?t.child=go(t,null,a,n):u(e,t,a),t.memoizedProps=a,e=t.child):e=m(e,t),e;case 14:return n=t.type.render,n=n(t.pendingProps,t.ref),u(e,t,n),t.memoizedProps=n,t.child;case 10:return n=t.pendingProps,w()||t.memoizedProps!==n?(u(e,t,n),t.memoizedProps=n,e=t.child):e=m(e,t),e;case 11:return n=t.pendingProps.children,w()||null!==n&&t.memoizedProps!==n?(u(e,t,n),t.memoizedProps=n,e=t.child):e=m(e,t),e;case 13:return h(e,t,n);case 12:o=t.type,i=t.pendingProps;var c=t.memoizedProps;return a=o._currentValue,l=o._changedBits,w()||0!==l||c!==i?(t.memoizedProps=i,c=i.unstable_observedBits,void 0!==c&&null!==c||(c=1073741823),t.stateNode=c,0!==(l&c)&&p(t,o,l,n),n=i.children,n=n(a),u(e,t,n),e=t.child):e=m(e,t),e;default:r("156")}}}}function Ct(e,t,n,o,a){function i(e){e.effectTag|=4}var l=e.createInstance,u=e.createTextInstance,c=e.appendInitialChild,s=e.finalizeInitialChildren,f=e.prepareUpdate,d=e.persistence,p=t.getRootHostContainer,h=t.popHostContext,m=t.getHostContext,v=t.popHostContainer,y=n.popContextProvider,g=n.popTopLevelContextObject,b=o.popProvider,C=a.prepareToHydrateHostInstance,k=a.prepareToHydrateHostTextInstance,x=a.popHydrationState,w=void 0,T=void 0,S=void 0;return e.mutation?(w=function(){},T=function(e,t,n){(t.updateQueue=n)&&i(t)},S=function(e,t,n,r){n!==r&&i(t)}):r(d?"235":"236"),{completeWork:function(e,t,n){var o=t.pendingProps;switch(t.tag){case 1:return null;case 2:return y(t),e=t.stateNode,o=t.updateQueue,null!==o&&null!==o.capturedValues&&(t.effectTag&=-65,"function"==typeof e.componentDidCatch?t.effectTag|=256:o.capturedValues=null),null;case 3:return v(t),g(t),o=t.stateNode,o.pendingContext&&(o.context=o.pendingContext,o.pendingContext=null),null!==e&&null!==e.child||(x(t),t.effectTag&=-3),w(t),e=t.updateQueue,null!==e&&null!==e.capturedValues&&(t.effectTag|=256),null;case 5:h(t),n=p();var a=t.type;if(null!==e&&null!=t.stateNode){var d=e.memoizedProps,E=t.stateNode,_=m();E=f(E,a,d,o,n,_),T(e,t,E,a,d,o,n,_),e.ref!==t.ref&&(t.effectTag|=128)}else{if(!o)return null===t.stateNode?r("166"):void 0,null;if(e=m(),x(t))C(t,n,e)&&i(t);else{d=l(a,o,n,e,t);e:for(_=t.child;null!==_;){if(5===_.tag||6===_.tag)c(d,_.stateNode);else if(4!==_.tag&&null!==_.child){_.child.return=_,_=_.child;continue}if(_===t)break;for(;null===_.sibling;){if(null===_.return||_.return===t)break e;_=_.return}_.sibling.return=_.return,_=_.sibling}s(d,a,o,n,e)&&i(t),t.stateNode=d}null!==t.ref&&(t.effectTag|=128)}return null;case 6:if(e&&null!=t.stateNode)S(e,t,e.memoizedProps,o);else{if("string"!=typeof o)return null===t.stateNode?r("166"):void 0,null;e=p(),n=m(),x(t)?k(t)&&i(t):t.stateNode=u(o,e,n,t)}return null;case 7:(o=t.memoizedProps)?void 0:r("165"),t.tag=8,a=[];e:for((d=t.stateNode)&&(d.return=t);null!==d;){if(5===d.tag||6===d.tag||4===d.tag)r("247");else if(9===d.tag)a.push(d.pendingProps.value);else if(null!==d.child){d.child.return=d,d=d.child;continue}for(;null===d.sibling;){if(null===d.return||d.return===t)break e;d=d.return}d.sibling.return=d.return,d=d.sibling}return d=o.handler,o=d(o.props,a),t.child=go(t,null!==e?e.child:null,o,n),t.child;case 8:return t.tag=7,null;case 9:return null;case 14:return null;case 10:return null;case 11:return null;case 4:return v(t),w(t),null;case 13:return b(t),null;case 12:return null;case 0:r("167");default:r("156")}}}}function kt(e,t,n,r,o){var a=e.popHostContainer,i=e.popHostContext,l=t.popContextProvider,u=t.popTopLevelContextObject,c=n.popProvider;return{throwException:function(e,t,n){t.effectTag|=512,t.firstEffect=t.lastEffect=null,t={value:n,source:t,stack:ae(t)};do{switch(e.tag){case 3:return st(e),e.updateQueue.capturedValues=[t],void(e.effectTag|=1024);case 2:if(n=e.stateNode,0===(64&e.effectTag)&&null!==n&&"function"==typeof n.componentDidCatch&&!o(n)){st(e),n=e.updateQueue;var r=n.capturedValues;return null===r?n.capturedValues=[t]:r.push(t),void(e.effectTag|=1024)}}e=e.return}while(null!==e)},unwindWork:function(e){switch(e.tag){case 2:l(e);var t=e.effectTag;return 1024&t?(e.effectTag=t&-1025|64,e):null;case 3:return a(e),u(e),t=e.effectTag,1024&t?(e.effectTag=t&-1025|64,e):null;case 5:return i(e),null;case 4:return a(e),null;case 13:return c(e),null;default:return null}},unwindInterruptedWork:function(e){switch(e.tag){case 2:l(e);break;case 3:a(e),u(e);break;case 5:i(e);break;case 4:a(e);break;case 13:c(e)}}}}function xt(e,t){var n=t.source;null===t.stack&&ae(n),null!==n&&oe(n),t=t.value,null!==e&&2===e.tag&&oe(e);try{t&&t.suppressReactErrorLogging||console.error(t)}catch(e){e&&e.suppressReactErrorLogging||console.error(e)}}function wt(e,t,n,o,a){function i(e){var n=e.ref;if(null!==n)if("function"==typeof n)try{n(null)}catch(n){t(e,n)}else n.current=null}function l(e){switch("function"==typeof lt&<(e),e.tag){case 2:i(e);var n=e.stateNode;if("function"==typeof n.componentWillUnmount)try{n.props=e.memoizedProps,n.state=e.memoizedState,n.componentWillUnmount()}catch(n){t(e,n)}break;case 5:i(e);break;case 7:u(e.stateNode);break;case 4:d&&s(e)}}function u(e){for(var t=e;;)if(l(t),null===t.child||d&&4===t.tag){if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return;t=t.return}t.sibling.return=t.return,t=t.sibling}else t.child.return=t,t=t.child}function c(e){return 5===e.tag||3===e.tag||4===e.tag}function s(e){for(var t=e,n=!1,o=void 0,a=void 0;;){if(!n){n=t.return;e:for(;;){switch(null===n?r("160"):void 0,n.tag){case 5:o=n.stateNode,a=!1;break e;case 3:o=n.stateNode.containerInfo,a=!0;break e;case 4:o=n.stateNode.containerInfo,a=!0;break e}n=n.return}n=!0}if(5===t.tag||6===t.tag)u(t),a?x(o,t.stateNode):k(o,t.stateNode);else if(4===t.tag?o=t.stateNode.containerInfo:l(t),null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return;t=t.return,4===t.tag&&(n=!1)}t.sibling.return=t.return,t=t.sibling}}var f=e.getPublicInstance,d=e.mutation;e=e.persistence,d||r(e?"235":"236");var p=d.commitMount,h=d.commitUpdate,m=d.resetTextContent,v=d.commitTextUpdate,y=d.appendChild,g=d.appendChildToContainer,b=d.insertBefore,C=d.insertInContainerBefore,k=d.removeChild,x=d.removeChildFromContainer;return{commitBeforeMutationLifeCycles:function(e,t){switch(t.tag){case 2:if(2048&t.effectTag&&null!==e){var n=e.memoizedProps,o=e.memoizedState;e=t.stateNode,e.props=t.memoizedProps,e.state=t.memoizedState,t=e.getSnapshotBeforeUpdate(n,o),e.__reactInternalSnapshotBeforeUpdate=t}break;case 3:case 5:case 6:case 4:break;default:r("163")}},commitResetTextContent:function(e){m(e.stateNode)},commitPlacement:function(e){e:{for(var t=e.return;null!==t;){if(c(t)){var n=t;break e}t=t.return}r("160"),n=void 0}var o=t=void 0;switch(n.tag){case 5:t=n.stateNode,o=!1;break;case 3:t=n.stateNode.containerInfo,o=!0;break;case 4:t=n.stateNode.containerInfo,o=!0;break;default:r("161")}16&n.effectTag&&(m(t),n.effectTag&=-17);e:t:for(n=e;;){for(;null===n.sibling;){if(null===n.return||c(n.return)){n=null;break e}n=n.return}for(n.sibling.return=n.return,n=n.sibling;5!==n.tag&&6!==n.tag;){if(2&n.effectTag)continue t;if(null===n.child||4===n.tag)continue t;n.child.return=n,n=n.child}if(!(2&n.effectTag)){n=n.stateNode;break e}}for(var a=e;;){if(5===a.tag||6===a.tag)n?o?C(t,a.stateNode,n):b(t,a.stateNode,n):o?g(t,a.stateNode):y(t,a.stateNode);else if(4!==a.tag&&null!==a.child){a.child.return=a,a=a.child;continue}if(a===e)break;for(;null===a.sibling;){if(null===a.return||a.return===e)return;a=a.return}a.sibling.return=a.return,a=a.sibling}},commitDeletion:function(e){s(e),e.return=null,e.child=null,e.alternate&&(e.alternate.child=null,e.alternate.return=null)},commitWork:function(e,t){switch(t.tag){case 2:break;case 5:var n=t.stateNode;if(null!=n){var o=t.memoizedProps;e=null!==e?e.memoizedProps:o;var a=t.type,i=t.updateQueue;t.updateQueue=null,null!==i&&h(n,i,a,e,o,t)}break;case 6:null===t.stateNode?r("162"):void 0,n=t.memoizedProps,v(t.stateNode,null!==e?e.memoizedProps:n,n);break;case 3:break;default:r("163")}},commitLifeCycles:function(e,t,n){switch(n.tag){case 2:if(e=n.stateNode,4&n.effectTag)if(null===t)e.props=n.memoizedProps,e.state=n.memoizedState,e.componentDidMount();else{var o=t.memoizedProps;t=t.memoizedState,e.props=n.memoizedProps,e.state=n.memoizedState,e.componentDidUpdate(o,t,e.__reactInternalSnapshotBeforeUpdate)}n=n.updateQueue,null!==n&&ht(n,e);break;case 3:if(t=n.updateQueue,null!==t){if(e=null,null!==n.child)switch(n.child.tag){case 5:e=f(n.child.stateNode);break;case 2:e=n.child.stateNode}ht(t,e)}break;case 5:e=n.stateNode,null===t&&4&n.effectTag&&p(e,n.type,n.memoizedProps,n);break;case 6:break;case 4:break;default:r("163")}},commitErrorLogging:function(e,t){switch(e.tag){case 2:var n=e.type;t=e.stateNode;var o=e.updateQueue;null===o||null===o.capturedValues?r("264"):void 0;var i=o.capturedValues;for(o.capturedValues=null,"function"!=typeof n.getDerivedStateFromCatch&&a(t),t.props=e.memoizedProps,t.state=e.memoizedState,n=0;nt||(n.current=e[t],e[t]=null,t--)},push:function(n,r){t++,e[t]=n.current,n.current=r},checkThatStackIsEmpty:function(){},resetStackAfterFatalErrorInDev:function(){}}}function Nt(e){function t(){if(null!==Z)for(var e=Z.return;null!==e;)F(e),e=e.return;ee=null,te=0,Z=null,oe=!1}function n(e){return null!==ie&&ie.has(e)}function o(e){for(;;){var t=e.alternate,n=e.return,r=e.sibling;if(0===(512&e.effectTag)){t=N(t,e,te);var o=e;if(1073741823===te||1073741823!==o.expirationTime){e:switch(o.tag){case 3:case 2:var a=o.updateQueue;a=null===a?0:a.expirationTime;break e;default:a=0}for(var i=o.child;null!==i;)0!==i.expirationTime&&(0===a||a>i.expirationTime)&&(a=i.expirationTime),i=i.sibling;o.expirationTime=a}if(null!==t)return t;if(null!==n&&0===(512&n.effectTag)&&(null===n.firstEffect&&(n.firstEffect=e.firstEffect),null!==e.lastEffect&&(null!==n.lastEffect&&(n.lastEffect.nextEffect=e.firstEffect),n.lastEffect=e.lastEffect),1he)&&(he=e),e}function s(e,n){e:{for(;null!==e;){if((0===e.expirationTime||e.expirationTime>n)&&(e.expirationTime=n),null!==e.alternate&&(0===e.alternate.expirationTime||e.alternate.expirationTime>n)&&(e.alternate.expirationTime=n),null===e.return){if(3!==e.tag){n=void 0;break e}var o=e.stateNode;!J&&0!==te&&nwe&&r("185")}e=e.return}n=void 0}return n}function f(){return G=V()-$,q=(G/10|0)+2}function d(e,t,n,r,o){var a=X;X=1;try{return e(t,n,r,o)}finally{X=a}}function p(e){if(0!==ce){if(e>ce)return;W(se)}var t=V()-$;ce=e,se=B(v,{timeout:10*(e-2)-t})}function h(e,t){if(null===e.nextScheduledRoot)e.remainingExpirationTime=t,null===ue?(le=ue=e,e.nextScheduledRoot=e):(ue=ue.nextScheduledRoot=e,ue.nextScheduledRoot=le);else{var n=e.remainingExpirationTime;(0===n||t=pe)&&(!me||f()>=pe);)C(de,pe,!me),m();else for(;null!==de&&0!==pe&&(0===e||e>=pe);)C(de,pe,!1),m();null!==ge&&(ce=0,se=-1),0!==pe&&p(pe),ge=null,me=!1,b()}function b(){if(Te=0,null!==xe){var e=xe;xe=null;for(var t=0;tSe)&&(me=!0)}function w(e){null===de?r("246"):void 0,de.remainingExpirationTime=0,ve||(ve=!0,ye=e)}var T=Pt(),S=Tt(e,T),E=Et(T);T=_t(T);var _=St(e),P=bt(e,S,E,T,_,s,c).beginWork,N=Ct(e,S,E,T,_).completeWork;S=kt(S,E,T,s,n);var I=S.throwException,O=S.unwindWork,F=S.unwindInterruptedWork;S=wt(e,u,s,c,function(e){null===ie?ie=new Set([e]):ie.add(e)},f);var D=S.commitBeforeMutationLifeCycles,R=S.commitResetTextContent,M=S.commitPlacement,U=S.commitDeletion,L=S.commitWork,z=S.commitLifeCycles,A=S.commitErrorLogging,H=S.commitAttachRef,j=S.commitDetachRef,V=e.now,B=e.scheduleDeferredCallback,W=e.cancelDeferredCallback,K=e.prepareForCommit,Q=e.resetAfterCommit,$=V(),q=2,G=$,Y=0,X=0,J=!1,Z=null,ee=null,te=0,ne=null,re=!1,oe=!1,ie=null,le=null,ue=null,ce=0,se=-1,fe=!1,de=null,pe=0,he=0,me=!1,ve=!1,ye=null,ge=null,be=!1,Ce=!1,ke=!1,xe=null,we=1e3,Te=0,Se=1;return{recalculateCurrentTime:f,computeExpirationForFiber:c,scheduleWork:s,requestWork:h,flushRoot:function(e,t){fe?r("253"):void 0,de=e,pe=t,C(e,t,!1),y(),b()},batchedUpdates:function(e,t){var n=be;be=!0;try{return e(t)}finally{(be=n)||fe||y()}},unbatchedUpdates:function(e,t){if(be&&!Ce){Ce=!0;try{return e(t)}finally{Ce=!1}}return e(t)},flushSync:function(e,t){fe?r("187"):void 0;var n=be;be=!0;try{return d(e,t)}finally{be=n,y()}},flushControlled:function(e){var t=be;be=!0;try{d(e)}finally{(be=t)||fe||g(1,!1,null)}},deferredUpdates:function(e){var t=X;X=25*(((f()+500)/25|0)+1);try{return e()}finally{X=t}},syncUpdates:d,interactiveUpdates:function(e,t,n){if(ke)return e(t,n);be||fe||0===he||(g(he,!1,null),he=0);var r=ke,o=be;be=ke=!0;try{return e(t,n)}finally{ke=r,(be=o)||fe||y()}},flushInteractiveUpdates:function(){fe||0===he||(g(he,!1,null),he=0)},computeUniqueAsyncExpiration:function(){var e=25*(((f()+500)/25|0)+1);return e<=Y&&(e=Y+1),Y=e},legacyContext:E}}function It(e){function t(e,t,n,r,o,a){if(r=t.current,n){n=n._reactInternalFiber;var l=u(n);n=c(n)?s(n,l):l}else n=yn;return null===t.context?t.context=n:t.pendingContext=n,t=a,ft(r,{expirationTime:o,partialState:{element:e},callback:void 0===t?null:t,isReplace:!1,isForced:!1,capturedValue:null,next:null}),i(r,o),o}function n(e){return e=Me(e),null===e?null:e.stateNode}var r=e.getPublicInstance;e=Nt(e);var o=e.recalculateCurrentTime,a=e.computeExpirationForFiber,i=e.scheduleWork,l=e.legacyContext,u=l.findCurrentUnmaskedContext,c=l.isContextProvider,s=l.processChildContext;return{createContainer:function(e,t,n){return t=new Je(3,null,null,t?3:0),e={current:t,containerInfo:e,pendingChildren:null,pendingCommitExpirationTime:0,finishedWork:null,context:null,pendingContext:null,hydrate:n,remainingExpirationTime:0,firstBatch:null,nextScheduledRoot:null},t.stateNode=e},updateContainer:function(e,n,r,i){var l=n.current,u=o();return l=a(l),t(e,n,r,u,l,i)},updateContainerAtExpirationTime:function(e,n,r,a,i){var l=o();return t(e,n,r,l,a,i)},flushRoot:e.flushRoot,requestWork:e.requestWork,computeUniqueAsyncExpiration:e.computeUniqueAsyncExpiration,batchedUpdates:e.batchedUpdates,unbatchedUpdates:e.unbatchedUpdates,deferredUpdates:e.deferredUpdates,syncUpdates:e.syncUpdates,interactiveUpdates:e.interactiveUpdates,flushInteractiveUpdates:e.flushInteractiveUpdates,flushControlled:e.flushControlled,flushSync:e.flushSync,getPublicRootInstance:function(e){if(e=e.current,!e.child)return null;switch(e.child.tag){case 5:return r(e.child.stateNode);default:return e.child.stateNode}},findHostInstance:n,findHostInstanceWithNoPortals:function(e){return e=Ue(e),null===e?null:e.stateNode},injectIntoDevTools:function(e){var t=e.findFiberByHostInstance;return at(dn({},e,{findHostInstanceByFiber:function(e){return n(e)},findFiberByHostInstance:function(e){return t?t(e):null}}))}}}function Ot(e,t,n){var r=3=t.length?void 0:r("93"),t=t[0]),n=""+t),null==n&&(n="")),e._wrapperState={initialValue:""+n}}function zt(e,t){var n=t.value;null!=n&&(n=""+n,n!==e.value&&(e.value=n),null==t.defaultValue&&(e.defaultValue=n)),null!=t.defaultValue&&(e.defaultValue=t.defaultValue)}function At(e){var t=e.textContent;t===e._wrapperState.initialValue&&(e.value=t)}function Ht(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";case"math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function jt(e,t){return null==e||"http://www.w3.org/1999/xhtml"===e?Ht(t):"http://www.w3.org/2000/svg"===e&&"foreignObject"===t?"http://www.w3.org/1999/xhtml":e}function Vt(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t}function Bt(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=0===n.indexOf("--"),o=n,a=t[n];o=null==a||"boolean"==typeof a||""===a?"":r||"number"!=typeof a||0===a||jo.hasOwnProperty(o)&&jo[o]?(""+a).trim():a+"px","float"===n&&(n="cssFloat"),r?e.setProperty(n,o):e[n]=o}}function Wt(e,t,n){t&&(Bo[e]&&(null!=t.children||null!=t.dangerouslySetInnerHTML?r("137",e,n()):void 0),null!=t.dangerouslySetInnerHTML&&(null!=t.children?r("60"):void 0,"object"===cn(t.dangerouslySetInnerHTML)&&"__html"in t.dangerouslySetInnerHTML?void 0:r("61")),null!=t.style&&"object"!==cn(t.style)?r("62",n()):void 0)}function Kt(e,t){if(-1===e.indexOf("-"))return"string"==typeof t.is;switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}function Qt(e,t){e=9===e.nodeType||11===e.nodeType?e:e.ownerDocument;var n=$e(e);t=Tn[t];for(var r=0;r",e=e.removeChild(e.firstChild)):e="string"==typeof t.is?n.createElement(e,{is:t.is}):n.createElement(e):e=n.createElementNS(r,e),e}function qt(e,t){return(9===t.nodeType?t:t.ownerDocument).createTextNode(e)}function Gt(e,t,n,r){var o=Kt(t,n);switch(t){case"iframe":case"object":je("topLoad","load",e);var a=n;break;case"video":case"audio":for(a in to)to.hasOwnProperty(a)&&je(a,to[a],e);a=n;break;case"source":je("topError","error",e),a=n;break;case"img":case"image":case"link":je("topError","error",e),je("topLoad","load",e),a=n;break;case"form": 16 | je("topReset","reset",e),je("topSubmit","submit",e),a=n;break;case"details":je("topToggle","toggle",e),a=n;break;case"input":pe(e,n),a=de(e,n),je("topInvalid","invalid",e),Qt(r,"onChange");break;case"option":a=Dt(e,n);break;case"select":Mt(e,n),a=dn({},n,{value:void 0}),je("topInvalid","invalid",e),Qt(r,"onChange");break;case"textarea":Lt(e,n),a=Ut(e,n),je("topInvalid","invalid",e),Qt(r,"onChange");break;default:a=n}Wt(t,a,Ko);var i,l=a;for(i in l)if(l.hasOwnProperty(i)){var u=l[i];"style"===i?Bt(e,u,Ko):"dangerouslySetInnerHTML"===i?(u=u?u.__html:void 0,null!=u&&Ho(e,u)):"children"===i?"string"==typeof u?("textarea"!==t||""!==u)&&Vt(e,u):"number"==typeof u&&Vt(e,""+u):"suppressContentEditableWarning"!==i&&"suppressHydrationWarning"!==i&&"autoFocus"!==i&&(wn.hasOwnProperty(i)?null!=u&&Qt(r,i):null!=u&&fe(e,i,u,o))}switch(t){case"input":te(e),ve(e,n);break;case"textarea":te(e),At(e,n);break;case"option":null!=n.value&&e.setAttribute("value",n.value);break;case"select":e.multiple=!!n.multiple,t=n.value,null!=t?Rt(e,!!n.multiple,t,!1):null!=n.defaultValue&&Rt(e,!!n.multiple,n.defaultValue,!0);break;default:"function"==typeof a.onClick&&(e.onclick=pn)}}function Yt(e,t,n,r,o){var a=null;switch(t){case"input":n=de(e,n),r=de(e,r),a=[];break;case"option":n=Dt(e,n),r=Dt(e,r),a=[];break;case"select":n=dn({},n,{value:void 0}),r=dn({},r,{value:void 0}),a=[];break;case"textarea":n=Ut(e,n),r=Ut(e,r),a=[];break;default:"function"!=typeof n.onClick&&"function"==typeof r.onClick&&(e.onclick=pn)}Wt(t,r,Ko),t=e=void 0;var i=null;for(e in n)if(!r.hasOwnProperty(e)&&n.hasOwnProperty(e)&&null!=n[e])if("style"===e){var l=n[e];for(t in l)l.hasOwnProperty(t)&&(i||(i={}),i[t]="")}else"dangerouslySetInnerHTML"!==e&&"children"!==e&&"suppressContentEditableWarning"!==e&&"suppressHydrationWarning"!==e&&"autoFocus"!==e&&(wn.hasOwnProperty(e)?a||(a=[]):(a=a||[]).push(e,null));for(e in r){var u=r[e];if(l=null!=n?n[e]:void 0,r.hasOwnProperty(e)&&u!==l&&(null!=u||null!=l))if("style"===e)if(l){for(t in l)!l.hasOwnProperty(t)||u&&u.hasOwnProperty(t)||(i||(i={}),i[t]="");for(t in u)u.hasOwnProperty(t)&&l[t]!==u[t]&&(i||(i={}),i[t]=u[t])}else i||(a||(a=[]),a.push(e,i)),i=u;else"dangerouslySetInnerHTML"===e?(u=u?u.__html:void 0,l=l?l.__html:void 0,null!=u&&l!==u&&(a=a||[]).push(e,""+u)):"children"===e?l===u||"string"!=typeof u&&"number"!=typeof u||(a=a||[]).push(e,""+u):"suppressContentEditableWarning"!==e&&"suppressHydrationWarning"!==e&&(wn.hasOwnProperty(e)?(null!=u&&Qt(o,e),a||l===u||(a=[])):(a=a||[]).push(e,u))}return i&&(a=a||[]).push("style",i),a}function Xt(e,t,n,r,o){"input"===n&&"radio"===o.type&&null!=o.name&&he(e,o),Kt(n,r),r=Kt(n,o);for(var a=0;a=Kn),qn=String.fromCharCode(32),Gn={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["topCompositionEnd","topKeyPress","topTextInput","topPaste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"topBlur topCompositionEnd topKeyDown topKeyPress topKeyUp topMouseDown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"topBlur topCompositionStart topKeyDown topKeyPress topKeyUp topMouseDown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"topBlur topCompositionUpdate topKeyDown topKeyPress topKeyUp topMouseDown".split(" ")}},Yn=!1,Xn=!1,Jn={eventTypes:Gn,extractEvents:function(e,t,n,r){var o=void 0,a=void 0;if(Wn)e:{switch(e){case"topCompositionStart":o=Gn.compositionStart;break e;case"topCompositionEnd":o=Gn.compositionEnd;break e;case"topCompositionUpdate":o=Gn.compositionUpdate;break e}o=void 0}else Xn?z(e,n)&&(o=Gn.compositionEnd):"topKeyDown"===e&&229===n.keyCode&&(o=Gn.compositionStart);return o?($n&&(Xn||o!==Gn.compositionStart?o===Gn.compositionEnd&&Xn&&(a=F()):(zn._root=r,zn._startText=D(),Xn=!0)),o=jn.getPooled(o,t,n,r),a?o.data=a:(a=A(n),null!==a&&(o.data=a)),N(o),a=o):a=null,(e=Qn?H(e,n):j(e,n))?(t=Vn.getPooled(Gn.beforeInput,t,n,r),t.data=e,N(t)):t=null,null===a?t:null===t?a:[a,t]}},Zn=null,er=null,tr=null,nr={injectFiberControlledHostComponent:function(e){Zn=e}},rr=Object.freeze({injection:nr,enqueueStateRestore:B,needsStateRestore:W,restoreStateIfNeeded:K}),or=!1,ar={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0},ir=sn.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,lr="function"==typeof Symbol&&Symbol.for,ur=lr?Symbol.for("react.element"):60103,cr=lr?Symbol.for("react.call"):60104,sr=lr?Symbol.for("react.return"):60105,fr=lr?Symbol.for("react.portal"):60106,dr=lr?Symbol.for("react.fragment"):60107,pr=lr?Symbol.for("react.strict_mode"):60108,hr=lr?Symbol.for("react.provider"):60109,mr=lr?Symbol.for("react.context"):60110,vr=lr?Symbol.for("react.async_mode"):60111,yr=lr?Symbol.for("react.forward_ref"):60112,gr="function"==typeof Symbol&&Symbol.iterator,br=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Cr={},kr={},xr={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){xr[e]=new ce(e,0,!1,e,null)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];xr[t]=new ce(t,1,!1,e[1],null)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){xr[e]=new ce(e,2,!1,e.toLowerCase(),null)}),["autoReverse","externalResourcesRequired","preserveAlpha"].forEach(function(e){xr[e]=new ce(e,2,!1,e,null)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){xr[e]=new ce(e,3,!1,e.toLowerCase(),null)}),["checked","multiple","muted","selected"].forEach(function(e){xr[e]=new ce(e,3,!0,e.toLowerCase(),null)}),["capture","download"].forEach(function(e){xr[e]=new ce(e,4,!1,e.toLowerCase(),null)}),["cols","rows","size","span"].forEach(function(e){xr[e]=new ce(e,6,!1,e.toLowerCase(),null)}),["rowSpan","start"].forEach(function(e){xr[e]=new ce(e,5,!1,e.toLowerCase(),null)});var wr=/[\-\:]([a-z])/g;"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(wr,se);xr[t]=new ce(t,1,!1,e,null)}),"xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(wr,se);xr[t]=new ce(t,1,!1,e,"http://www.w3.org/1999/xlink")}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(wr,se);xr[t]=new ce(t,1,!1,e,"http://www.w3.org/XML/1998/namespace")}),xr.tabIndex=new ce("tabIndex",1,!1,"tabindex",null);var Tr={change:{phasedRegistrationNames:{bubbled:"onChange",captured:"onChangeCapture"},dependencies:"topBlur topChange topClick topFocus topInput topKeyDown topKeyUp topSelectionChange".split(" ")}},Sr=null,Er=null,_r=!1;fn.canUseDOM&&(_r=J("input")&&(!document.documentMode||9=document.documentMode,io={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"},dependencies:"topBlur topContextMenu topFocus topKeyDown topKeyUp topMouseDown topMouseUp topSelectionChange".split(" ")}},lo=null,uo=null,co=null,so=!1,fo={eventTypes:io,extractEvents:function(e,t,n,r){var o,a=r.window===r?r.document:9===r.nodeType?r:r.ownerDocument;if(!(o=!a)){e:{a=$e(a),o=Tn.onSelect;for(var i=0;i=Fo-e){if(!(-1!==Io&&Io<=e))return void(Oo||(Oo=!0,requestAnimationFrame(Lo)));Mo.didTimeout=!0}else Mo.didTimeout=!1;Io=-1,e=Po,Po=null,null!==e&&e(Mo)}},!1);var Lo=function(e){Oo=!1;var t=e-Fo+Ro;tt&&(t=8),Ro=t"+t+"",t=Ao.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}}),jo={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Vo=["Webkit","ms","Moz","O"];Object.keys(jo).forEach(function(e){Vo.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),jo[t]=jo[e]})});var Bo=dn({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0}),Wo=zo.html,Ko=pn.thatReturns(""),Qo=Object.freeze({createElement:$t,createTextNode:qt,setInitialProperties:Gt,diffProperties:Yt,updateProperties:Xt,diffHydratedProperties:Jt,diffHydratedText:Zt,warnForUnmatchedText:function(){},warnForDeletedHydratableElement:function(){},warnForDeletedHydratableText:function(){},warnForInsertedHydratedElement:function(){},warnForInsertedHydratedText:function(){},restoreControlledState:function(e,t,n){switch(t){case"input":if(me(e,n),t=n.name,"radio"===n.type&&null!=t){for(n=e;n.parentNode;)n=n.parentNode;for(n=n.querySelectorAll("input[name="+JSON.stringify(""+t)+'][type="radio"]'),t=0;tr&&(o=r,r=e,e=o),o=Ge(n,e);var a=Ge(n,r);if(o&&a&&(1!==t.rangeCount||t.anchorNode!==o.node||t.anchorOffset!==o.offset||t.focusNode!==a.node||t.focusOffset!==a.offset)){var i=document.createRange();i.setStart(o.node,o.offset),t.removeAllRanges(),e>r?(t.addRange(i),t.extend(a.node,a.offset)):(i.setEnd(a.node,a.offset),t.addRange(i))}}for(t=[],e=n;e=e.parentNode;)1===e.nodeType&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(n.focus(),n=0;nL.length&&L.push(e)}function d(e,t,n,o){var a="undefined"==typeof e?"undefined":y(e);"undefined"!==a&&"boolean"!==a||(e=null);var i=!1;if(null===e)i=!0;else switch(a){case"string":case"number":i=!0;break;case"object":switch(e.$$typeof){case x:case w:i=!0}}if(i)return n(o,e,""===t?"."+p(e,0):t),1;if(i=0,t=""===t?".":t+":",Array.isArray(e))for(var l=0;l1&&void 0!==arguments[1]?arguments[1]:I.default;return function(n){var o,u;return u=o=function(o){function u(t){r(this,u);var n=i(this,(u.__proto__||Object.getPrototypeOf(u)).call(this,t));return n._isMounted=!1,n.options=a({container:"undefined"!=typeof window?window:void 0},e),n.state={firstItemIndex:0,lastItemIndex:-1},e&&e.initialState&&(n.state=a({},n.state,e.initialState)),n.refreshState=n.refreshState.bind(n),"undefined"!=typeof window&&"requestAnimationFrame"in window&&(n.refreshState=(0,_.default)(n.refreshState)),n}return s(u,o),f(u,[{key:"setStateIfNeeded",value:function(e,t,n,o,r){var i=(0,v.default)(e,t,n,o,r);void 0!==i&&(i.firstItemIndex>i.lastItemIndex||i.firstItemIndex===this.state.firstItemIndex&&i.lastItemIndex===this.state.lastItemIndex||this.setState(i))}},{key:"refreshState",value:function(){if(this._isMounted){var e=this.props,t=e.itemHeight,n=e.items,o=e.itemBuffer;this.setStateIfNeeded(this.domNode,this.options.container,n,t,o)}}},{key:"componentWillMount",value:function(){this._isMounted=!0}},{key:"componentDidMount",value:function(){this.domNode=p.default.findDOMNode(this),this.refreshState(),this.options.container.addEventListener("scroll",this.refreshState),this.options.container.addEventListener("resize",this.refreshState)}},{key:"componentWillUnmount",value:function(){this._isMounted=!1,this.options.container.removeEventListener("scroll",this.refreshState),this.options.container.removeEventListener("resize",this.refreshState)}},{key:"componentWillReceiveProps",value:function(e){var t=e.itemHeight,n=e.items,o=e.itemBuffer;this.setStateIfNeeded(this.domNode,this.options.container,n,t,o)}},{key:"render",value:function(){return l.default.createElement(n,a({},this.props,t(this.props,this.state)))}}]),u}(c.PureComponent),o.propTypes={items:h.default.array.isRequired,itemHeight:h.default.number.isRequired,itemBuffer:h.default.number},o.defaultProps={itemBuffer:0},u}};t.default=O},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e,t){var n=e.items,o=e.itemHeight,r=t.firstItemIndex,i=t.lastItemIndex,s=i>-1?n.slice(r,i+1):[],u=n.length*o,a=r*o;return{virtual:{items:s,style:{height:u,paddingTop:a,boxSizing:"border-box"}}}};t.default=n},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){return e.pageYOffset?e.pageYOffset:e.document?e.document.documentElement&&e.document.documentElement.scrollTop?e.document.documentElement.scrollTop:e.document.body&&e.document.body.scrollTop?e.document.body.scrollTop:0:e.scrollY||e.scrollTop||0};t.default=n},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(13),i=o(r),s=n(10),u=o(s),a=function(e,t,n,o,r){if(t&&o&&n&&0!==n.length){var s=t.innerHeight,a=t.clientHeight,f=s||a;if(f){var c=(0,u.default)(t),l=c+f,d=(0,i.default)(e)-(0,i.default)(t),p=o*n.length,m=Math.max(0,c-d),h=Math.max(0,Math.min(p,l-d)),y=Math.max(0,Math.floor(m/o)-r),v=Math.min(n.length,Math.ceil(h/o)+r)-1;return{firstItemIndex:y,lastItemIndex:v}}}};t.default=a},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=arguments;t.default=function(e){var t=!1;return function(){t||(t=!0,window.requestAnimationFrame(function(){e.apply(void 0,n),t=!1}))}}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function e(t){return"undefined"!=typeof t&&t?(t.offsetTop||0)+e(t.offsetParent):0};t.default=n},,,,function(e,t,n){"use strict";function o(e,t,n,o,i,s,u,a){if(r(t),!e){var f;if(void 0===t)f=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,o,i,s,u,a],l=0;f=new Error(t.replace(/%s/g,function(){return c[l++]})),f.name="Invariant Violation"}throw f.framesToPop=1,f}}var r=function(e){};e.exports=o},,,,function(e,t,n){"use strict";var o=n(3),r=n(17),i=n(22);e.exports=function(){function e(e,t,n,o,s,u){u!==i&&r(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types")}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t};return n.checkPropTypes=o,n.PropTypes=n,n}},function(e,t){"use strict";var n="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";e.exports=n}]); -------------------------------------------------------------------------------- /demo/src/ConfigurableExample.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent, PropTypes } from 'react'; 2 | import VirtualList from '../../src/VirtualList'; 3 | 4 | const makeItem = (i) => ({ 5 | id: i, 6 | title: `Media heading #${i+1}`, 7 | text: 'Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis.', 8 | }); 9 | 10 | const ConfigurableExample = (MyList) => { 11 | let MyVirtualList = VirtualList()(MyList); 12 | 13 | return class MyConfigurableList extends PureComponent { 14 | constructor() { 15 | super(); 16 | 17 | const defaultItemCount = 100000; 18 | 19 | const items = []; 20 | 21 | for (let i = 0; i < defaultItemCount; i++) { 22 | items[i] = makeItem(i); 23 | } 24 | 25 | const state = { 26 | itemHeight: 100, 27 | itemCount: defaultItemCount, 28 | items: items, 29 | contained: false, 30 | containerHeight: 250, 31 | itemBuffer: 0, 32 | }; 33 | 34 | this.state = state; 35 | }; 36 | 37 | update = () => { 38 | const items = []; 39 | const itemCount = parseInt(this.refs.itemCount.value, 10); 40 | 41 | for (var i = 0; i < itemCount; i++) { 42 | items[i] = makeItem(i); 43 | } 44 | 45 | const contained = this.refs.contained.checked; 46 | 47 | const state = { 48 | itemHeight: parseInt(this.refs.itemHeight.value, 10), 49 | itemCount: itemCount, 50 | items: items, 51 | contained: contained, 52 | container: this.refs.container, 53 | containerHeight: parseInt(this.refs.containerHeight.value, 10), 54 | itemBuffer: parseInt(this.refs.itemBuffer.value, 10), 55 | }; 56 | 57 | if (state.contained !== this.state.contained) { 58 | const options = { 59 | container: state.contained ? state.container : window, 60 | }; 61 | 62 | MyVirtualList = VirtualList(options)(MyList); 63 | } 64 | 65 | this.setState(state); 66 | }; 67 | 68 | render() { 69 | return ( 70 |
71 |
72 |
73 | 74 |
75 | 76 |
77 | 78 |
79 | 80 |
81 |
82 |
83 | 84 |
85 | 86 |
87 | 88 |
89 | 90 |
91 |
92 |
93 | 94 |
95 | 96 |
97 |
98 |
99 |
100 |
101 | 106 |
107 |
108 |
109 | ); 110 | }; 111 | }; 112 | }; 113 | 114 | export default ConfigurableExample; -------------------------------------------------------------------------------- /demo/src/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import ConfigurableExample from './ConfigurableExample'; 4 | 5 | require('file?name=[name].[ext]!./index.html'); 6 | 7 | const MyList = ({ 8 | virtual, 9 | itemHeight, 10 | }) => ( 11 |
    12 | {virtual.items.map((item) => ( 13 |
  • 14 |
    15 | 16 |
    17 |
    18 |

    {item.title}

    19 |

    {item.text}

    20 |
    21 |
  • 22 | ))} 23 |
24 | ); 25 | 26 | const MyConfigurableExample = ConfigurableExample(MyList); 27 | 28 | render( 29 | , 30 | document.getElementById('app') 31 | ); 32 | 33 | -------------------------------------------------------------------------------- /demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 |
18 |

React Virtual List

19 |
20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | Fork me on GitHub 30 | 31 | -------------------------------------------------------------------------------- /demo/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import path from 'path'; 3 | import packageJson from '../package.json'; 4 | 5 | const config = [{ 6 | context: __dirname, 7 | 8 | entry: { 9 | app: './src/app.js', 10 | react: ['react', 'react-dom'], 11 | virtualList: ['../lib/VirtualList.js'], 12 | }, 13 | 14 | output: { 15 | path: path.join(__dirname, './dist'), 16 | filename: '[name].js', 17 | }, 18 | 19 | module: { 20 | loaders: [ 21 | { 22 | test: /\.js$/, 23 | loader: 'babel', 24 | query: { 25 | presets: ['es2015', 'stage-0', 'react'] 26 | }, 27 | }, 28 | ], 29 | }, 30 | 31 | plugins: [ 32 | new webpack.optimize.CommonsChunkPlugin({ 33 | names: ['virtualList', 'react'], 34 | minChunks: 2, 35 | }), 36 | new webpack.optimize.OccurenceOrderPlugin(), 37 | new webpack.DefinePlugin({ 38 | PACKAGE_NAME: JSON.stringify(packageJson.name), 39 | PACKAGE_VERSION: JSON.stringify(packageJson.version), 40 | 'process.env.NODE_ENV': JSON.stringify('production') 41 | }), 42 | new webpack.optimize.UglifyJsPlugin({ 43 | compress: { 44 | warnings: false, 45 | }, 46 | }), 47 | ], 48 | }]; 49 | 50 | export default config; -------------------------------------------------------------------------------- /deploy-to-gh-pages.sh: -------------------------------------------------------------------------------- 1 | # See https://medium.com/@nthgergo/publishing-gh-pages-with-travis-ci-53a8270e87db 2 | set -o errexit 3 | 4 | # config 5 | git config --global user.email "developerdizzle+travis@gmail.com" 6 | git config --global user.name "Travis CI" 7 | 8 | # build (CHANGE THIS) 9 | rm -rf demo/dist 10 | mkdir -p demo/dist 11 | 12 | npm run build:demo 13 | 14 | # deploy 15 | cd demo/dist 16 | git init 17 | git add . 18 | git commit -m "[travis] Deploy to Github Pages" 19 | git push --force "https://${GITHUB_TOKEN}@github.com/developerdizzle/react-virtual-list.git" master:gh-pages -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /lib/VirtualList.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 8 | 9 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 10 | 11 | var _react = require('react'); 12 | 13 | var _react2 = _interopRequireDefault(_react); 14 | 15 | var _reactDom = require('react-dom'); 16 | 17 | var _reactDom2 = _interopRequireDefault(_reactDom); 18 | 19 | var _propTypes = require('prop-types'); 20 | 21 | var _propTypes2 = _interopRequireDefault(_propTypes); 22 | 23 | var _getVisibleItemBounds = require('./utils/getVisibleItemBounds'); 24 | 25 | var _getVisibleItemBounds2 = _interopRequireDefault(_getVisibleItemBounds); 26 | 27 | var _throttleWithRAF = require('./utils/throttleWithRAF'); 28 | 29 | var _throttleWithRAF2 = _interopRequireDefault(_throttleWithRAF); 30 | 31 | var _defaultMapVirtualToProps = require('./utils/defaultMapVirtualToProps'); 32 | 33 | var _defaultMapVirtualToProps2 = _interopRequireDefault(_defaultMapVirtualToProps); 34 | 35 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 36 | 37 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 38 | 39 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 40 | 41 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 42 | 43 | var VirtualList = function VirtualList(options) { 44 | var mapVirtualToProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _defaultMapVirtualToProps2.default; 45 | return function (InnerComponent) { 46 | var _class, _temp; 47 | 48 | return _temp = _class = function (_PureComponent) { 49 | _inherits(vlist, _PureComponent); 50 | 51 | function vlist(props) { 52 | _classCallCheck(this, vlist); 53 | 54 | var _this = _possibleConstructorReturn(this, (vlist.__proto__ || Object.getPrototypeOf(vlist)).call(this, props)); 55 | 56 | _this._isMounted = false; 57 | 58 | 59 | _this.options = _extends({ 60 | container: typeof window !== 'undefined' ? window : undefined 61 | }, options); 62 | 63 | _this.state = { 64 | firstItemIndex: 0, 65 | lastItemIndex: -1 66 | }; 67 | 68 | // initialState allows us to set the first/lastItemIndex (useful for server-rendering) 69 | if (options && options.initialState) { 70 | _this.state = _extends({}, _this.state, options.initialState); 71 | } 72 | 73 | _this.refreshState = _this.refreshState.bind(_this); 74 | 75 | // if requestAnimationFrame is available, use it to throttle refreshState 76 | if (typeof window !== 'undefined' && 'requestAnimationFrame' in window) { 77 | _this.refreshState = (0, _throttleWithRAF2.default)(_this.refreshState); 78 | } 79 | return _this; 80 | } 81 | 82 | _createClass(vlist, [{ 83 | key: 'setStateIfNeeded', 84 | value: function setStateIfNeeded(list, container, items, itemHeight, itemBuffer) { 85 | // get first and lastItemIndex 86 | var state = (0, _getVisibleItemBounds2.default)(list, container, items, itemHeight, itemBuffer); 87 | 88 | if (state === undefined) { 89 | return; 90 | } 91 | 92 | if (state.firstItemIndex > state.lastItemIndex) { 93 | return; 94 | } 95 | 96 | if (state.firstItemIndex !== this.state.firstItemIndex || state.lastItemIndex !== this.state.lastItemIndex) { 97 | this.setState(state); 98 | } 99 | } 100 | }, { 101 | key: 'refreshState', 102 | value: function refreshState() { 103 | if (!this._isMounted) { 104 | return; 105 | } 106 | 107 | var _props = this.props, 108 | itemHeight = _props.itemHeight, 109 | items = _props.items, 110 | itemBuffer = _props.itemBuffer; 111 | 112 | 113 | this.setStateIfNeeded(this.domNode, this.options.container, items, itemHeight, itemBuffer); 114 | } 115 | }, { 116 | key: 'componentWillMount', 117 | value: function componentWillMount() { 118 | this._isMounted = true; 119 | } 120 | }, { 121 | key: 'componentDidMount', 122 | value: function componentDidMount() { 123 | // cache the DOM node 124 | this.domNode = _reactDom2.default.findDOMNode(this); 125 | 126 | // we need to refreshState because we didn't have access to the DOM node before 127 | this.refreshState(); 128 | 129 | // add events 130 | this.options.container.addEventListener('scroll', this.refreshState); 131 | this.options.container.addEventListener('resize', this.refreshState); 132 | } 133 | }, { 134 | key: 'componentWillUnmount', 135 | value: function componentWillUnmount() { 136 | this._isMounted = false; 137 | 138 | // remove events 139 | this.options.container.removeEventListener('scroll', this.refreshState); 140 | this.options.container.removeEventListener('resize', this.refreshState); 141 | } 142 | }, { 143 | key: 'componentWillReceiveProps', 144 | 145 | 146 | // if props change, just assume we have to recalculate 147 | value: function componentWillReceiveProps(nextProps) { 148 | var itemHeight = nextProps.itemHeight, 149 | items = nextProps.items, 150 | itemBuffer = nextProps.itemBuffer; 151 | 152 | 153 | this.setStateIfNeeded(this.domNode, this.options.container, items, itemHeight, itemBuffer); 154 | } 155 | }, { 156 | key: 'render', 157 | value: function render() { 158 | return _react2.default.createElement(InnerComponent, _extends({}, this.props, mapVirtualToProps(this.props, this.state))); 159 | } 160 | }]); 161 | 162 | return vlist; 163 | }(_react.PureComponent), _class.propTypes = { 164 | items: _propTypes2.default.array.isRequired, 165 | itemHeight: _propTypes2.default.number.isRequired, 166 | itemBuffer: _propTypes2.default.number 167 | }, _class.defaultProps = { 168 | itemBuffer: 0 169 | }, _temp; 170 | }; 171 | }; 172 | 173 | exports.default = VirtualList; -------------------------------------------------------------------------------- /lib/utils/defaultMapVirtualToProps.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var defaultMapToVirtualProps = function defaultMapToVirtualProps(_ref, _ref2) { 7 | var items = _ref.items, 8 | itemHeight = _ref.itemHeight; 9 | var firstItemIndex = _ref2.firstItemIndex, 10 | lastItemIndex = _ref2.lastItemIndex; 11 | 12 | var visibleItems = lastItemIndex > -1 ? items.slice(firstItemIndex, lastItemIndex + 1) : []; 13 | 14 | // style 15 | var height = items.length * itemHeight; 16 | var paddingTop = firstItemIndex * itemHeight; 17 | 18 | return { 19 | virtual: { 20 | items: visibleItems, 21 | style: { 22 | height: height, 23 | paddingTop: paddingTop, 24 | boxSizing: 'border-box' 25 | } 26 | } 27 | }; 28 | }; 29 | 30 | exports.default = defaultMapToVirtualProps; -------------------------------------------------------------------------------- /lib/utils/getElementTop.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var getElementTop = function getElementTop(element) { 7 | if (element.pageYOffset) return element.pageYOffset; 8 | 9 | if (element.document) { 10 | if (element.document.documentElement && element.document.documentElement.scrollTop) return element.document.documentElement.scrollTop; 11 | if (element.document.body && element.document.body.scrollTop) return element.document.body.scrollTop; 12 | 13 | return 0; 14 | } 15 | 16 | return element.scrollY || element.scrollTop || 0; 17 | }; 18 | 19 | exports.default = getElementTop; -------------------------------------------------------------------------------- /lib/utils/getVisibleItemBounds.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _topFromWindow = require('./topFromWindow'); 8 | 9 | var _topFromWindow2 = _interopRequireDefault(_topFromWindow); 10 | 11 | var _getElementTop = require('./getElementTop'); 12 | 13 | var _getElementTop2 = _interopRequireDefault(_getElementTop); 14 | 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 16 | 17 | var getVisibleItemBounds = function getVisibleItemBounds(list, container, items, itemHeight, itemBuffer) { 18 | // early return if we can't calculate 19 | if (!container) return undefined; 20 | if (!itemHeight) return undefined; 21 | if (!items) return undefined; 22 | if (items.length === 0) return undefined; 23 | 24 | // what the user can see 25 | var innerHeight = container.innerHeight, 26 | clientHeight = container.clientHeight; 27 | 28 | 29 | var viewHeight = innerHeight || clientHeight; // how many pixels are visible 30 | 31 | if (!viewHeight) return undefined; 32 | 33 | var viewTop = (0, _getElementTop2.default)(container); // top y-coordinate of viewport inside container 34 | var viewBottom = viewTop + viewHeight; 35 | 36 | var listTop = (0, _topFromWindow2.default)(list) - (0, _topFromWindow2.default)(container); // top y-coordinate of container inside window 37 | var listHeight = itemHeight * items.length; 38 | 39 | // visible list inside view 40 | var listViewTop = Math.max(0, viewTop - listTop); // top y-coordinate of list that is visible inside view 41 | var listViewBottom = Math.max(0, Math.min(listHeight, viewBottom - listTop)); // bottom y-coordinate of list that is visible inside view 42 | 43 | // visible item indexes 44 | var firstItemIndex = Math.max(0, Math.floor(listViewTop / itemHeight) - itemBuffer); 45 | var lastItemIndex = Math.min(items.length, Math.ceil(listViewBottom / itemHeight) + itemBuffer) - 1; 46 | 47 | return { 48 | firstItemIndex: firstItemIndex, 49 | lastItemIndex: lastItemIndex 50 | }; 51 | }; 52 | 53 | exports.default = getVisibleItemBounds; -------------------------------------------------------------------------------- /lib/utils/throttleWithRAF.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var _arguments = arguments; 7 | 8 | exports.default = function (fn) { 9 | var running = false; 10 | 11 | return function () { 12 | if (running) return; 13 | 14 | running = true; 15 | 16 | window.requestAnimationFrame(function () { 17 | fn.apply(undefined, _arguments); 18 | 19 | running = false; 20 | }); 21 | }; 22 | }; -------------------------------------------------------------------------------- /lib/utils/topFromWindow.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var topFromWindow = function topFromWindow(element) { 7 | if (typeof element === 'undefined' || !element) return 0; 8 | 9 | return (element.offsetTop || 0) + topFromWindow(element.offsetParent); 10 | }; 11 | 12 | exports.default = topFromWindow; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-virtual-list", 3 | "version": "2.3.0", 4 | "description": "Super simple virtualized list React higher-order component", 5 | "main": "lib/VirtualList.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "build": "babel src --out-dir lib --copy-files --ignore tests", 11 | "build:demo": "webpack --config demo/webpack.config.babel.js", 12 | "stats": "webpack .\\demo\\src\\app.js --config .\\demo\\webpack.config.babel.js --profile --json > stats.json", 13 | "test": "jest --verbose" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/developerdizzle/react-virtual-list.git" 18 | }, 19 | "keywords": [ 20 | "react", 21 | "virtual", 22 | "list", 23 | "render", 24 | "scroll", 25 | "performance" 26 | ], 27 | "author": "developerdizzle", 28 | "bugs": { 29 | "url": "https://github.com/developerdizzle/react-virtual-list/issues" 30 | }, 31 | "homepage": "https://github.com/developerdizzle/react-virtual-list", 32 | "dependencies": { 33 | "prop-types": "^15.5.10" 34 | }, 35 | "devDependencies": { 36 | "babel-cli": "^6.18.0", 37 | "babel-core": "^6.18.2", 38 | "babel-jest": "^17.0.2", 39 | "babel-loader": "^6.2.8", 40 | "babel-preset-es2015": "^6.18.0", 41 | "babel-preset-react": "^6.16.0", 42 | "babel-preset-stage-0": "^6.16.0", 43 | "file-loader": "^0.9.0", 44 | "gulp": "^3.8.11", 45 | "gulp-react": "^3.0.1", 46 | "jasmine-node": "^1.14.5", 47 | "jest": "^17.0.3", 48 | "react": "^16.3.1", 49 | "react-dom": "^16.3.1", 50 | "react-test-renderer": "^16.3.1", 51 | "webpack": "^1.13.3" 52 | }, 53 | "peerDependencies": { 54 | "react": "^15.0.0 || ^16.0.0", 55 | "react-dom": "^15.0.0 || ^16.0.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/VirtualList.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import getVisibleItemBounds from './utils/getVisibleItemBounds'; 6 | import throttleWithRAF from './utils/throttleWithRAF'; 7 | import defaultMapToVirtualProps from './utils/defaultMapVirtualToProps'; 8 | 9 | const VirtualList = (options, mapVirtualToProps = defaultMapToVirtualProps) => (InnerComponent) => { 10 | return class vlist extends PureComponent { 11 | static propTypes = { 12 | items: PropTypes.array.isRequired, 13 | itemHeight: PropTypes.number.isRequired, 14 | itemBuffer: PropTypes.number, 15 | }; 16 | 17 | static defaultProps = { 18 | itemBuffer: 0, 19 | }; 20 | 21 | _isMounted = false; 22 | 23 | constructor(props) { 24 | super(props); 25 | 26 | this.options = { 27 | container: typeof window !== 'undefined' ? window : undefined, 28 | ...options, 29 | }; 30 | 31 | this.state = { 32 | firstItemIndex: 0, 33 | lastItemIndex: -1, 34 | }; 35 | 36 | // initialState allows us to set the first/lastItemIndex (useful for server-rendering) 37 | if (options && options.initialState) { 38 | this.state = { 39 | ...this.state, 40 | ...options.initialState, 41 | }; 42 | } 43 | 44 | this.refreshState = this.refreshState.bind(this); 45 | 46 | // if requestAnimationFrame is available, use it to throttle refreshState 47 | if (typeof window !== 'undefined' && 'requestAnimationFrame' in window) { 48 | this.refreshState = throttleWithRAF(this.refreshState); 49 | } 50 | }; 51 | 52 | setStateIfNeeded(list, container, items, itemHeight, itemBuffer) { 53 | // get first and lastItemIndex 54 | const state = getVisibleItemBounds(list, container, items, itemHeight, itemBuffer); 55 | 56 | if (state === undefined) { return; } 57 | 58 | if (state.firstItemIndex > state.lastItemIndex) { return; } 59 | 60 | if (state.firstItemIndex !== this.state.firstItemIndex || state.lastItemIndex !== this.state.lastItemIndex) { 61 | this.setState(state); 62 | } 63 | } 64 | 65 | refreshState() { 66 | if (!this._isMounted) { 67 | return; 68 | } 69 | 70 | const { itemHeight, items, itemBuffer } = this.props; 71 | 72 | this.setStateIfNeeded(this.domNode, this.options.container, items, itemHeight, itemBuffer); 73 | }; 74 | 75 | componentWillMount() { 76 | this._isMounted = true; 77 | } 78 | 79 | componentDidMount() { 80 | // cache the DOM node 81 | this.domNode = ReactDOM.findDOMNode(this); 82 | 83 | // we need to refreshState because we didn't have access to the DOM node before 84 | this.refreshState(); 85 | 86 | // add events 87 | this.options.container.addEventListener('scroll', this.refreshState); 88 | this.options.container.addEventListener('resize', this.refreshState); 89 | }; 90 | 91 | componentWillUnmount() { 92 | this._isMounted = false; 93 | 94 | // remove events 95 | this.options.container.removeEventListener('scroll', this.refreshState); 96 | this.options.container.removeEventListener('resize', this.refreshState); 97 | }; 98 | 99 | // if props change, just assume we have to recalculate 100 | componentWillReceiveProps(nextProps) { 101 | const { itemHeight, items, itemBuffer } = nextProps; 102 | 103 | this.setStateIfNeeded(this.domNode, this.options.container, items, itemHeight, itemBuffer); 104 | }; 105 | 106 | render() { 107 | return (); 108 | }; 109 | }; 110 | }; 111 | 112 | export default VirtualList; 113 | -------------------------------------------------------------------------------- /src/__tests__/VirtualList.js: -------------------------------------------------------------------------------- 1 | import ShallowRenderer from 'react-test-renderer/shallow' 2 | import React from 'react'; 3 | 4 | import VirtualList from '../VirtualList'; 5 | 6 | const MyList = ({ itemHeight, virtual }) => { 7 | return ( 8 |
    9 | {virtual.items.map(item => ( 10 |
  • {item}
  • 11 | ))} 12 |
13 | ); 14 | }; 15 | 16 | const items = Array.apply(null, {length: 1000}).map(Number.call, Number); 17 | 18 | describe('higher-order component that only renders visible items', () => { 19 | it('is a function', () => { 20 | expect(typeof VirtualList).toBe('function'); 21 | }); 22 | 23 | it('renders the inner component', () => { 24 | const MyVirtualList = VirtualList()(MyList); 25 | 26 | const renderer = ShallowRenderer.createRenderer(); 27 | renderer.render( 28 | ( 29 | 33 | ) 34 | ); 35 | const result = renderer.getRenderOutput(); 36 | 37 | expect(result.type).toBe(MyList); 38 | }); 39 | 40 | it('provides the virtual prop', () => { 41 | const MyVirtualList = VirtualList()(MyList); 42 | 43 | const renderer = ShallowRenderer.createRenderer(); 44 | renderer.render( 45 | ( 46 | 50 | ) 51 | ); 52 | const result = renderer.getRenderOutput(); 53 | 54 | expect(result.props.virtual).not.toBe(undefined); 55 | }); 56 | 57 | it('provides the items prop', () => { 58 | const MyVirtualList = VirtualList()(MyList); 59 | 60 | const renderer = ShallowRenderer.createRenderer(); 61 | renderer.render( 62 | ( 63 | 67 | ) 68 | ); 69 | const result = renderer.getRenderOutput(); 70 | 71 | expect(result.props.virtual.items).not.toBe(undefined); 72 | }); 73 | 74 | it('provides the style prop', () => { 75 | const MyVirtualList = VirtualList()(MyList); 76 | 77 | const renderer = ShallowRenderer.createRenderer(); 78 | renderer.render( 79 | ( 80 | 84 | ) 85 | ); 86 | const result = renderer.getRenderOutput(); 87 | 88 | expect(result.props.virtual.style).not.toBe(undefined); 89 | }); 90 | 91 | // it('renders only visible items', () => { 92 | // const container = { 93 | // clientHeight: 500, 94 | // offsetTop: 0, 95 | // }; 96 | 97 | // const options = { 98 | // container, 99 | // }; 100 | 101 | // const MyVirtualList = VirtualList(options)(MyList); 102 | 103 | // const renderer = ShallowRenderer.createRenderer(); 104 | // renderer.render( 105 | // ( 106 | // 110 | // ) 111 | // ); 112 | 113 | // const result = renderer.getRenderOutput(); 114 | 115 | // expect(result.props.virtual.items).toHaveLength(5); 116 | // }); 117 | 118 | it('uses initialState options', () => { 119 | const container = { 120 | clientHeight: 500, 121 | offsetTop: 0, 122 | }; 123 | 124 | const options = { 125 | container, 126 | initialState: { 127 | firstItemIndex: 0, 128 | lastItemIndex: 4, 129 | style: { 130 | height: 500, 131 | paddingTop: 0, 132 | }, 133 | }, 134 | }; 135 | 136 | const MyVirtualList = VirtualList(options)(MyList); 137 | 138 | const renderer = ShallowRenderer.createRenderer(); 139 | renderer.render( 140 | ( 141 | 145 | ) 146 | ); 147 | 148 | const result = renderer.getRenderOutput(); 149 | 150 | expect(result.props.virtual.items).toHaveLength(5); 151 | }); 152 | 153 | it('has default mapVirtualToProps', () => { 154 | const container = { 155 | clientHeight: 500, 156 | offsetTop: 0, 157 | }; 158 | 159 | const options = { 160 | container, 161 | initialState: { 162 | firstItemIndex: 0, 163 | lastItemIndex: 4, 164 | }, 165 | }; 166 | 167 | const MyVirtualList = VirtualList(options)(MyList); 168 | 169 | const renderer = ShallowRenderer.createRenderer(); 170 | renderer.render( 171 | ( 172 | 176 | ) 177 | ); 178 | 179 | const result = renderer.getRenderOutput(); 180 | 181 | expect(result.props.virtual).toBeDefined(); 182 | }); 183 | 184 | it('allows custom mapVirtualToProps', () => { 185 | const container = { 186 | clientHeight: 500, 187 | offsetTop: 0, 188 | }; 189 | 190 | const options = { 191 | container, 192 | initialState: { 193 | firstItemIndex: 0, 194 | lastItemIndex: 4, 195 | }, 196 | }; 197 | 198 | const mapVirtualToProps = ({ items }) => ({ customItemsRef: items }) 199 | 200 | const MyVirtualList = VirtualList(options, mapVirtualToProps)(MyList); 201 | 202 | const renderer = ShallowRenderer.createRenderer(); 203 | renderer.render( 204 | ( 205 | 209 | ) 210 | ); 211 | 212 | const result = renderer.getRenderOutput(); 213 | 214 | expect(result.props.virtual).toBeUndefined(); 215 | expect(result.props.customItemsRef).toBeDefined(); 216 | }); 217 | }); 218 | -------------------------------------------------------------------------------- /src/utils/__tests__/defaultMapVirtualToProps.js: -------------------------------------------------------------------------------- 1 | import defaultMapVirtualToProps from '../defaultMapVirtualToProps'; 2 | 3 | const defaultProps = { 4 | items: [1, 2, 3, 4, 5], 5 | itemHeight: 100, 6 | }; 7 | 8 | const defaultState = { 9 | firstItemIndex: 0, 10 | lastItemIndex: 4, 11 | }; 12 | 13 | describe('function to convert state and props into virtual props', () => { 14 | it('is a function', () => { 15 | expect(typeof defaultMapVirtualToProps).toBe('function'); 16 | }); 17 | 18 | it('returns object with items prop', () => { 19 | const props = defaultMapVirtualToProps(defaultProps, defaultState); 20 | 21 | expect(props.virtual).toBeDefined(); 22 | expect(props.virtual.items).toBeDefined(); 23 | }); 24 | 25 | it('returns object with style prop', () => { 26 | const props = defaultMapVirtualToProps(defaultProps, defaultState); 27 | 28 | expect(props.virtual).toBeDefined(); 29 | expect(props.virtual.style).toBeDefined(); 30 | expect(props.virtual.style.height).toBeDefined(); 31 | expect(props.virtual.style.paddingTop).toBeDefined(); 32 | }); 33 | 34 | it('calculates items properly', () => { 35 | const props = defaultMapVirtualToProps(defaultProps, defaultState); 36 | 37 | expect(props.virtual.items).toHaveLength(5); 38 | }); 39 | 40 | it('calculates style properly', () => { 41 | const props = defaultMapVirtualToProps(defaultProps, defaultState); 42 | 43 | expect(props.virtual.style.height).toBe(500); 44 | expect(props.virtual.style.paddingTop).toBe(0); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/utils/__tests__/getElementTop.js: -------------------------------------------------------------------------------- 1 | import getElementTop from '../getElementTop'; 2 | 3 | describe('function to get top position of element', () => { 4 | it('is a function', () => { 5 | expect(typeof getElementTop).toBe('function'); 6 | }); 7 | 8 | it('returns pageYOffset for window', () => { 9 | const element = { 10 | pageYOffset: 10, 11 | document: { 12 | documentElement: { }, 13 | body: { }, 14 | }, 15 | }; 16 | 17 | const top = getElementTop(element); 18 | 19 | expect(top).toBe(10); 20 | expect(top).toBe(element.pageYOffset); 21 | expect(top).not.toBe(element.document.documentElement.scrollTop); 22 | expect(top).not.toBe(element.document.body.scrollTop); 23 | }); 24 | 25 | it('falls back to documentElement.scrollTop for window', () => { 26 | const element = { 27 | document: { 28 | documentElement: { 29 | scrollTop: 10, 30 | }, 31 | body: { }, 32 | }, 33 | }; 34 | 35 | const top = getElementTop(element); 36 | 37 | expect(top).toBe(10); 38 | expect(top).not.toBe(element.pageYOffset); 39 | expect(top).toBe(element.document.documentElement.scrollTop); 40 | expect(top).not.toBe(element.document.body.scrollTop); 41 | }); 42 | 43 | it('falls back to body.scrollTop for window', () => { 44 | const element = { 45 | document: { 46 | documentElement: { }, 47 | body: { 48 | scrollTop: 10, 49 | }, 50 | }, 51 | }; 52 | 53 | const top = getElementTop(element); 54 | 55 | expect(top).toBe(10); 56 | expect(top).not.toBe(element.pageYOffset); 57 | expect(top).not.toBe(element.document.documentElement.scrollTop); 58 | expect(top).toBe(element.document.body.scrollTop); 59 | }); 60 | 61 | it('falls back to 0 for window', () => { 62 | const element = { 63 | document: { 64 | documentElement: { }, 65 | body: { }, 66 | }, 67 | }; 68 | 69 | const top = getElementTop(element); 70 | 71 | expect(top).toBe(0); 72 | expect(top).not.toBe(element.pageYOffset); 73 | expect(top).not.toBe(element.document.documentElement.scrollTop); 74 | expect(top).not.toBe(element.document.body.scrollTop); 75 | }); 76 | 77 | 78 | it('returns scrollY for element', () => { 79 | const element = { 80 | scrollY: 10, 81 | }; 82 | 83 | const top = getElementTop(element); 84 | 85 | expect(top).toBe(10); 86 | expect(top).toBe(element.scrollY); 87 | expect(top).not.toBe(element.scrollTop); 88 | }); 89 | 90 | it('falls back to scrollTop for element', () => { 91 | const element = { 92 | scrollTop: 10, 93 | }; 94 | 95 | const top = getElementTop(element); 96 | 97 | expect(top).toBe(10); 98 | expect(top).not.toBe(element.scrollY); 99 | expect(top).toBe(element.scrollTop); 100 | }); 101 | 102 | it('falls back to 0 for element', () => { 103 | const element = { }; 104 | 105 | const top = getElementTop(element); 106 | 107 | expect(top).toBe(0); 108 | expect(top).not.toBe(element.scrollY); 109 | expect(top).not.toBe(element.scrollTop); 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /src/utils/__tests__/getVisibleItemBounds.js: -------------------------------------------------------------------------------- 1 | import getVisibleItemBounds from '../getVisibleItemBounds'; 2 | 3 | const items = Array.apply(null, {length: 1000}).map(Number.call, Number); 4 | const itemHeight = 100; 5 | const itemBuffer = 0; 6 | 7 | describe('function to get the visible item bounds within a container', () => { 8 | it('is a function', () => { 9 | expect(typeof getVisibleItemBounds).toBe('function'); 10 | }); 11 | 12 | it ('returns the correct first/lastItemIndex when the list is at the top of the container', () => { 13 | const container = { 14 | clientHeight: 100, 15 | offsetTop: 0, 16 | }; 17 | 18 | const list = { 19 | offsetTop: 0, 20 | }; 21 | 22 | const { firstItemIndex, lastItemIndex } = getVisibleItemBounds(list, container, items, itemHeight, itemBuffer); 23 | 24 | expect(firstItemIndex).toBe(0); 25 | expect(lastItemIndex).toBe(0); 26 | }); 27 | 28 | it ('returns the correct first/lastItemIndex when the list is at the top of the container', () => { 29 | const container = { 30 | clientHeight: 500, 31 | offsetTop: 0, 32 | }; 33 | 34 | const list = { 35 | offsetTop: 0, 36 | }; 37 | 38 | const { firstItemIndex, lastItemIndex } = getVisibleItemBounds(list, container, items, itemHeight, itemBuffer); 39 | 40 | expect(firstItemIndex).toBe(0); 41 | expect(lastItemIndex).toBe(4); 42 | }); 43 | 44 | it ('returns the correct first/lastItemIndex when the list is in the middle of the container', () => { 45 | const container = { 46 | clientHeight: 500, 47 | offsetTop: 0, 48 | }; 49 | 50 | const list = { 51 | offsetTop: 250, 52 | }; 53 | 54 | const { firstItemIndex, lastItemIndex } = getVisibleItemBounds(list, container, items, itemHeight, itemBuffer); 55 | 56 | expect(firstItemIndex).toBe(0); 57 | expect(lastItemIndex).toBe(2); 58 | }); 59 | 60 | it ('returns 0 and -1 when the list is below the container', () => { 61 | const container = { 62 | clientHeight: 500, 63 | offsetTop: 0, 64 | }; 65 | 66 | const list = { 67 | offsetTop: 500, 68 | }; 69 | 70 | const { firstItemIndex, lastItemIndex } = getVisibleItemBounds(list, container, items, itemHeight, itemBuffer); 71 | 72 | expect(firstItemIndex).toBe(0); 73 | expect(lastItemIndex).toBe(-1); 74 | }); 75 | 76 | 77 | it ('returns the correct first/lastItemIndex when the list is scrolled down', () => { 78 | const container = { 79 | clientHeight: 500, 80 | offsetTop: 500, 81 | }; 82 | 83 | const list = { 84 | offsetTop: 0, 85 | }; 86 | 87 | const { firstItemIndex, lastItemIndex } = getVisibleItemBounds(list, container, items, itemHeight, itemBuffer); 88 | 89 | expect(firstItemIndex).toBe(5); 90 | expect(lastItemIndex).toBe(9); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /src/utils/__tests__/topFromWindow.js: -------------------------------------------------------------------------------- 1 | import topFromWindow from '../topFromWindow'; 2 | 3 | describe('function to get distance between the top of an element and the window', () => { 4 | it('is a function', () => { 5 | expect(typeof topFromWindow).toBe('function'); 6 | }); 7 | 8 | it('returns 0 for window', () => { 9 | const top = topFromWindow(window); 10 | 11 | expect(top).toBe(0); 12 | }); 13 | 14 | it('returns 0 for non-object', () => { 15 | const top = topFromWindow(undefined); 16 | 17 | expect(top).toBe(0); 18 | }); 19 | 20 | it('returns offsetTop for top-level element', () => { 21 | const element = { 22 | offsetTop: 10, 23 | }; 24 | 25 | const top = topFromWindow(element); 26 | 27 | expect(top).toBe(element.offsetTop); 28 | }); 29 | 30 | it('returns cumulative offsetTop for nested element', () => { 31 | const element = { 32 | offsetTop: 10, 33 | offsetParent: { 34 | offsetTop: 20, 35 | }, 36 | }; 37 | 38 | const top = topFromWindow(element); 39 | 40 | const expectedTop = element.offsetTop + element.offsetParent.offsetTop 41 | 42 | expect(top).toBe(expectedTop); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/utils/defaultMapVirtualToProps.js: -------------------------------------------------------------------------------- 1 | const defaultMapToVirtualProps = ({ 2 | items, 3 | itemHeight, 4 | }, { 5 | firstItemIndex, 6 | lastItemIndex, 7 | }) => { 8 | const visibleItems = lastItemIndex > -1 ? items.slice(firstItemIndex, lastItemIndex + 1) : []; 9 | 10 | // style 11 | const height = items.length * itemHeight; 12 | const paddingTop = firstItemIndex * itemHeight; 13 | 14 | return { 15 | virtual: { 16 | items: visibleItems, 17 | style: { 18 | height, 19 | paddingTop, 20 | boxSizing: 'border-box', 21 | }, 22 | } 23 | }; 24 | } 25 | 26 | export default defaultMapToVirtualProps; -------------------------------------------------------------------------------- /src/utils/getElementTop.js: -------------------------------------------------------------------------------- 1 | const getElementTop = (element) => { 2 | if (element.pageYOffset) return element.pageYOffset; 3 | 4 | if (element.document) { 5 | if (element.document.documentElement && element.document.documentElement.scrollTop) return element.document.documentElement.scrollTop; 6 | if (element.document.body && element.document.body.scrollTop) return element.document.body.scrollTop; 7 | 8 | return 0; 9 | } 10 | 11 | return element.scrollY || element.scrollTop || 0; 12 | }; 13 | 14 | export default getElementTop; 15 | -------------------------------------------------------------------------------- /src/utils/getVisibleItemBounds.js: -------------------------------------------------------------------------------- 1 | import topFromWindow from './topFromWindow'; 2 | import getElementTop from './getElementTop'; 3 | 4 | const getVisibleItemBounds = (list, container, items, itemHeight, itemBuffer) => { 5 | // early return if we can't calculate 6 | if (!container) return undefined; 7 | if (!itemHeight) return undefined; 8 | if (!items) return undefined; 9 | if (items.length === 0) return undefined; 10 | 11 | // what the user can see 12 | const { innerHeight, clientHeight } = container; 13 | 14 | const viewHeight = innerHeight || clientHeight; // how many pixels are visible 15 | 16 | if (!viewHeight) return undefined; 17 | 18 | const viewTop = getElementTop(container); // top y-coordinate of viewport inside container 19 | const viewBottom = viewTop + viewHeight; 20 | 21 | const listTop = topFromWindow(list) - topFromWindow(container); // top y-coordinate of container inside window 22 | const listHeight = itemHeight * items.length; 23 | 24 | // visible list inside view 25 | const listViewTop = Math.max(0, viewTop - listTop); // top y-coordinate of list that is visible inside view 26 | const listViewBottom = Math.max(0, Math.min(listHeight, viewBottom - listTop)); // bottom y-coordinate of list that is visible inside view 27 | 28 | // visible item indexes 29 | const firstItemIndex = Math.max(0, Math.floor(listViewTop / itemHeight) - itemBuffer); 30 | const lastItemIndex = Math.min(items.length, Math.ceil(listViewBottom / itemHeight) + itemBuffer) - 1; 31 | 32 | return { 33 | firstItemIndex, 34 | lastItemIndex, 35 | }; 36 | }; 37 | 38 | export default getVisibleItemBounds; 39 | -------------------------------------------------------------------------------- /src/utils/throttleWithRAF.js: -------------------------------------------------------------------------------- 1 | export default (fn) => { 2 | let running = false; 3 | 4 | return () => { 5 | if (running) return; 6 | 7 | running = true; 8 | 9 | window.requestAnimationFrame(() => { 10 | fn.apply(this, arguments); 11 | 12 | running = false; 13 | }); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /src/utils/topFromWindow.js: -------------------------------------------------------------------------------- 1 | const topFromWindow = (element) => { 2 | if (typeof element === 'undefined' || !element) return 0; 3 | 4 | return (element.offsetTop || 0) + topFromWindow(element.offsetParent); 5 | }; 6 | 7 | export default topFromWindow; --------------------------------------------------------------------------------